diff --git a/apps/QmlStreamer/main.cpp b/apps/QmlStreamer/main.cpp index b9b41c5..b74baa4 100644 --- a/apps/QmlStreamer/main.cpp +++ b/apps/QmlStreamer/main.cpp @@ -58,9 +58,11 @@ int main( int argc, char** argv ) try { - QScopedPointer< deflect::qt::QmlStreamer > streamer( + std::unique_ptr< deflect::qt::QmlStreamer > streamer( new deflect::qt::QmlStreamer( qmlFile, streamHost.toStdString(), streamName.toStdString( ))); + app.connect( streamer.get(), &deflect::qt::QmlStreamer::streamClosed, + &app, &QCoreApplication::quit ); return app.exec(); } catch( const std::runtime_error& exception ) diff --git a/deflect/Socket.cpp b/deflect/Socket.cpp index 498a121..f486803 100644 --- a/deflect/Socket.cpp +++ b/deflect/Socket.cpp @@ -136,7 +136,7 @@ bool Socket::receive( MessageHeader& messageHeader, QByteArray& message ) { QMutexLocker locker( &_socketMutex ); - if ( !_receiveHeader( messageHeader )) + if( !_receiveHeader( messageHeader )) return false; // get the message diff --git a/deflect/Stream.cpp b/deflect/Stream.cpp index 04af1d5..03b0404 100644 --- a/deflect/Stream.cpp +++ b/deflect/Stream.cpp @@ -56,8 +56,14 @@ namespace deflect Stream::Stream( const std::string& name, const std::string& address, const unsigned short port ) - : _impl( new StreamPrivate( this, name, address, port )) + : _impl( new StreamPrivate( name, address, port )) { + if( isConnected( )) + { + _impl->socket.connect( &_impl->socket, &Socket::disconnected, + [this]() { disconnected(); }); + _impl->sendOpen(); + } } Stream::~Stream() diff --git a/deflect/StreamPrivate.cpp b/deflect/StreamPrivate.cpp index b6b9f2f..45fcb3b 100644 --- a/deflect/StreamPrivate.cpp +++ b/deflect/StreamPrivate.cpp @@ -55,40 +55,39 @@ namespace deflect { -StreamPrivate::StreamPrivate( Stream* stream, const std::string &name_, +StreamPrivate::StreamPrivate( const std::string &name_, const std::string& address, const unsigned short port ) : name( name_ ) , socket( address, port ) , registeredForEvents( false ) - , _parent( stream ) - , _sendWorker( 0 ) { imageSegmenter.setNominalSegmentDimensions( SEGMENT_SIZE, SEGMENT_SIZE ); if( name.empty( )) throw std::runtime_error( "Invalid Stream name: " + name ); - - if( socket.isConnected( )) - { - connect( &socket, &Socket::disconnected, - this, &StreamPrivate::_onDisconnected ); - const MessageHeader mh( MESSAGE_TYPE_PIXELSTREAM_OPEN, 0, name ); - socket.send( mh, QByteArray( )); - } } StreamPrivate::~StreamPrivate() { - delete _sendWorker; + _sendWorker.reset(); if( !socket.isConnected( )) return; - const MessageHeader mh( MESSAGE_TYPE_QUIT, 0, name ); + sendClose(); +} + +void StreamPrivate::sendOpen() +{ + const MessageHeader mh( MESSAGE_TYPE_PIXELSTREAM_OPEN, 0, name ); socket.send( mh, QByteArray( )); +} - registeredForEvents = false; +void StreamPrivate::sendClose() +{ + const MessageHeader mh( MESSAGE_TYPE_QUIT, 0, name ); + socket.send( mh, QByteArray( )); } bool StreamPrivate::send( const ImageWrapper& image ) @@ -109,7 +108,7 @@ bool StreamPrivate::send( const ImageWrapper& image ) Stream::Future StreamPrivate::asyncSend( const ImageWrapper& image ) { if( !_sendWorker ) - _sendWorker = new StreamSendWorker( *this ); + _sendWorker.reset( new StreamSendWorker( *this )); return _sendWorker->enqueueImage( image ); } @@ -150,10 +149,4 @@ bool StreamPrivate::sendSizeHints( const SizeHints& hints ) return socket.send( mh, message ); } -void StreamPrivate::_onDisconnected() -{ - if( _parent ) - _parent->disconnected(); -} - } diff --git a/deflect/StreamPrivate.h b/deflect/StreamPrivate.h index e28d730..927f680 100644 --- a/deflect/StreamPrivate.h +++ b/deflect/StreamPrivate.h @@ -51,8 +51,7 @@ #include "Stream.h" // Stream::Future #include - -class QString; +#include namespace deflect { @@ -62,10 +61,8 @@ class StreamSendWorker; /** * Private implementation for the Stream class. */ -class StreamPrivate : public QObject +class StreamPrivate { - Q_OBJECT - public: /** * Create a new stream and open a new connection to the deflect::Server. @@ -74,17 +71,22 @@ class StreamPrivate : public QObject * e.g. "192.168.1.83" This method must be called by all Streams sharing a * common identifier before any of them starts sending images. * - * @param stream the parent object owning this object * @param name the unique stream name * @param address Address of the target Server instance. * @param port Port of the target Server instance. */ - StreamPrivate( Stream* stream, const std::string& name, - const std::string& address, const unsigned short port ); + StreamPrivate( const std::string& name, const std::string& address, + const unsigned short port ); /** Destructor, close the Stream. */ ~StreamPrivate(); + /** Send the open message to the server. */ + void sendOpen(); + + /** Send the quit message to the server. */ + void sendClose(); + /** * Close the stream. * @return true on success or if the Stream was not connected @@ -124,12 +126,8 @@ class StreamPrivate : public QObject /** Has a successful event registration reply been received */ bool registeredForEvents; -private slots: - void _onDisconnected(); - private: - Stream* _parent; - StreamSendWorker* _sendWorker; + std::unique_ptr< StreamSendWorker > _sendWorker; }; } diff --git a/deflect/qt/QmlStreamer.cpp b/deflect/qt/QmlStreamer.cpp index 7e5ca8f..fed18ed 100644 --- a/deflect/qt/QmlStreamer.cpp +++ b/deflect/qt/QmlStreamer.cpp @@ -50,6 +50,8 @@ QmlStreamer::QmlStreamer( const QString& qmlFile, const std::string& streamName ) : _impl( new Impl( qmlFile, streamHost, streamName )) { + connect( _impl.get(), &Impl::streamClosed, + this, &QmlStreamer::streamClosed ); } QmlStreamer::~QmlStreamer() @@ -66,6 +68,5 @@ QQmlEngine* QmlStreamer::getQmlEngine() return _impl->getQmlEngine(); } - } } diff --git a/deflect/qt/QmlStreamer.h b/deflect/qt/QmlStreamer.h index 52c26c7..24f03ee 100644 --- a/deflect/qt/QmlStreamer.h +++ b/deflect/qt/QmlStreamer.h @@ -60,8 +60,10 @@ namespace qt * on each update on the given Deflect stream. It automatically register also * for Deflect events, which can be directly handled in the QML. */ -class QmlStreamer +class QmlStreamer : public QObject { + Q_OBJECT + public: /** * Construct a new qml streamer by loading the QML, accessible by @@ -85,6 +87,10 @@ class QmlStreamer /** @return the QML engine. */ DEFLECTQT_API QQmlEngine* getQmlEngine(); +signals: + /** Emitted when the stream has been closed. */ + void streamClosed(); + private: QmlStreamer( const QmlStreamer& ) = delete; QmlStreamer operator=( const QmlStreamer& ) = delete; diff --git a/deflect/qt/QmlStreamerImpl.cpp b/deflect/qt/QmlStreamerImpl.cpp index 076150d..980dd24 100644 --- a/deflect/qt/QmlStreamerImpl.cpp +++ b/deflect/qt/QmlStreamerImpl.cpp @@ -202,6 +202,9 @@ void QmlStreamer::Impl::_render() qWarning() << "Could not setup Deflect stream"; } + if( !_streaming ) + return; + // Polish, synchronize and render the next frame (into our fbo). In this // example everything happens on the same thread and therefore all three // steps are performed in succession from here. In a threaded setup the @@ -215,12 +218,6 @@ void QmlStreamer::Impl::_render() _context->functions()->glFlush(); - if( !_streaming ) - { - QCoreApplication::quit(); - return; - } - const QImage image = _fbo->toImage(); if( image.isNull( )) { @@ -233,6 +230,9 @@ void QmlStreamer::Impl::_render() imageWrapper.compressionPolicy = COMPRESSION_ON; imageWrapper.compressionQuality = 100; _streaming = _stream->send( imageWrapper ) && _stream->finishFrame(); + + if( !_streaming ) + emit streamClosed(); } void QmlStreamer::Impl::_requestUpdate() @@ -345,6 +345,9 @@ bool QmlStreamer::Impl::_setupDeflectStream() if( !_stream->isConnected( )) return false; + _stream->disconnected.connect( + boost::bind( &QmlStreamer::Impl::streamClosed, this )); + if( !_stream->registerForEvents( )) return false; diff --git a/deflect/qt/QmlStreamerImpl.h b/deflect/qt/QmlStreamerImpl.h index 217ccaf..8cd737f 100644 --- a/deflect/qt/QmlStreamerImpl.h +++ b/deflect/qt/QmlStreamerImpl.h @@ -97,6 +97,9 @@ private slots: void _onResized( double, double ); void _onWheeled( double, double, double ); +signals: + void streamClosed(); + private: std::string _getDeflectStreamName() const; bool _setupDeflectStream(); diff --git a/doc/Changelog.md b/doc/Changelog.md index f7f2e75..6add934 100644 --- a/doc/Changelog.md +++ b/doc/Changelog.md @@ -4,6 +4,8 @@ Changelog {#Changelog} ## Deflect 0.11 ### 0.11.0 (git master) +* [100](https://github.com/BlueBrain/Deflect/pull/100): + QmlStreamer: correcly quit application when stream is closed. * [95](https://github.com/BlueBrain/Deflect/pull/95): DesktopStreamer: the list of windows available for streaming is also updated after a window has been hidden or unhidden.