From 6f015b1cb8fe8dd24f13ac88928400358e02a434 Mon Sep 17 00:00:00 2001 From: Tats Date: Sat, 18 Oct 2014 10:05:53 +0000 Subject: [PATCH 01/22] Moved gst_init() in main application and added gst_deinit(). --- MainApplication.cpp | 5 ++++- MainApplication.h | 1 + MediaImpl.cpp | 2 -- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/MainApplication.cpp b/MainApplication.cpp index 3e97e79..4752861 100644 --- a/MainApplication.cpp +++ b/MainApplication.cpp @@ -23,11 +23,14 @@ MainApplication::MainApplication(int &argc, char *argv[]) : QApplication(argc, argv) { - + // Initialize GStreamer. + gst_init (NULL, NULL); } MainApplication::~MainApplication() { + // Deinitialize GStreamer. + gst_deinit(); } bool MainApplication::notify(QObject *receiver, QEvent *event) diff --git a/MainApplication.h b/MainApplication.h index 5464bc8..0e26eb0 100644 --- a/MainApplication.h +++ b/MainApplication.h @@ -21,6 +21,7 @@ #ifndef MAINAPPLICATION_H_ #define MAINAPPLICATION_H_ +#include #include #include diff --git a/MediaImpl.cpp b/MediaImpl.cpp index 57a3fca..2d12767 100644 --- a/MediaImpl.cpp +++ b/MediaImpl.cpp @@ -260,8 +260,6 @@ bool MediaImpl::loadMovie(QString filename) // Free previously allocated structures unloadMovie(); - // Initialize GStreamer. - gst_init (NULL, NULL); GstElement *capsFilter = NULL; GstElement *videoScale = NULL; From c9b0888231217ccafb9a1be9467f424a78bbaa39 Mon Sep 17 00:00:00 2001 From: Tats Date: Sat, 18 Oct 2014 13:11:21 +0000 Subject: [PATCH 02/22] Reenabled seeking when clip is over (loop) without having to reload the movie. --- MainWindow.cpp | 6 ++++++ MediaImpl.cpp | 7 ++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/MainWindow.cpp b/MainWindow.cpp index 48a8fe1..c834940 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -114,6 +114,7 @@ void MainWindow::handlePaintItemSelectionChanged() void MainWindow::handleMappingItemSelectionChanged() { + qDebug() << "CURRENT: " << mappingList->selectedItems().count() << " " << mappingList->currentItem() <currentItem(); currentSelectedItem = item; @@ -136,6 +137,11 @@ void MainWindow::handleMappingItemSelectionChanged() updateCanvases(); } +void MainWindow::handleMappingCurrentItemChanged(QListWidgetItem* current,QListWidgetItem* previous) { + qDebug() << "CURRENT: " << current << " PREVIOUS: " << previous << " " << mappingList->selectedItems().count() << endl; +} + + void MainWindow::handleMappingItemChanged(QListWidgetItem* item) { // Toggle visibility of mapping depending on checkbox of item. diff --git a/MediaImpl.cpp b/MediaImpl.cpp index 2d12767..631e1d3 100644 --- a/MediaImpl.cpp +++ b/MediaImpl.cpp @@ -210,9 +210,8 @@ void MediaImpl::freeResources() void MediaImpl::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) + // XXX: There used to be an issue that when we reached EOS (_eos() == true) we could not seek anymore. + if (_seekEnabled) { qDebug() << "Seeking at position 0."; gst_element_seek_simple (_pipeline, GST_FORMAT_TIME, @@ -487,7 +486,9 @@ void MediaImpl::_postRun() break; case GST_MESSAGE_EOS: + // Automatically loop back. g_print("End-Of-Stream reached.\n"); + resetMovie(); // _terminate = true; // _finish(); break; From 436dc9d5a0d3317dba21885fde208029d4aa4db7 Mon Sep 17 00:00:00 2001 From: Alexandre Quessy Date: Sat, 18 Oct 2014 12:17:38 -0400 Subject: [PATCH 03/22] Refactor the pipeline + fix merge shmsrc feature --- AsyncQueue.h | 7 ++ MediaImpl.cpp | 252 ++++++++++++++++++++++++++++++++++---------------- MediaImpl.h | 22 ++--- 3 files changed, 192 insertions(+), 89 deletions(-) diff --git a/AsyncQueue.h b/AsyncQueue.h index 7c6e1af..56167c7 100644 --- a/AsyncQueue.h +++ b/AsyncQueue.h @@ -30,6 +30,9 @@ * used in classical producers/consumers multithreaded scenario. The * template parameter is the class which can be put/get to/from the * queue. + * + * We could probably use GAsyncQueue instead. + * See also https://github.com/aalex/toonloop/blob/master/src/concurrentqueue.h */ template class AsyncQueue @@ -43,6 +46,10 @@ public: return this->buffer.size(); } + /** + * Appends an element to the queue. + * Does not manage ref/unref copy/free memory. + */ void put(const T& item) { QMutexLocker locker(&mutex); diff --git a/MediaImpl.cpp b/MediaImpl.cpp index 57a3fca..1c3cdc4 100644 --- a/MediaImpl.cpp +++ b/MediaImpl.cpp @@ -109,9 +109,13 @@ bool MediaImpl::_videoPull() // For debugging: //gst_util_dump_mem(map.data, map.size) _data = map.data; + // release memory previously mapped with gst_buffer_map gst_buffer_unmap(buffer, &map); - if(this->_frame != NULL) + if (this->_frame != NULL) + { + // queue this frame to async queue _queueOutputBuffer.put(this->_frame); + } _frame = sample; } @@ -123,9 +127,9 @@ bool MediaImpl::_eos() const { if (_movieReady) { - Q_ASSERT( _videoSink ); + Q_ASSERT( _appsink0 ); gboolean videoEos; - g_object_get (G_OBJECT (_videoSink), "eos", &videoEos, NULL); + g_object_get (G_OBJECT (_appsink0), "eos", &videoEos, NULL); return (bool) (videoEos); } else @@ -135,11 +139,17 @@ bool MediaImpl::_eos() const GstFlowReturn MediaImpl::gstNewSampleCallback(GstElement*, MediaImpl *p) { GstSample *sample; - sample = gst_app_sink_pull_sample(GST_APP_SINK(p->_videoSink)); - //g_signal_emit_by_name (p->_videoSink, "pull-sample", &sample); + sample = gst_app_sink_pull_sample(GST_APP_SINK(p->_appsink0)); + //g_signal_emit_by_name (p->_appsink0, "pull-sample", &sample); + + // save last frame p->getQueueInputBuffer()->put(sample); - if (p->getQueueOutputBuffer()->size() > 1) { + + // we only keep the last one: + if (p->getQueueOutputBuffer()->size() > 1) + { sample = p->getQueueOutputBuffer()->get(); + // We free the memory for the previous frame here gst_sample_unref(sample); } return GST_FLOW_OK; @@ -149,24 +159,25 @@ MediaImpl::MediaImpl(const QString uri, bool live) : _currentMovie(""), _bus(NULL), _pipeline(NULL), -_source(NULL), -_videoQueue(NULL), -_videoConvert(NULL), -_videoColorSpace(NULL), -_audioSink(NULL), -_videoSink(NULL), +_uridecodebin0(NULL), +_queue0(NULL), +_videoconvert0(NULL), +//_audioSink(NULL), +_appsink0(NULL), _frame(NULL), -_width(640), -_height(480), +_width(640), // unused +_height(480), // unused _data(NULL), _seekEnabled(false), -_live(live), +_isSharedMemorySource(live), _attached(false), _movieReady(false), _uri(uri) { if (uri != "") + { loadMovie(uri); + } } void MediaImpl::unloadMovie() @@ -198,12 +209,11 @@ void MediaImpl::freeResources() _pipeline = NULL; } - _source = NULL; - _videoQueue = NULL; - _videoConvert = NULL; - _videoColorSpace = NULL; - _audioSink = NULL; - _videoSink = NULL; + _uridecodebin0 = NULL; + _queue0 = NULL; + _videoconvert0 = NULL; + //_audioSink = NULL; + _appsink0 = NULL; _frame = NULL; _padHandlerData = GstPadHandlerData(); } @@ -233,8 +243,11 @@ gboolean gstPollShmsrc (void *user_data) { MediaImpl *p = (MediaImpl*) user_data; - if (g_file_test(p->getUri().toUtf8().constData() , G_FILE_TEST_EXISTS) && !p->getAttached()) { - if (!p->setPlayState(true)) { + if (g_file_test(p->getUri().toUtf8().constData(), G_FILE_TEST_EXISTS) && + ! p->getAttached()) + { + if (! p->setPlayState(true)) + { qDebug() << "tried to attach, but starting pipeline failed!" << endl; return false; } @@ -262,49 +275,103 @@ bool MediaImpl::loadMovie(QString filename) // Initialize GStreamer. gst_init (NULL, NULL); - GstElement *capsFilter = NULL; - GstElement *videoScale = NULL; + GstElement *capsfilter0 = NULL; + GstElement *videoscale0 = NULL; // Create the elements. - _source = gst_element_factory_make ("uridecodebin", "source"); - _videoQueue = gst_element_factory_make ("queue", "vqueue"); - _videoColorSpace = gst_element_factory_make ("videoconvert", "vcolorspace"); - videoScale = gst_element_factory_make ("videoscale", "videoscale0"); - capsFilter = gst_element_factory_make ("capsfilter", "capsfilter0"); - _videoSink = gst_element_factory_make ("appsink", "vsink"); + if (_isSharedMemorySource) + { + _shmsrc0 = gst_element_factory_make ("shmsrc", "shmsrc0"); + _gdpdepay0 = gst_element_factory_make ("gdpdepay", "gdpdepay0"); + _pollSource = g_timeout_source_new (500); + g_source_set_callback (_pollSource, + gstPollShmsrc, + this, + NULL); + g_source_attach (_pollSource, g_main_context_default()); + g_source_unref (_pollSource); + } + else { + _uridecodebin0 = gst_element_factory_make ("uridecodebin", "uridecodebin0"); + } + _queue0 = gst_element_factory_make ("queue", "queue0"); + _videoconvert0 = gst_element_factory_make ("videoconvert", "videoconvert0"); + videoscale0 = gst_element_factory_make ("videoscale", "videoscale0"); + capsfilter0 = gst_element_factory_make ("capsfilter", "capsfilter0"); + _appsink0 = gst_element_factory_make ("appsink", "appsink0"); // Prepare handler data. - _padHandlerData.videoToConnect = _videoQueue; - _padHandlerData.videoSink = _videoSink; + _padHandlerData.videoToConnect = _queue0; + _padHandlerData.videoSink = _appsink0; _padHandlerData.videoIsConnected = false; // Create the empty pipeline. _pipeline = gst_pipeline_new ( "video-source-pipeline" ); - if (!_pipeline || !_source || - !_videoQueue || !_videoColorSpace || ! videoScale || ! capsFilter || ! _videoSink) + if (!_pipeline || + !_queue0 || !_videoconvert0 || ! videoscale0 || ! capsfilter0 || ! _appsink0) { g_printerr ("Not all elements could be created.\n"); + + if (! _pipeline) g_printerr("_pipeline"); + if (! _queue0) g_printerr("_queue0"); + if (! _videoconvert0) g_printerr("_videoconvert0"); + if (! videoscale0) g_printerr("videoscale0"); + if (! capsfilter0) g_printerr("capsfilter0"); + if (! _appsink0) g_printerr("_appsink0"); + unloadMovie(); return -1; } + + if (_isSharedMemorySource) + { + if (! _shmsrc0 || ! _gdpdepay0) + { + g_printerr ("Not all elements could be created.\n"); + if (! _shmsrc0) g_printerr("_shmsrc0"); + if (! _gdpdepay0) g_printerr("_gdpdepay0"); + unloadMovie(); + return -1; + } + } + else + { + if (! _uridecodebin0) + { + g_printerr ("Not all elements could be created.\n"); + if (! _uridecodebin0) g_printerr("_uridecodebin0"); + 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, - _videoQueue, _videoColorSpace, videoScale, capsFilter, _videoSink, NULL); + gst_bin_add_many (GST_BIN (_pipeline), + _uridecodebin0, _queue0, _videoconvert0, + videoscale0, capsfilter0, _appsink0, NULL); - if (_live) { - gst_bin_add (GST_BIN(_pipeline), _deserializer); - if (!gst_element_link_many (_source, _deserializer, _videoQueue, NULL)) { - g_printerr ("Video elements could not be linked.\n"); + // special case for shmsrc + if (_isSharedMemorySource) + { + gst_bin_add (GST_BIN(_pipeline), _gdpdepay0); + if (! gst_element_link_many (_uridecodebin0, _gdpdepay0, _queue0, NULL)) + { + g_printerr ("Could not link shmsrc, deserializer and video queue.\n"); + } + } + else + { + if (! gst_element_link (_uridecodebin0, _queue0)) + { + g_printerr ("Could not link uridecodebin to video queue.\n"); } } - else if (!gst_element_link (_source, _videoQueue)) - g_printerr ("Video elements could not be linked.\n"); - if (!gst_element_link_many (_videoQueue, _videoColorSpace, capsFilter, videoScale, _videoSink, NULL)) { - g_printerr ("Video elements could not be linked.\n"); + if (! gst_element_link_many (_queue0, _videoconvert0, capsfilter0, videoscale0, _appsink0, NULL)) + { + g_printerr ("Could not link video queue, colorspace converter, caps filter, scaler and app sink.\n"); unloadMovie(); return false; } @@ -312,12 +379,13 @@ bool MediaImpl::loadMovie(QString filename) // Process URI. QByteArray ba = filename.toLocal8Bit(); gchar* uri = (gchar*) filename.toUtf8().constData(); - if (!_live && !gst_uri_is_valid(uri)) + if (! _isSharedMemorySource && ! gst_uri_is_valid(uri)) { // Try to convert filename to URI. GError* error = NULL; uri = gst_filename_to_uri(uri, &error); - if (error) { + if (error) + { qDebug() << "Filename to URI error: " << error->message; g_error_free(error); gst_object_unref (uri); @@ -326,23 +394,26 @@ bool MediaImpl::loadMovie(QString filename) } } - if (_live) + if (_isSharedMemorySource) + { uri = (gchar*) ba.data(); + } // Set URI to be played. qDebug() << "URI for uridecodebin: " << uri; // FIXME: sometimes it's just the path to the directory that is given, not the file itself. - // Connect to the pad-added signal - if (!_live) { - g_object_set (_source, "uri", uri, NULL); - g_signal_connect (_source, "pad-added", G_CALLBACK (MediaImpl::gstPadAddedCallback), &_padHandlerData); + if (! _isSharedMemorySource) + { + g_object_set (_uridecodebin0, "uri", uri, NULL); + g_signal_connect (_uridecodebin0, "pad-added", G_CALLBACK (MediaImpl::gstPadAddedCallback), &_padHandlerData); } - else { + else + { //qDebug() << "LIVE mode" << uri; - g_object_set (_source, "socket-path", uri, NULL); - g_object_set (_source, "is-live", TRUE, NULL); + g_object_set (_uridecodebin0, "socket-path", uri, NULL); + g_object_set (_uridecodebin0, "is-live", TRUE, NULL); _padHandlerData.videoIsConnected = true; } @@ -362,21 +433,23 @@ bool MediaImpl::loadMovie(QString filename) // Configure video appsink. GstCaps *videoCaps = gst_caps_from_string ("video/x-raw,format=RGBA"); - g_object_set (capsFilter, "caps", videoCaps, NULL); - g_object_set (_videoSink, "emit-signals", TRUE, + g_object_set (capsfilter0, "caps", videoCaps, NULL); + g_object_set (_appsink0, "emit-signals", TRUE, "max-buffers", 1, // only one buffer (the last) is maintained in the queue "drop", TRUE, // ... other buffers are dropped "sync", TRUE, NULL); - g_signal_connect (_videoSink, "new-sample", G_CALLBACK (MediaImpl::gstNewSampleCallback), this); + g_signal_connect (_appsink0, "new-sample", G_CALLBACK (MediaImpl::gstNewSampleCallback), this); gst_caps_unref (videoCaps); // Listen to the bus. _bus = gst_element_get_bus (_pipeline); // Start playing. - if (!_live && !setPlayState(true)) + if (! _isSharedMemorySource && ! setPlayState(true)) + { return false; + } qDebug() << "Pipeline started."; @@ -384,19 +457,23 @@ bool MediaImpl::loadMovie(QString filename) return true; } -bool MediaImpl::runVideo() { - - if (!_preRun()) +bool MediaImpl::runVideo() +{ + if (! _preRun()) + { return false; + } bool bitsChanged = false; // Check if we have some frames in the input buffer. - if (_queueInputBuffer.size() > 0) { - + if (_queueInputBuffer.size() > 0) + { // Pull video. if (_videoPull()) + { bitsChanged = true; + } //std::cout << "VideoImpl::runVideo: read frame #" << _videoNewBufferCounter << std::endl; } @@ -409,17 +486,20 @@ bool MediaImpl::runVideo() { * textures will appear to be flickering/alternating. Maybe a better solution is * needed (in the GL layer or here?)*/ else - bitsChanged = true; + { + bitsChanged = true; + } _postRun(); - return bitsChanged; } bool MediaImpl::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) @@ -431,7 +511,6 @@ bool MediaImpl::setPlayState(bool play) else { _setReady(play); - return true; } } @@ -445,11 +524,14 @@ bool MediaImpl::_preRun() resetMovie(); } else + { _setFinished(false); + } if (!_movieReady || !_padHandlerData.videoIsConnected) + { return false; - + } return true; } @@ -459,15 +541,16 @@ void MediaImpl::_postRun() if (_bus != NULL) { GstMessage *msg = gst_bus_timed_pop_filtered( - _bus, 0, - (GstMessageType) (GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_ERROR | GST_MESSAGE_EOS)); + _bus, 0, + (GstMessageType) (GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_ERROR | GST_MESSAGE_EOS)); - if (msg != NULL) { + if (msg != NULL) + { GError *err; gchar *debug_info; - switch (GST_MESSAGE_TYPE (msg)) { - + 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", @@ -477,9 +560,12 @@ void MediaImpl::_postRun() g_clear_error(&err); g_free(debug_info); - if (!_live) + if (!_isSharedMemorySource) + { _terminate = true; - else { + } + else + { _attached = false; gst_element_set_state (_pipeline, GST_STATE_PAUSED); gst_element_set_state (_pipeline, GST_STATE_NULL); @@ -552,12 +638,17 @@ void MediaImpl::_setReady(bool ready) _movieReady = ready; } -void MediaImpl::_setFinished(bool finished) { +void MediaImpl::_setFinished(bool finished) +{ Q_UNUSED(finished); // qDebug() << "Clip " << (finished ? "finished" : "not finished"); } -void MediaImpl::gstPadAddedCallback(GstElement *src, GstPad *newPad, MediaImpl::GstPadHandlerData* data) { +/** + * FIXME: remove GOTO + */ +void MediaImpl::gstPadAddedCallback(GstElement *src, GstPad *newPad, MediaImpl::GstPadHandlerData* data) +{ g_print ("Received new pad '%s' from '%s':\n", GST_PAD_NAME (newPad), GST_ELEMENT_NAME (src)); GstPad *sinkPad = NULL; @@ -598,7 +689,8 @@ void MediaImpl::gstPadAddedCallback(GstElement *src, GstPad *newPad, MediaImpl:: } // Attempt the link - if (GST_PAD_LINK_FAILED (gst_pad_link (newPad, sinkPad))) { + if (GST_PAD_LINK_FAILED (gst_pad_link (newPad, sinkPad))) + { g_print (" Type is '%s' but link failed.\n", newPadType); goto exit; } else { @@ -609,9 +701,13 @@ void MediaImpl::gstPadAddedCallback(GstElement *src, GstPad *newPad, MediaImpl:: 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); + } } diff --git a/MediaImpl.h b/MediaImpl.h index b48a94b..4d2b225 100644 --- a/MediaImpl.h +++ b/MediaImpl.h @@ -123,16 +123,16 @@ private: // gstreamer GstBus *_bus; GstElement *_pipeline; - GstElement *_source; - GstElement *_deserializer; - GstElement *_audioQueue; - GstElement *_audioConvert; - GstElement *_audioResample; - GstElement *_videoQueue; - GstElement *_videoConvert; - GstElement *_videoColorSpace; - GstElement *_audioSink; - GstElement *_videoSink; + GstElement *_uridecodebin0; + GstElement *_shmsrc0; + GstElement *_gdpdepay0; + //GstElement *_audioQueue; + //GstElement *_audioConvert; + //GstElement *_audioResample; + GstElement *_queue0; + GstElement *_videoconvert0; + //GstElement *_audioSink; + GstElement *_appsink0; GstSample *_frame; GSource *_pollSource; @@ -143,7 +143,7 @@ private: uchar *_data; bool _seekEnabled; - bool _live; + bool _isSharedMemorySource; bool _attached; int _audioNewBufferCounter; From 1d152d487f1dccb6fc7085c7a1c62a319d2e09e3 Mon Sep 17 00:00:00 2001 From: Tats Date: Sat, 18 Oct 2014 13:40:42 +0000 Subject: [PATCH 04/22] Integrated latest changes from Dame wrt item selections (paint and mapping lists). --- MainWindow.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/MainWindow.cpp b/MainWindow.cpp index d0c76c3..d10a590 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -94,13 +94,12 @@ void MainWindow::handlePaintItemSelectionChanged() if (paintItemSelected) { // Set current paint. - uid idx = getItemId(*item); - if (currentPaintId != idx) { - setCurrentPaint(idx); - // Unselect current mapping. + uid paintId = getItemId(*item); + // Unselect current mapping. + if (currentPaintId != paintId) removeCurrentMapping(); - mappingList->clearSelection(); - } + // Set current paint. + setCurrentPaint(paintId); } else removeCurrentPaint(); @@ -126,11 +125,11 @@ void MainWindow::handleMappingItemSelectionChanged() currentSelectedItem = item; // Set current paint and mappings. - Mapping::ptr mapping = mappingManager->getMappingById(idm); + uid mappingId = getItemId(*item); + Mapping::ptr mapping = mappingManager->getMappingById(mappingId); uid paintId = mapping->getPaint()->getId(); + setCurrentMapping(mappingId); setCurrentPaint(paintId); - - setCurrentMapping(mapping->getId()); } @@ -166,6 +165,7 @@ void MainWindow::handleMappingIndexesMoved() void MainWindow::handleItemSelected(QListWidgetItem* item) { + Q_UNUSED(item); // Change currently selected item. //currentSelectedItem = item; } @@ -1920,11 +1920,13 @@ void MainWindow::setCurrentMapping(int uid) void MainWindow::removeCurrentPaint() { _hasCurrentPaint = false; currentPaintId = NULL_UID; + paintList->clearSelection(); } void MainWindow::removeCurrentMapping() { _hasCurrentMapping = false; currentMappingId = NULL_UID; + mappingList->clearSelection(); } void MainWindow::startOscReceiver() From c309219cea304ef18e589848f685d11b1f2be6c1 Mon Sep 17 00:00:00 2001 From: Tats Date: Sat, 18 Oct 2014 14:28:22 +0000 Subject: [PATCH 05/22] Fixed bug #33: The --fullscreen option doesn't work. --- MainWindow.cpp | 9 +++++++-- MainWindow.h | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/MainWindow.cpp b/MainWindow.cpp index d10a590..d0dbb0c 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -1198,9 +1198,14 @@ void MainWindow::createActions() connect(stickyVertices, SIGNAL(toggled(bool)), outputWindow->getCanvas(), SLOT(enableStickyVertices(bool))); } -void MainWindow::enableFullscreen() +void MainWindow::startFullScreen() { - outputWindowFullScreen->setEnabled(true); + // Remove canvas controls. + displayCanvasControls->setChecked(false); + // Display output window. + displayOutputWindow->setChecked(true); + // Send fullscreen. + outputWindowFullScreen->setChecked(true); } void MainWindow::createMenus() diff --git a/MainWindow.h b/MainWindow.h index 7cf2c5c..5df305d 100644 --- a/MainWindow.h +++ b/MainWindow.h @@ -323,7 +323,7 @@ public: void removeCurrentPaint(); void removeCurrentMapping(); - void enableFullscreen(); + void startFullScreen(); void setOscPort(QString portNumber); public: // Constants. /////////////////////////////////////////////////////////////////////////////////////// From 8e9f7dd7e5999179beef23009a3b330d0edf159c Mon Sep 17 00:00:00 2001 From: Vasilis Liaskovitis Date: Sat, 18 Oct 2014 17:12:39 +0200 Subject: [PATCH 06/22] use startFullScreen to fix compilation failure (introduced for bug #33) --- main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index 3cb5421..7ccc150 100644 --- a/main.cpp +++ b/main.cpp @@ -144,7 +144,7 @@ int main(int argc, char *argv[]) if (parser.isSet(fullscreenOption)) { qDebug() << "TODO: Running in fullscreen mode"; - win.enableFullscreen(); + win.startFullScreen(); } // Start app. From 835165c89aee93983eec564f1392b1a68f559969 Mon Sep 17 00:00:00 2001 From: Vasilis Liaskovitis Date: Sat, 18 Oct 2014 19:03:06 +0200 Subject: [PATCH 07/22] Fix some issues with shmsrc pipeline and video dimensions (bug #29) Shmsrc seems to work with a gstreamer 1.4 build, but not with distro gstreamer (segfault due to missing fix https://bugzilla.gnome.org/show_bug.cgi?id=731093) Reader / writer of live source need to be using same gstreamer build, otherwise some pipeline problems appear in deserializing and caps detection. Playback of the first shape mapped with the live source doesn't seem to work (?), so this bug stiil needs investigation. --- MediaImpl.cpp | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/MediaImpl.cpp b/MediaImpl.cpp index ac1fdb4..083971e 100644 --- a/MediaImpl.cpp +++ b/MediaImpl.cpp @@ -103,6 +103,19 @@ bool MediaImpl::_videoPull() // Pull current frame buffer. GstBuffer *buffer = gst_sample_get_buffer(sample); + // for live sources, video dimensions have not been set, because + // gstPadAddedCallback is never called. Fix dimensions from first sample / + // caps we receive + if (_isSharedMemorySource && ( _padHandlerData.width == -1 || + _padHandlerData.height == -1)) { + GstCaps *caps = gst_sample_get_caps(sample); + GstStructure *structure; + structure = gst_caps_get_structure(caps, 0); + gst_structure_get_int(structure, "width", &_padHandlerData.width); + gst_structure_get_int(structure, "height", &_padHandlerData.height); + // g_print("Size is %u x %u\n", _padHandlerData.width, _padHandlerData.height); + } + GstMapInfo map; if (gst_buffer_map(buffer, &map, GST_MAP_READ)) { @@ -347,14 +360,14 @@ bool MediaImpl::loadMovie(QString filename) // 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), - _uridecodebin0, _queue0, _videoconvert0, - videoscale0, capsfilter0, _appsink0, NULL); + _isSharedMemorySource ? _shmsrc0 : _uridecodebin0, _queue0, + _videoconvert0, videoscale0, capsfilter0, _appsink0, NULL); // special case for shmsrc if (_isSharedMemorySource) { gst_bin_add (GST_BIN(_pipeline), _gdpdepay0); - if (! gst_element_link_many (_uridecodebin0, _gdpdepay0, _queue0, NULL)) + if (! gst_element_link_many (_shmsrc0, _gdpdepay0, _queue0, NULL)) { g_printerr ("Could not link shmsrc, deserializer and video queue.\n"); } @@ -410,8 +423,8 @@ bool MediaImpl::loadMovie(QString filename) else { //qDebug() << "LIVE mode" << uri; - g_object_set (_uridecodebin0, "socket-path", uri, NULL); - g_object_set (_uridecodebin0, "is-live", TRUE, NULL); + g_object_set (_shmsrc0, "socket-path", uri, NULL); + g_object_set (_shmsrc0, "is-live", TRUE, NULL); _padHandlerData.videoIsConnected = true; } @@ -448,7 +461,6 @@ bool MediaImpl::loadMovie(QString filename) { return false; } - qDebug() << "Pipeline started."; //_movieReady = true; From 6308aca59f49548713e424c8bbfeeb9a3e413c4b Mon Sep 17 00:00:00 2001 From: Tats Date: Sat, 18 Oct 2014 17:07:59 +0000 Subject: [PATCH 08/22] Fixed bug #33: The --fullscreen option doesn't work. --- MainWindow.cpp | 9 +++++++-- MainWindow.h | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/MainWindow.cpp b/MainWindow.cpp index d10a590..d0dbb0c 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -1198,9 +1198,14 @@ void MainWindow::createActions() connect(stickyVertices, SIGNAL(toggled(bool)), outputWindow->getCanvas(), SLOT(enableStickyVertices(bool))); } -void MainWindow::enableFullscreen() +void MainWindow::startFullScreen() { - outputWindowFullScreen->setEnabled(true); + // Remove canvas controls. + displayCanvasControls->setChecked(false); + // Display output window. + displayOutputWindow->setChecked(true); + // Send fullscreen. + outputWindowFullScreen->setChecked(true); } void MainWindow::createMenus() diff --git a/MainWindow.h b/MainWindow.h index 7cf2c5c..5df305d 100644 --- a/MainWindow.h +++ b/MainWindow.h @@ -323,7 +323,7 @@ public: void removeCurrentPaint(); void removeCurrentMapping(); - void enableFullscreen(); + void startFullScreen(); void setOscPort(QString portNumber); public: // Constants. /////////////////////////////////////////////////////////////////////////////////////// From 6c95c8f11a43b665591dc4a5f249c769c64cb237 Mon Sep 17 00:00:00 2001 From: Alexandre Quessy Date: Sat, 18 Oct 2014 21:02:42 -0400 Subject: [PATCH 09/22] Add Dame Diongue to the list of developers --- MM.cpp | 1 + MM.h | 1 + MainWindow.cpp | 5 ++--- README | 1 + 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/MM.cpp b/MM.cpp index 97098ec..3455bbb 100644 --- a/MM.cpp +++ b/MM.cpp @@ -20,6 +20,7 @@ #include "MM.h" const QString MM::VERSION = "0.1.1"; +const QString MM::COPYRIGHT_OWNERS = "Sofian Audry, Alexandre Quessy, Mike Latona, Vasilis Liaskovitis, Dame Diongue"; const QString MM::FILE_EXTENSION = "mmp"; const QString MM::VIDEO_FILES_FILTER = "*.mov *.mp4 *.avi *.ogg *.ogv *.mpeg *.mpeg1 *.mpeg4 *.mpg *.mpg2 *.mp2 *.mjpq *.mjp *.wmv *sock"; const QString MM::IMAGE_FILES_FILTER = "*.jpg *.jpeg *.gif *.png *.tiff *.tif *.bmp"; diff --git a/MM.h b/MM.h index 22fc5aa..7ffb8a0 100644 --- a/MM.h +++ b/MM.h @@ -30,6 +30,7 @@ class MM public: // General. static const QString VERSION; + static const QString COPYRIGHT_OWNERS; static const QString FILE_EXTENSION; static const QString VIDEO_FILES_FILTER; static const QString IMAGE_FILES_FILTER; diff --git a/MainWindow.cpp b/MainWindow.cpp index d0dbb0c..bd547e5 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -532,8 +532,7 @@ void MainWindow::about() // Pop-up about dialog. QMessageBox::about(this, tr("About MapMap"), tr("

%1

" - "

Copyright © 2013 Sofian Audry, Alexandre Quessy, " - "Mike Latona and Vasilis Liaskovitis.

" + "

Copyright © 2013 %2.

" "

MapMap is a free software for video mapping.

" "

Projection mapping, also known as video mapping and spatial augmented reality, " "is a projection technology used to turn objects, often irregularly shaped, into " @@ -550,7 +549,7 @@ void MainWindow::about() "La Francophonie.

" "

http://mapmap.info
" "http://www.francophonie.org

" - ).arg(MM::VERSION)); + ).arg(MM::VERSION, MM::COPYRIGHT_OWNERS)); // Restart video playback. XXX Hack videoTimer->start(); diff --git a/README b/README index 3fc5bd7..87fa1b4 100644 --- a/README +++ b/README @@ -35,6 +35,7 @@ Authors * Sofian Audry: lead developer, user interface designer, project manager. * Vasilis Liaskovitis: developer. * Mike Latona: user interface designer. +* Dame Diongue: developer. Contributors ------------ From c85b4e073c236dd1228f86192f5dc12d07ede373 Mon Sep 17 00:00:00 2001 From: Vasilis Liaskovitis Date: Mon, 20 Oct 2014 00:33:53 +0200 Subject: [PATCH 10/22] MediaImpl: move buffer processing in gstNewSampleCallback (bug #30) Buffer releasing should happen independently of videoPull() getting called for texture updates. We move all processing and buffer unreferencing in gstNewSampleCallback. This way we don't run out of memory in case of video pipeline playing without texture/mappings. --- MediaImpl.cpp | 67 ++++++++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 36 deletions(-) diff --git a/MediaImpl.cpp b/MediaImpl.cpp index 083971e..2706dbf 100644 --- a/MediaImpl.cpp +++ b/MediaImpl.cpp @@ -98,42 +98,7 @@ bool MediaImpl::_videoPull() // Either means we are not playing or we have reached EOS. return false; } - else - { - // Pull current frame buffer. - GstBuffer *buffer = gst_sample_get_buffer(sample); - - // for live sources, video dimensions have not been set, because - // gstPadAddedCallback is never called. Fix dimensions from first sample / - // caps we receive - if (_isSharedMemorySource && ( _padHandlerData.width == -1 || - _padHandlerData.height == -1)) { - GstCaps *caps = gst_sample_get_caps(sample); - GstStructure *structure; - structure = gst_caps_get_structure(caps, 0); - gst_structure_get_int(structure, "width", &_padHandlerData.width); - gst_structure_get_int(structure, "height", &_padHandlerData.height); - // g_print("Size is %u x %u\n", _padHandlerData.width, _padHandlerData.height); - } - - GstMapInfo map; - if (gst_buffer_map(buffer, &map, GST_MAP_READ)) - { - // For debugging: - //gst_util_dump_mem(map.data, map.size) - _data = map.data; - // release memory previously mapped with gst_buffer_map - gst_buffer_unmap(buffer, &map); - if (this->_frame != NULL) - { - // queue this frame to async queue - _queueOutputBuffer.put(this->_frame); - } - _frame = sample; - } - - return true; - } + else return true; } bool MediaImpl::_eos() const @@ -152,11 +117,41 @@ bool MediaImpl::_eos() const GstFlowReturn MediaImpl::gstNewSampleCallback(GstElement*, MediaImpl *p) { GstSample *sample; + GstMapInfo map; sample = gst_app_sink_pull_sample(GST_APP_SINK(p->_appsink0)); //g_signal_emit_by_name (p->_appsink0, "pull-sample", &sample); // save last frame p->getQueueInputBuffer()->put(sample); + GstBuffer *buffer = gst_sample_get_buffer(sample); + + // for live sources, video dimensions have not been set, because + // gstPadAddedCallback is never called. Fix dimensions from first sample / + // caps we receive + if (p->_isSharedMemorySource && ( p->_padHandlerData.width == -1 || + p->_padHandlerData.height == -1)) { + GstCaps *caps = gst_sample_get_caps(sample); + GstStructure *structure; + structure = gst_caps_get_structure(caps, 0); + gst_structure_get_int(structure, "width", &p->_padHandlerData.width); + gst_structure_get_int(structure, "height", &p->_padHandlerData.height); + // g_print("Size is %u x %u\n", _padHandlerData.width, _padHandlerData.height); + } + + if (gst_buffer_map(buffer, &map, GST_MAP_READ)) + { + // For debugging: + //gst_util_dump_mem(map.data, map.size) + p->_data = map.data; + // release memory previously mapped with gst_buffer_map + gst_buffer_unmap(buffer, &map); + if (p->_frame != NULL) + { + // queue this frame to async queue + p->getQueueOutputBuffer()->put(p->_frame); + } + p->_frame = sample; + } // we only keep the last one: if (p->getQueueOutputBuffer()->size() > 1) From 7d4e060b782b226dd13b4af573ece2b12045373f Mon Sep 17 00:00:00 2001 From: Tats Date: Mon, 20 Oct 2014 10:39:20 +0000 Subject: [PATCH 11/22] Removed commandline options for Qt < 5.0.0 (because it is not supported then). --- main.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index 7ccc150..1790185 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,15 @@ // NOTE: To run, it is recommended not to be in Compiz or Beryl, they have shown some instability. +#define USING_QT_5 (QT_VERSION >= QT_VERSION_CHECK(5,0,0)) + #include #include #include +#include +#if USING_QT_5 #include #include -#include +#endif #include "MM.h" #include "MainWindow.h" #include "MainApplication.h" @@ -45,6 +49,7 @@ int main(int argc, char *argv[]) MainApplication app(argc, argv); +#if USING_QT_5 QCommandLineParser parser; parser.setApplicationDescription("Video mapping editor"); @@ -81,6 +86,7 @@ int main(int argc, char *argv[]) { Util::eraseSettings(); } +#endif if (! QGLFormat::hasOpenGL()) qFatal("This system has no OpenGL support."); @@ -120,6 +126,7 @@ int main(int argc, char *argv[]) //win.setLocale(QLocale("fr")); +#if USING_QT_5 QString projectFileValue = parser.value("file"); if (projectFileValue != "") { @@ -131,6 +138,7 @@ int main(int argc, char *argv[]) { win.setOscPort(oscPortNumberValue); } +#endif // Terminate splash. splash.showMessage(" " + QObject::tr("Done."), @@ -141,11 +149,13 @@ int main(int argc, char *argv[]) // Launch program. win.show(); +#if USING_QT_5 if (parser.isSet(fullscreenOption)) { qDebug() << "TODO: Running in fullscreen mode"; win.startFullScreen(); } +#endif // Start app. return app.exec(); From 2022379c81fdab10a2e8411fc5c8a94b0a59ebde Mon Sep 17 00:00:00 2001 From: Tats Date: Mon, 20 Oct 2014 11:53:46 +0000 Subject: [PATCH 12/22] Unref more leaking samples in MediaImpl.cpp (queues are kept at a limited size). --- MediaImpl.cpp | 46 +++++++++++++++++++++++++++++++++++----------- MediaImpl.h | 2 ++ 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/MediaImpl.cpp b/MediaImpl.cpp index 2706dbf..61a6c6c 100644 --- a/MediaImpl.cpp +++ b/MediaImpl.cpp @@ -116,14 +116,18 @@ bool MediaImpl::_eos() const GstFlowReturn MediaImpl::gstNewSampleCallback(GstElement*, MediaImpl *p) { - GstSample *sample; - GstMapInfo map; - sample = gst_app_sink_pull_sample(GST_APP_SINK(p->_appsink0)); - //g_signal_emit_by_name (p->_appsink0, "pull-sample", &sample); - // save last frame + // get next frame + GstSample *sample = gst_app_sink_pull_sample(GST_APP_SINK(p->_appsink0)); + + // save frame to input buffer queue p->getQueueInputBuffer()->put(sample); - GstBuffer *buffer = gst_sample_get_buffer(sample); + + // keep input queue to a reasonable size + // the input buffer never releases memory: it is the output queue that releases it. + while (p->getQueueInputBuffer()->size() > MAX_SAMPLES_IN_BUFFER_QUEUES) { + p->getQueueInputBuffer()->get(); // just remove it from queue + } // for live sources, video dimensions have not been set, because // gstPadAddedCallback is never called. Fix dimensions from first sample / @@ -138,23 +142,28 @@ GstFlowReturn MediaImpl::gstNewSampleCallback(GstElement*, MediaImpl *p) // g_print("Size is %u x %u\n", _padHandlerData.width, _padHandlerData.height); } + GstMapInfo map; + GstBuffer *buffer = gst_sample_get_buffer(sample); if (gst_buffer_map(buffer, &map, GST_MAP_READ)) { // For debugging: //gst_util_dump_mem(map.data, map.size) + // retrieve data from map info p->_data = map.data; // release memory previously mapped with gst_buffer_map - gst_buffer_unmap(buffer, &map); + gst_buffer_unmap(buffer, &map); + + // queue previous frame to async queue if (p->_frame != NULL) { - // queue this frame to async queue p->getQueueOutputBuffer()->put(p->_frame); } + // set current frame p->_frame = sample; - } + } - // we only keep the last one: - if (p->getQueueOutputBuffer()->size() > 1) + // keep only the last few output frames. + while (p->getQueueOutputBuffer()->size() > MAX_SAMPLES_IN_BUFFER_QUEUES) { sample = p->getQueueOutputBuffer()->get(); // We free the memory for the previous frame here @@ -217,6 +226,21 @@ void MediaImpl::freeResources() _pipeline = NULL; } + qDebug() << "Freeing async queue" << endl; + // Clear remaining samples in queue. + while (getQueueOutputBuffer()->size() > 0) + { + GstSample *sample = getQueueOutputBuffer()->get(); + // We free the memory for the previous frame here + gst_sample_unref(sample); + } + + // Also free last frame. + if (_frame) { + gst_sample_unref(_frame); + _frame = NULL; + } + _uridecodebin0 = NULL; _queue0 = NULL; _videoconvert0 = NULL; diff --git a/MediaImpl.h b/MediaImpl.h index 4d2b225..1c6672f 100644 --- a/MediaImpl.h +++ b/MediaImpl.h @@ -156,6 +156,8 @@ private: private: QString _uri; + static const int MAX_SAMPLES_IN_BUFFER_QUEUES = 30; + }; #endif /* ifndef */ From 2d55e6d9c6a494a369b0843b5784b4bd93642ae4 Mon Sep 17 00:00:00 2001 From: Tats Date: Mon, 20 Oct 2014 11:54:21 +0000 Subject: [PATCH 13/22] Free memory from call to gst_structure_to_string(). --- MediaImpl.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MediaImpl.cpp b/MediaImpl.cpp index 61a6c6c..0c92636 100644 --- a/MediaImpl.cpp +++ b/MediaImpl.cpp @@ -690,7 +690,9 @@ void MediaImpl::gstPadAddedCallback(GstElement *src, GstPad *newPad, MediaImpl:: GstCaps *newPadCaps = gst_pad_query_caps (newPad, NULL); 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)); + gchar *newPadStructStr = gst_structure_to_string(newPadStruct); + g_print("Structure is %s\n", newPadStructStr); + g_free(newPadStructStr); if (g_str_has_prefix (newPadType, "video/x-raw")) { sinkPad = gst_element_get_static_pad (data->videoToConnect, "sink"); From 69c73d57f0a2167da13318bc156df92be0e19d47 Mon Sep 17 00:00:00 2001 From: Tats Date: Mon, 20 Oct 2014 12:41:19 +0000 Subject: [PATCH 14/22] Added application information. --- MM.cpp | 3 +++ MM.h | 3 +++ MainApplication.cpp | 6 ++++++ MainApplication.h | 1 + 4 files changed, 13 insertions(+) diff --git a/MM.cpp b/MM.cpp index 3455bbb..5dbd57f 100644 --- a/MM.cpp +++ b/MM.cpp @@ -19,8 +19,11 @@ #include "MM.h" +const QString MM::APPLICATION_NAME = "MapMap"; const QString MM::VERSION = "0.1.1"; const QString MM::COPYRIGHT_OWNERS = "Sofian Audry, Alexandre Quessy, Mike Latona, Vasilis Liaskovitis, Dame Diongue"; +const QString MM::ORGANIZATION_NAME = "MapMap"; +const QString MM::ORGANIZATION_DOMAIN = "mapmap.info"; const QString MM::FILE_EXTENSION = "mmp"; const QString MM::VIDEO_FILES_FILTER = "*.mov *.mp4 *.avi *.ogg *.ogv *.mpeg *.mpeg1 *.mpeg4 *.mpg *.mpg2 *.mp2 *.mjpq *.mjp *.wmv *sock"; const QString MM::IMAGE_FILES_FILTER = "*.jpg *.jpeg *.gif *.png *.tiff *.tif *.bmp"; diff --git a/MM.h b/MM.h index 7ffb8a0..fade564 100644 --- a/MM.h +++ b/MM.h @@ -29,8 +29,11 @@ class MM { public: // General. + static const QString APPLICATION_NAME; static const QString VERSION; static const QString COPYRIGHT_OWNERS; + static const QString ORGANIZATION_NAME; + static const QString ORGANIZATION_DOMAIN; static const QString FILE_EXTENSION; static const QString VIDEO_FILES_FILTER; static const QString IMAGE_FILES_FILTER; diff --git a/MainApplication.cpp b/MainApplication.cpp index 4752861..ed17f8c 100644 --- a/MainApplication.cpp +++ b/MainApplication.cpp @@ -25,6 +25,12 @@ MainApplication::MainApplication(int &argc, char *argv[]) { // Initialize GStreamer. gst_init (NULL, NULL); + + // Set application information. + setApplicationName(MM::APPLICATION_NAME); + setApplicationVersion(MM::VERSION); + setOrganizationName(MM::ORGANIZATION_NAME); + setOrganizationDomain(MM::ORGANIZATION_DOMAIN); } MainApplication::~MainApplication() diff --git a/MainApplication.h b/MainApplication.h index 0e26eb0..6ceaa8f 100644 --- a/MainApplication.h +++ b/MainApplication.h @@ -24,6 +24,7 @@ #include #include #include +#include "MM.h" class MainApplication : public QApplication { From a9898f4585a5c4ffeb7db453ad395dc43a9c1382 Mon Sep 17 00:00:00 2001 From: Alexandre Quessy Date: Mon, 20 Oct 2014 12:08:59 -0400 Subject: [PATCH 15/22] Fix a memory leak with the shmsrc poller --- MediaImpl.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MediaImpl.cpp b/MediaImpl.cpp index 083971e..6d6fe56 100644 --- a/MediaImpl.cpp +++ b/MediaImpl.cpp @@ -187,6 +187,7 @@ _attached(false), _movieReady(false), _uri(uri) { + _pollSource = NULL; if (uri != "") { loadMovie(uri); @@ -204,6 +205,7 @@ void MediaImpl::unloadMovie() // Un-ready. _setReady(false); + } void MediaImpl::freeResources() @@ -229,6 +231,13 @@ void MediaImpl::freeResources() _appsink0 = NULL; _frame = NULL; _padHandlerData = GstPadHandlerData(); + + // unref the shmsrc poller + if (_pollSource) + { + g_source_unref(_pollSource); + _pollSource = NULL; + } } void MediaImpl::resetMovie() From ca4eef0e7114e6f38be710e6ade0c04ca9639e21 Mon Sep 17 00:00:00 2001 From: Alexandre Quessy Date: Mon, 20 Oct 2014 12:09:18 -0400 Subject: [PATCH 16/22] Add documentation strings in MediaImpl.h --- MediaImpl.h | 105 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 99 insertions(+), 6 deletions(-) diff --git a/MediaImpl.h b/MediaImpl.h index 4d2b225..c080e81 100644 --- a/MediaImpl.h +++ b/MediaImpl.h @@ -46,25 +46,75 @@ class MediaImpl { public: + /** + * Constructor. + * This media player works for both video files and shared memory sockets. + * If live is true, it's a shared memory socket. + */ MediaImpl(const QString uri, bool live); ~MediaImpl(); // void setUri(const QString uri); + /** + * Returns whether or not GStreamer video support is ok. + */ static bool hasVideoSupport(); + /** + * Sets up the player. + * Basically calls loadMovie(). + */ void build(); + /** + * Returns the width of the video image. + */ int getWidth() const; + /** + * Returns the height of the video image. + */ int getHeight() const; + /** + * Returns the path to the media file being played. + */ QString getUri() const; + /** + * When using the shared memory source, returns whether or not we + * are attached to a shared memory socket. + */ bool getAttached(); + /** + * Returns the raw image of the last video frame. + * It is currently unused! + */ const uchar* getBits() const; + /** + * Checks if the pipeline is ready. + * + * Returns whether or not the elements in the pipeline are connected, + * and if we are using shmsrc, if the shared memory socket is being read. + */ bool isReady() const { return _padHandlerData.videoIsConnected; } - /// Returns true if the image has changed. + /** + * Tries to pull a video frame from the queue. + * + * Returns true if the image has changed. + */ bool runVideo(); -// void runAudio(); + + // void runAudio(); + + /** + * Loads a new movie file. + * + * Creates a new GStreamer pipeline, opens a movie or a shmsrc socket. + */ bool loadMovie(QString filename); bool setPlayState(bool play); + /** + * Tells the MediaImpl that we are actually reading from a shmsrc. + * Called from the GStreamer callback of the shmsrc. + */ void setAttached(bool attach); void resetMovie(); @@ -74,10 +124,21 @@ protected: void freeResources(); private: + /** + * Tries to pull a video frame from the asynchronous input buffer. + * + * Pushes the frame into the asynchronous output buffer. + */ bool _videoPull(); + /** + * Checks if we reached the end of the video file. + * + * Returns false if the pipeline is not ready yet. + */ bool _eos() const; -// void _finish(); -// void _init(); + + // void _finish(); + // void _init(); bool _preRun(); void _postRun(); @@ -108,6 +169,7 @@ public: // 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, MediaImpl::GstPadHandlerData* data); + AsyncQueue *getQueueInputBuffer() { return &this->_queueInputBuffer; } @@ -118,9 +180,12 @@ public: private: //locals + /** + * Path of the movie being played. + */ QString _currentMovie; - // gstreamer + // gstreamer elements GstBus *_bus; GstElement *_pipeline; GstElement *_uridecodebin0; @@ -133,29 +198,57 @@ private: GstElement *_videoconvert0; //GstElement *_audioSink; GstElement *_appsink0; + /** + * Temporary contains the image data of the last frame. + */ GstSample *_frame; + /** + * shmsrc socket poller. + */ GSource *_pollSource; GstPadHandlerData _padHandlerData; + // unused int _width; + // unused int _height; + + /** + * Raw image data of the last video frame. + */ uchar *_data; bool _seekEnabled; + /** + * Whether or not we are reading video from a shmsrc. + */ bool _isSharedMemorySource; + /** + * Whether or not we are attached to a shmsrc, if using a shmsrc. + */ bool _attached; int _audioNewBufferCounter; bool _terminate; bool _movieReady; + /** + * Input buffer. + * Contains the raw data of last image. + */ AsyncQueue _queueInputBuffer; + /** + * Output buffer. + * Contains the raw data of last image. + */ AsyncQueue _queueOutputBuffer; private: + /** + * Path of the movie file being played. + */ QString _uri; - }; #endif /* ifndef */ From cf34d604c4d87373cf8e3f3e85aa19b2cd8c735a Mon Sep 17 00:00:00 2001 From: Alexandre Quessy Date: Mon, 20 Oct 2014 12:12:41 -0400 Subject: [PATCH 17/22] fix --- MediaImpl.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/MediaImpl.h b/MediaImpl.h index 5804aa8..aac42d9 100644 --- a/MediaImpl.h +++ b/MediaImpl.h @@ -249,12 +249,8 @@ private: * Path of the movie file being played. */ QString _uri; -<<<<<<< HEAD -======= static const int MAX_SAMPLES_IN_BUFFER_QUEUES = 30; - ->>>>>>> 2d55e6d9c6a494a369b0843b5784b4bd93642ae4 }; #endif /* ifndef */ From 7fe2a1f1a745990dad287ae9967f2dcae45fed5e Mon Sep 17 00:00:00 2001 From: Tats Date: Mon, 20 Oct 2014 17:00:22 +0000 Subject: [PATCH 18/22] Bugfix #30 : Reimplemented MediaImpl removing the defective queue system and implementing a thread-safe system. Removed the memory leaks previously shown by valgrind. --- Mapper.cpp | 2 + MediaImpl.cpp | 116 +++++++++++++++++++++++++++----------------------- MediaImpl.h | 34 +++++++-------- Paint.cpp | 8 ++++ Paint.h | 16 ++++++- 5 files changed, 103 insertions(+), 73 deletions(-) diff --git a/Mapper.cpp b/Mapper.cpp index e5442b0..63cd193 100644 --- a/Mapper.cpp +++ b/Mapper.cpp @@ -324,12 +324,14 @@ void TextureMapper::_preDraw(QPainter* painter) glBindTexture(GL_TEXTURE_2D, texture->getTextureId()); // Copy bits to texture iff necessary. + texture->lockMutex(); if (texture->bitsHaveChanged()) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture->getWidth(), texture->getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, texture->getBits()); } + texture->unlockMutex(); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); diff --git a/MediaImpl.cpp b/MediaImpl.cpp index 1652a76..5cadd3c 100644 --- a/MediaImpl.cpp +++ b/MediaImpl.cpp @@ -54,6 +54,10 @@ int MediaImpl::getHeight() const const uchar* MediaImpl::getBits() const { + if (_currentFrameSample == NULL) + return NULL; + + return _data; } @@ -83,15 +87,16 @@ void MediaImpl::build() MediaImpl::~MediaImpl() { + // Free all resources. freeResources(); - /* _data points to gstreamer-allocated data, we don't manage it ourselves */ - //if (_data) - // free(_data); + + // Free mutex locker object. + delete _mutexLocker; } bool MediaImpl::_videoPull() { - GstSample *sample = _queueInputBuffer.get(); + GstSample *sample = _currentFrameSample; if (sample == NULL) { @@ -116,22 +121,21 @@ bool MediaImpl::_eos() const GstFlowReturn MediaImpl::gstNewSampleCallback(GstElement*, MediaImpl *p) { + // Make it thread-safe. + p->lockMutex(); - // get next frame + // Get next frame. GstSample *sample = gst_app_sink_pull_sample(GST_APP_SINK(p->_appsink0)); - // save frame to input buffer queue - p->getQueueInputBuffer()->put(sample); + // Unref last frame. + p->_freeCurrentSample(); - // keep input queue to a reasonable size - // the input buffer never releases memory: it is the output queue that releases it. - while (p->getQueueInputBuffer()->size() > MAX_SAMPLES_IN_BUFFER_QUEUES) { - p->getQueueInputBuffer()->get(); // just remove it from queue - } + // Set current frame. + p->_currentFrameSample = sample; - // for live sources, video dimensions have not been set, because + // For live sources, video dimensions have not been set, because // gstPadAddedCallback is never called. Fix dimensions from first sample / - // caps we receive + // caps we receive. if (p->_isSharedMemorySource && ( p->_padHandlerData.width == -1 || p->_padHandlerData.height == -1)) { GstCaps *caps = gst_sample_get_caps(sample); @@ -142,33 +146,21 @@ GstFlowReturn MediaImpl::gstNewSampleCallback(GstElement*, MediaImpl *p) // g_print("Size is %u x %u\n", _padHandlerData.width, _padHandlerData.height); } - GstMapInfo map; - GstBuffer *buffer = gst_sample_get_buffer(sample); + // Try to retrieve data bits of frame. + GstMapInfo& map = p->_mapInfo; + GstBuffer *buffer = gst_sample_get_buffer( sample ); if (gst_buffer_map(buffer, &map, GST_MAP_READ)) - { + { + p->_currentFrameBuffer = buffer; // For debugging: //gst_util_dump_mem(map.data, map.size) - // retrieve data from map info + + // Retrieve data from map info. p->_data = map.data; - // release memory previously mapped with gst_buffer_map - gst_buffer_unmap(buffer, &map); - - // queue previous frame to async queue - if (p->_frame != NULL) - { - p->getQueueOutputBuffer()->put(p->_frame); - } - // set current frame - p->_frame = sample; } - // keep only the last few output frames. - while (p->getQueueOutputBuffer()->size() > MAX_SAMPLES_IN_BUFFER_QUEUES) - { - sample = p->getQueueOutputBuffer()->get(); - // We free the memory for the previous frame here - gst_sample_unref(sample); - } + p->unlockMutex(); + return GST_FLOW_OK; } @@ -181,7 +173,8 @@ _queue0(NULL), _videoconvert0(NULL), //_audioSink(NULL), _appsink0(NULL), -_frame(NULL), +_currentFrameSample(NULL), +_currentFrameBuffer(NULL), _width(640), // unused _height(480), // unused _data(NULL), @@ -196,6 +189,7 @@ _uri(uri) { loadMovie(uri); } + _mutexLocker = new QMutexLocker(&_mutex); } void MediaImpl::unloadMovie() @@ -209,7 +203,6 @@ void MediaImpl::unloadMovie() // Un-ready. _setReady(false); - } void MediaImpl::freeResources() @@ -228,27 +221,16 @@ void MediaImpl::freeResources() _pipeline = NULL; } - qDebug() << "Freeing async queue" << endl; - // Clear remaining samples in queue. - while (getQueueOutputBuffer()->size() > 0) - { - GstSample *sample = getQueueOutputBuffer()->get(); - // We free the memory for the previous frame here - gst_sample_unref(sample); - } + qDebug() << "Freeing remaining samples/buffers" << endl; - // Also free last frame. - if (_frame) { - gst_sample_unref(_frame); - _frame = NULL; - } + _freeCurrentSample(); _uridecodebin0 = NULL; _queue0 = NULL; _videoconvert0 = NULL; //_audioSink = NULL; _appsink0 = NULL; - _frame = NULL; + _currentFrameSample = NULL; _padHandlerData = GstPadHandlerData(); // unref the shmsrc poller @@ -267,7 +249,7 @@ void MediaImpl::resetMovie() qDebug() << "Seeking at position 0."; gst_element_seek_simple (_pipeline, GST_FORMAT_TIME, (GstSeekFlags) (GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT), 0); - this->_frame = NULL; + this->_currentFrameSample = NULL; _setReady(true); } else @@ -308,7 +290,6 @@ bool MediaImpl::loadMovie(QString filename) _uri = filename; qDebug() << "Opening movie: " << filename << "."; - this->_frame = NULL; // Free previously allocated structures unloadMovie(); @@ -505,7 +486,7 @@ bool MediaImpl::runVideo() bool bitsChanged = false; // Check if we have some frames in the input buffer. - if (_queueInputBuffer.size() > 0) + if (_currentFrameSample != NULL) { // Pull video. if (_videoPull()) @@ -687,6 +668,22 @@ void MediaImpl::_setFinished(bool finished) // qDebug() << "Clip " << (finished ? "finished" : "not finished"); } +void MediaImpl::_freeCurrentSample() { + if (_currentFrameBuffer != NULL) + { + gst_buffer_unmap(_currentFrameBuffer, &_mapInfo); + } + + if (_currentFrameSample != NULL) + { + gst_sample_unref(_currentFrameSample); + } + + _currentFrameSample = NULL; + _currentFrameBuffer = NULL; + _data = NULL; +} + /** * FIXME: remove GOTO */ @@ -756,3 +753,14 @@ exit: gst_object_unref (sinkPad); } } + +void MediaImpl::lockMutex() +{ + _mutexLocker->relock(); +} + +void MediaImpl::unlockMutex() +{ + _mutexLocker->unlock(); +} + diff --git a/MediaImpl.h b/MediaImpl.h index aac42d9..93cb336 100644 --- a/MediaImpl.h +++ b/MediaImpl.h @@ -30,13 +30,14 @@ #include #include #include +#include +#include #if __APPLE__ #include #else #include #endif #include -#include "AsyncQueue.h" /** * Private declaration of the video player. @@ -145,6 +146,8 @@ private: void _setReady(bool ready); void _setFinished(bool finished); + void _freeCurrentSample(); + public: // GStreamer callbacks. @@ -170,13 +173,11 @@ public: // are made available by the source. static void gstPadAddedCallback(GstElement *src, GstPad *newPad, MediaImpl::GstPadHandlerData* data); - AsyncQueue *getQueueInputBuffer() { - return &this->_queueInputBuffer; - } + /// Locks mutex (default = no effect). + void lockMutex(); - AsyncQueue *getQueueOutputBuffer() { - return &this->_queueOutputBuffer; - } + /// Unlocks mutex (default = no effect). + void unlockMutex(); private: //locals @@ -198,10 +199,14 @@ private: GstElement *_videoconvert0; //GstElement *_audioSink; GstElement *_appsink0; + /** * Temporary contains the image data of the last frame. */ - GstSample *_frame; + GstSample *_currentFrameSample; + GstBuffer *_currentFrameBuffer; + GstMapInfo _mapInfo; + /** * shmsrc socket poller. */ @@ -233,16 +238,9 @@ private: bool _terminate; bool _movieReady; - /** - * Input buffer. - * Contains the raw data of last image. - */ - AsyncQueue _queueInputBuffer; - /** - * Output buffer. - * Contains the raw data of last image. - */ - AsyncQueue _queueOutputBuffer; + + QMutex _mutex; + QMutexLocker* _mutexLocker; private: /** diff --git a/Paint.cpp b/Paint.cpp index 6a221b2..bfa6807 100644 --- a/Paint.cpp +++ b/Paint.cpp @@ -105,6 +105,14 @@ void Media::rewind() impl_->resetMovie(); } +void Media::lockMutex() { + impl_->lockMutex(); +} + +void Media::unlockMutex() { + impl_->unlockMutex(); +} + const uchar* Media::_getBits() const { return this->impl_->getBits(); diff --git a/Paint.h b/Paint.h index d9aa419..4fe3b57 100644 --- a/Paint.h +++ b/Paint.h @@ -25,8 +25,8 @@ #include #include #include - #include +#include #if __APPLE__ #include @@ -76,6 +76,12 @@ public: /// Rewinds. virtual void rewind() {} + /// Locks mutex (default = no effect). + virtual void lockMutex() {} + + /// Unlocks mutex (default = no effect). + virtual void unlockMutex() {} + void setName(const QString& name) { _name = name; } QString getName() const { return _name; } uid getId() const { return _id; } @@ -134,6 +140,8 @@ public: GLuint getTextureId() const { return textureId; } virtual int getWidth() const = 0; virtual int getHeight() const = 0; + + /// Returns image bits data. Next call to bitsHaveChanged() will be false. virtual const uchar* getBits() const { bitsChanged = false; return _getBits(); @@ -215,6 +223,12 @@ public: /// Rewinds. virtual void rewind(); + /// Locks mutex (default = no effect). + virtual void lockMutex(); + + /// Unlocks mutex (default = no effect). + virtual void unlockMutex(); + virtual QString getType() const { return "media"; From 363b5795bab6537f795a3438f85fab9ee71ed6a0 Mon Sep 17 00:00:00 2001 From: Alexandre Quessy Date: Mon, 20 Oct 2014 13:54:06 -0400 Subject: [PATCH 19/22] Fix delete menu option --- MainWindow.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/MainWindow.cpp b/MainWindow.cpp index bd547e5..6bcd60a 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -562,24 +562,34 @@ void MainWindow::updateStatusBar() // formulaLabel->setText(spreadsheet->currentFormula()); } +/** + * Called when the user wants to delete an item. + * + * Deletes either a Paint or a Mapping. + */ void MainWindow::deleteItem() { + bool isMappingTabSelected = (mappingSplitter == contentTab->currentWidget()); + bool isPaintTabSelected = (paintSplitter == contentTab->currentWidget()); + if (currentSelectedItem) { - if (currentSelectedItem->listWidget() == mappingList) + if (isMappingTabSelected) //currentSelectedItem->listWidget() == mappingList) { // Delete mapping. deleteMapping( getItemId(*mappingList->currentItem()) ); //currentSelectedItem = NULL; } - else if (currentSelectedItem->listWidget() == paintList) + else if (isPaintTabSelected) //currentSelectedItem->listWidget() == paintList) { // Delete paint. deletePaint( getItemId(*paintList->currentItem()), false ); //currentSelectedItem = NULL; } else + { qCritical() << "Selected item neither a mapping nor a paint." << endl; + } } } From f9d51e6880b14cae6e7f519104a6c2123ab30da4 Mon Sep 17 00:00:00 2001 From: Tats Date: Mon, 20 Oct 2014 19:10:08 +0000 Subject: [PATCH 20/22] More cleanups and documentation in media related stuff. --- MediaImpl.cpp | 94 +++++++++++++++++++++------------------------------ MediaImpl.h | 30 ++++++++-------- Paint.cpp | 11 ++++-- Paint.h | 28 +++++++-------- 4 files changed, 76 insertions(+), 87 deletions(-) diff --git a/MediaImpl.cpp b/MediaImpl.cpp index 5cadd3c..a989ddf 100644 --- a/MediaImpl.cpp +++ b/MediaImpl.cpp @@ -52,13 +52,16 @@ int MediaImpl::getHeight() const return _padHandlerData.height; } -const uchar* MediaImpl::getBits() const +const uchar* MediaImpl::getBits() { + // Reset bits changed. + _bitsChanged = false; + + // Return data. if (_currentFrameSample == NULL) return NULL; - - - return _data; + else + return _data; } QString MediaImpl::getUri() const @@ -94,18 +97,6 @@ MediaImpl::~MediaImpl() delete _mutexLocker; } -bool MediaImpl::_videoPull() -{ - GstSample *sample = _currentFrameSample; - - if (sample == NULL) - { - // Either means we are not playing or we have reached EOS. - return false; - } - else return true; -} - bool MediaImpl::_eos() const { if (_movieReady) @@ -157,6 +148,9 @@ GstFlowReturn MediaImpl::gstNewSampleCallback(GstElement*, MediaImpl *p) // Retrieve data from map info. p->_data = map.data; + + // Bits have changed. + p->_bitsChanged = true; } p->unlockMutex(); @@ -175,6 +169,7 @@ _videoconvert0(NULL), _appsink0(NULL), _currentFrameSample(NULL), _currentFrameBuffer(NULL), +_bitsChanged(false), _width(640), // unused _height(480), // unused _data(NULL), @@ -221,24 +216,29 @@ void MediaImpl::freeResources() _pipeline = NULL; } - qDebug() << "Freeing remaining samples/buffers" << endl; - - _freeCurrentSample(); - + // Reset pipeline elements. _uridecodebin0 = NULL; _queue0 = NULL; _videoconvert0 = NULL; - //_audioSink = NULL; _appsink0 = NULL; - _currentFrameSample = NULL; + + // Reset pad handler. _padHandlerData = GstPadHandlerData(); - - // unref the shmsrc poller + + // Unref the shmsrc poller. if (_pollSource) { g_source_unref(_pollSource); _pollSource = NULL; } + + qDebug() << "Freeing remaining samples/buffers" << endl; + + // Frees current sample and buffer. + _freeCurrentSample(); + + // Resets bits changed. + _bitsChanged = false; } void MediaImpl::resetMovie() @@ -476,43 +476,27 @@ bool MediaImpl::loadMovie(QString filename) return true; } -bool MediaImpl::runVideo() +void MediaImpl::update() { - if (! _preRun()) + // Check for end-of-stream or terminate. + if (_eos() || _terminate) { - return false; + _setFinished(true); + resetMovie(); } - - bool bitsChanged = false; - - // Check if we have some frames in the input buffer. - if (_currentFrameSample != NULL) - { - // Pull video. - if (_videoPull()) - { - bitsChanged = true; - } - - //std::cout << "VideoImpl::runVideo: read frame #" << _videoNewBufferCounter << std::endl; - } - - /* TODO: This causes the texture to be loaded always in Mapper.cpp . The - * problem if this is not set is: When we have more than one shape, a - * shape that has a new buffer coming in will overdraw the old buffer of the - * shape on top. This implementation seems to be fast enough that - * _videoNewBufferCounter is often 1 or 0. If bitsChanged is often switching - * between true and false (as in the case described above), than the shape - * textures will appear to be flickering/alternating. Maybe a better solution is - * needed (in the GL layer or here?)*/ else { - bitsChanged = true; + _setFinished(false); } - _postRun(); - - return bitsChanged; +// // Check if movie is ready and connected. +// if (!isReady()) +// { +// _bitsChanged = false; +// } +// + // Check gstreamer messages on bus. + _checkMessages(); } bool MediaImpl::setPlayState(bool play) @@ -557,7 +541,7 @@ bool MediaImpl::_preRun() return true; } -void MediaImpl::_postRun() +void MediaImpl::_checkMessages() { // Parse message. if (_bus != NULL) diff --git a/MediaImpl.h b/MediaImpl.h index 93cb336..4f6f098 100644 --- a/MediaImpl.h +++ b/MediaImpl.h @@ -60,33 +60,42 @@ public: * Returns whether or not GStreamer video support is ok. */ static bool hasVideoSupport(); + /** * Sets up the player. * Basically calls loadMovie(). */ void build(); + /** * Returns the width of the video image. */ int getWidth() const; + /** * Returns the height of the video image. */ int getHeight() const; + /** * Returns the path to the media file being played. */ QString getUri() const; + /** * When using the shared memory source, returns whether or not we * are attached to a shared memory socket. */ bool getAttached(); + /** * Returns the raw image of the last video frame. * It is currently unused! */ - const uchar* getBits() const; + const uchar* getBits(); + + /// Returns true iff bits have changed since last call to getBits(). + bool bitsHaveChanged() const { return _bitsChanged; } /** * Checks if the pipeline is ready. @@ -94,14 +103,12 @@ public: * Returns whether or not the elements in the pipeline are connected, * and if we are using shmsrc, if the shared memory socket is being read. */ - bool isReady() const { return _padHandlerData.videoIsConnected; } + bool isReady() const { return _movieReady && _padHandlerData.videoIsConnected; } /** - * Tries to pull a video frame from the queue. - * - * Returns true if the image has changed. + * Performs regular updates (checks if movie is ready and checks messages). */ - bool runVideo(); + void update(); // void runAudio(); @@ -125,12 +132,6 @@ protected: void freeResources(); private: - /** - * Tries to pull a video frame from the asynchronous input buffer. - * - * Pushes the frame into the asynchronous output buffer. - */ - bool _videoPull(); /** * Checks if we reached the end of the video file. * @@ -142,7 +143,7 @@ private: // void _init(); bool _preRun(); - void _postRun(); + void _checkMessages(); void _setReady(bool ready); void _setFinished(bool finished); @@ -205,7 +206,8 @@ private: */ GstSample *_currentFrameSample; GstBuffer *_currentFrameBuffer; - GstMapInfo _mapInfo; + GstMapInfo _mapInfo; + bool _bitsChanged; /** * shmsrc socket poller. diff --git a/Paint.cpp b/Paint.cpp index bfa6807..74b6af2 100644 --- a/Paint.cpp +++ b/Paint.cpp @@ -86,8 +86,7 @@ int Media::getHeight() const } void Media::update() { - if (impl_->runVideo()) - bitsChanged = true; + impl_->update(); } void Media::play() @@ -113,11 +112,17 @@ void Media::unlockMutex() { impl_->unlockMutex(); } -const uchar* Media::_getBits() const +const uchar* Media::getBits() { return this->impl_->getBits(); } +bool Media::bitsHaveChanged() const +{ + return this->impl_->bitsHaveChanged(); +} + + bool Media::hasVideoSupport() { return MediaImpl::hasVideoSupport(); diff --git a/Paint.h b/Paint.h index 4fe3b57..3fb7ef3 100644 --- a/Paint.h +++ b/Paint.h @@ -124,8 +124,7 @@ protected: Paint(id), textureId(0), x(0), - y(0), - bitsChanged(true) + y(0) { glGenTextures(1, &textureId); } @@ -142,13 +141,10 @@ public: virtual int getHeight() const = 0; /// Returns image bits data. Next call to bitsHaveChanged() will be false. - virtual const uchar* getBits() const { - bitsChanged = false; - return _getBits(); - } + virtual const uchar* getBits() = 0; /// Returns true iff bits have changed since last call to getBits(). - bool bitsHaveChanged() const { return bitsChanged; } + virtual bool bitsHaveChanged() const = 0; virtual void setPosition(GLfloat xPos, GLfloat yPos) { x = xPos; @@ -156,9 +152,6 @@ public: } virtual GLfloat getX() const { return x; } virtual GLfloat getY() const { return y; } - -protected: - virtual const uchar* _getBits() const = 0; }; /** @@ -192,8 +185,12 @@ public: virtual int getWidth() const { return image.width(); } virtual int getHeight() const { return image.height(); } -protected: - virtual const uchar* _getBits() const { return image.bits(); } + virtual const uchar* getBits() { + bitsChanged = false; + return image.bits(); + } + + virtual bool bitsHaveChanged() const { return bitsChanged; } }; class MediaImpl; // forward declaration @@ -236,14 +233,15 @@ public: virtual int getWidth() const; virtual int getHeight() const; + virtual const uchar* getBits(); + + virtual bool bitsHaveChanged() const; + /** * Checks whether or not video is supported on this platform. */ static bool hasVideoSupport(); -protected: - virtual const uchar* _getBits() const; - private: /** * Private implementation, so that GStreamer headers don't need From af84795c78ff732e6354f1f2a6f8be7d00c6b050 Mon Sep 17 00:00:00 2001 From: Alexandre Quessy Date: Mon, 20 Oct 2014 17:13:37 -0400 Subject: [PATCH 21/22] Try to hide cursor in output window, when desired --- MainWindow.cpp | 9 +++++++++ MainWindow.h | 1 + OutputGLWindow.cpp | 34 +++++++++++++++++++++++++++------- OutputGLWindow.h | 6 ++++++ 4 files changed, 43 insertions(+), 7 deletions(-) diff --git a/MainWindow.cpp b/MainWindow.cpp index 6bcd60a..70e08ea 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -1180,6 +1180,14 @@ void MainWindow::createActions() // Output window should be displayed for full screen option to be available. connect(displayOutputWindow, SIGNAL(toggled(bool)), outputWindowFullScreen, SLOT(setEnabled(bool))); + + outputWindowHasCursor = new QAction(tr("O&utput window has cursor"), this); + outputWindowHasCursor->setStatusTip(tr("Show cursor in output window")); + outputWindowHasCursor->setIconVisibleInMenu(false); + outputWindowHasCursor->setCheckable(true); + outputWindowHasCursor->setChecked(true); + connect(outputWindowHasCursor, SIGNAL(toggled(bool)), outputWindow, SLOT(setFullScreen(bool))); + // Toggle display of canvas controls. displayCanvasControls = new QAction(tr("&Display canvas controls"), this); // displayCanvasControls->setShortcut(tr("Ctrl+E")); @@ -1254,6 +1262,7 @@ void MainWindow::createMenus() viewMenu->addAction(outputWindowFullScreen); viewMenu->addAction(displayCanvasControls); viewMenu->addAction(stickyVertices); + viewMenu->addAction(outputWindowHasCursor); // Run. runMenu = menuBar->addMenu(tr("&Run")); diff --git a/MainWindow.h b/MainWindow.h index 5df305d..33443cc 100644 --- a/MainWindow.h +++ b/MainWindow.h @@ -256,6 +256,7 @@ private: QAction *rewindAction; QAction *displayOutputWindow; + QAction *outputWindowHasCursor; QAction *outputWindowFullScreen; QAction *displayCanvasControls; QAction *stickyVertices; diff --git a/OutputGLWindow.cpp b/OutputGLWindow.cpp index e9ce620..f446628 100644 --- a/OutputGLWindow.cpp +++ b/OutputGLWindow.cpp @@ -36,6 +36,24 @@ OutputGLWindow::OutputGLWindow(MainWindow* mainWindow, QWidget* parent, const QG // Save window geometry. _geometry = saveGeometry(); + + _pointerIsVisible = true; +} + +void OutputGLWindow::setCursorVisible(bool visible) +{ + _pointerIsVisible = visible; + + if (_pointerIsVisible) + { + this->setCursor(Qt::BlankCursor); + _pointerIsVisible = false; + } + else + { + this->setCursor(Qt::ArrowCursor); + _pointerIsVisible = true; + } } void OutputGLWindow::closeEvent(QCloseEvent *event) @@ -47,14 +65,16 @@ void OutputGLWindow::closeEvent(QCloseEvent *event) void OutputGLWindow::keyPressEvent(QKeyEvent *event) { // Escape from full screen mode. - if (isFullScreen() && event->key() == Qt::Key_Escape) + if (event->key() == Qt::Key_Escape) { - setFullScreen(false); - emit fullScreenToggled(false); - } - else if (event->key() == Qt::Key_Escape) - { - // pass + if (isFullScreen()) + { + setFullScreen(false); + emit fullScreenToggled(false); + } else { + setFullScreen(true); + emit fullScreenToggled(true); + } } else { diff --git a/OutputGLWindow.h b/OutputGLWindow.h index 8c85572..afaa678 100644 --- a/OutputGLWindow.h +++ b/OutputGLWindow.h @@ -2,6 +2,7 @@ * OutputGLWindow.h * * (c) 2014 Sofian Audry -- info(@)sofianaudry(.)com + * (c) 2014 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 @@ -22,6 +23,7 @@ #include #include +#include #include "DestinationGLCanvas.h" // TODO: add SLOT for mySetVisible @@ -36,6 +38,7 @@ public: public slots: void setFullScreen(bool fullScreen); + void setCursorVisible(bool visible); protected: void closeEvent(QCloseEvent* event); @@ -47,10 +50,13 @@ signals: public: DestinationGLCanvas* getCanvas() const { return canvas; } + void setPointerHasMoved(); private: DestinationGLCanvas* canvas; QByteArray _geometry; + + bool _pointerIsVisible; }; #endif /* OutputGLWINDOW_H_ */ From afc84bb692985da1e4765852b9adc179acb69518 Mon Sep 17 00:00:00 2001 From: Alexandre Quessy Date: Tue, 21 Oct 2014 01:52:03 -0400 Subject: [PATCH 22/22] Fix logic in setCursorVisible + make it visible if controls are --- MainWindow.cpp | 14 +++++++------- MainWindow.h | 2 +- OutputGLWindow.cpp | 8 ++++---- OutputGLWindow.h | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/MainWindow.cpp b/MainWindow.cpp index 70e08ea..4b5cee8 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -1181,12 +1181,12 @@ void MainWindow::createActions() connect(displayOutputWindow, SIGNAL(toggled(bool)), outputWindowFullScreen, SLOT(setEnabled(bool))); - outputWindowHasCursor = new QAction(tr("O&utput window has cursor"), this); - outputWindowHasCursor->setStatusTip(tr("Show cursor in output window")); - outputWindowHasCursor->setIconVisibleInMenu(false); - outputWindowHasCursor->setCheckable(true); - outputWindowHasCursor->setChecked(true); - connect(outputWindowHasCursor, SIGNAL(toggled(bool)), outputWindow, SLOT(setFullScreen(bool))); + // outputWindowHasCursor = new QAction(tr("O&utput window has cursor"), this); + // outputWindowHasCursor->setStatusTip(tr("Show cursor in output window")); + // outputWindowHasCursor->setIconVisibleInMenu(false); + // outputWindowHasCursor->setCheckable(true); + // outputWindowHasCursor->setChecked(true); + // connect(outputWindowHasCursor, SIGNAL(toggled(bool)), outputWindow, SLOT(setFullScreen(bool))); // Toggle display of canvas controls. displayCanvasControls = new QAction(tr("&Display canvas controls"), this); @@ -1262,7 +1262,7 @@ void MainWindow::createMenus() viewMenu->addAction(outputWindowFullScreen); viewMenu->addAction(displayCanvasControls); viewMenu->addAction(stickyVertices); - viewMenu->addAction(outputWindowHasCursor); + //viewMenu->addAction(outputWindowHasCursor); // Run. runMenu = menuBar->addMenu(tr("&Run")); diff --git a/MainWindow.h b/MainWindow.h index 33443cc..54ac351 100644 --- a/MainWindow.h +++ b/MainWindow.h @@ -256,7 +256,7 @@ private: QAction *rewindAction; QAction *displayOutputWindow; - QAction *outputWindowHasCursor; + //QAction *outputWindowHasCursor; QAction *outputWindowFullScreen; QAction *displayCanvasControls; QAction *stickyVertices; diff --git a/OutputGLWindow.cpp b/OutputGLWindow.cpp index f446628..4a58770 100644 --- a/OutputGLWindow.cpp +++ b/OutputGLWindow.cpp @@ -46,13 +46,13 @@ void OutputGLWindow::setCursorVisible(bool visible) if (_pointerIsVisible) { - this->setCursor(Qt::BlankCursor); - _pointerIsVisible = false; + this->setCursor(Qt::ArrowCursor); + _pointerIsVisible = true; } else { - this->setCursor(Qt::ArrowCursor); - _pointerIsVisible = true; + this->setCursor(Qt::BlankCursor); + _pointerIsVisible = false; } } diff --git a/OutputGLWindow.h b/OutputGLWindow.h index afaa678..768df62 100644 --- a/OutputGLWindow.h +++ b/OutputGLWindow.h @@ -38,7 +38,6 @@ public: public slots: void setFullScreen(bool fullScreen); - void setCursorVisible(bool visible); protected: void closeEvent(QCloseEvent* event); @@ -51,6 +50,7 @@ signals: public: DestinationGLCanvas* getCanvas() const { return canvas; } void setPointerHasMoved(); + void setCursorVisible(bool visible); private: DestinationGLCanvas* canvas;