diff --git a/apps/DesktopStreamer/MainWindow.cpp b/apps/DesktopStreamer/MainWindow.cpp
index d40c5d0..9da1cc4 100644
--- a/apps/DesktopStreamer/MainWindow.cpp
+++ b/apps/DesktopStreamer/MainWindow.cpp
@@ -80,7 +80,7 @@ MainWindow::MainWindow()
{
setupUi( this );
- connect( _hostnameComboBox, &QComboBox::currentTextChanged,
+ connect( _hostComboBox, &QComboBox::currentTextChanged,
[&]( const QString& text )
{
_streamButton->setEnabled( !text.isEmpty( ));
@@ -88,14 +88,14 @@ MainWindow::MainWindow()
});
for( const auto& entry : defaultHosts )
- _hostnameComboBox->addItem( entry.first, entry.second );
+ _hostComboBox->addItem( entry.first, entry.second );
// no default host selected initially
- _hostnameComboBox->setCurrentIndex( -1 );
+ _hostComboBox->setCurrentIndex( -1 );
char hostname[256] = { 0 };
gethostname( hostname, 256 );
- _streamnameLineEdit->setText( QString( "%1" ).arg( hostname ));
+ _streamIdLineEdit->setText( QString( "%1" ).arg( hostname ));
#ifdef DEFLECT_USE_QT5MACEXTRAS
_listView->setModel( new DesktopWindowsModel );
@@ -195,13 +195,13 @@ void MainWindow::_updateStreams()
continue;
}
- const std::string name = index.isValid() ?
+ const std::string appName = index.isValid() ?
_listView->model()->data( index, Qt::DisplayRole ).
toString().toStdString() : std::string();
- const std::string streamName = std::to_string( ++_streamID ) +
- " " + name + " - " +
- _streamnameLineEdit->text().toStdString();
- StreamPtr stream( new Stream( *this, index, streamName, host ));
+ const std::string streamId = std::to_string( ++_streamID ) +
+ " " + appName + " - " +
+ _streamIdLineEdit->text().toStdString();
+ StreamPtr stream( new Stream( *this, index, streamId, host ));
if( !stream->isConnected( ))
{
@@ -235,7 +235,7 @@ void MainWindow::_updateStreams()
{
const QPersistentModelIndex index; // default == use desktop
StreamPtr stream( new Stream( *this, index,
- _streamnameLineEdit->text().toStdString(),
+ _streamIdLineEdit->text().toStdString(),
host ));
if( stream->isConnected( ))
{
@@ -332,10 +332,10 @@ void MainWindow::_regulateFrameRate()
std::string MainWindow::_getStreamHost() const
{
QString streamHost;
- if( _hostnameComboBox->findText(_hostnameComboBox->currentText( )) == -1 )
- streamHost = _hostnameComboBox->currentText();
+ if( _hostComboBox->findText(_hostComboBox->currentText( )) == -1 )
+ streamHost = _hostComboBox->currentText();
else
- streamHost = _hostnameComboBox->currentData().toString();
+ streamHost = _hostComboBox->currentData().toString();
return streamHost.toStdString();
}
diff --git a/apps/DesktopStreamer/MainWindow.ui b/apps/DesktopStreamer/MainWindow.ui
index ba4340a..e074c40 100644
--- a/apps/DesktopStreamer/MainWindow.ui
+++ b/apps/DesktopStreamer/MainWindow.ui
@@ -58,7 +58,7 @@
-
-
+
0
@@ -173,7 +173,7 @@
-
-
+
0
@@ -261,7 +261,7 @@
_streamButton
toggled(bool)
- _streamnameLineEdit
+ _streamIdLineEdit
setDisabled(bool)
@@ -277,7 +277,7 @@
_streamButton
toggled(bool)
- _hostnameComboBox
+ _hostComboBox
setDisabled(bool)
diff --git a/apps/DesktopStreamer/Stream.cpp b/apps/DesktopStreamer/Stream.cpp
index 532d346..173b663 100644
--- a/apps/DesktopStreamer/Stream.cpp
+++ b/apps/DesktopStreamer/Stream.cpp
@@ -57,8 +57,8 @@
#define CURSOR_IMAGE_SIZE 20
Stream::Stream( const MainWindow& parent, const QPersistentModelIndex window,
- const std::string& name, const std::string& host )
- : deflect::Stream( name, host )
+ const std::string& id, const std::string& host )
+ : deflect::Stream( id, host )
, _parent( parent )
, _window( window )
, _cursor( QImage( CURSOR_IMAGE_FILE ).scaled(
diff --git a/apps/DesktopStreamer/Stream.h b/apps/DesktopStreamer/Stream.h
index 5fb3543..8ecc094 100644
--- a/apps/DesktopStreamer/Stream.h
+++ b/apps/DesktopStreamer/Stream.h
@@ -52,7 +52,7 @@ class Stream : public deflect::Stream
public:
/** Construct a new stream for the given desktop window. */
Stream( const MainWindow& parent, const QPersistentModelIndex window,
- const std::string& name, const std::string& host );
+ const std::string& id, const std::string& host );
~Stream();
/**
diff --git a/apps/QmlStreamer/main.cpp b/apps/QmlStreamer/main.cpp
index b74baa4..7f09976 100644
--- a/apps/QmlStreamer/main.cpp
+++ b/apps/QmlStreamer/main.cpp
@@ -38,9 +38,9 @@ int main( int argc, char** argv )
"qrc:/qml/gui.qml" );
parser.addOption( qmlFileOption );
- QCommandLineOption streamHostOption( "host", "Stream target hostname "
+ QCommandLineOption streamHostOption( "host", "Stream target host "
"(default: localhost)",
- "hostname", "localhost" );
+ "host", "localhost" );
parser.addOption( streamHostOption );
// note: the 'name' command line option is already taken by QCoreApplication
diff --git a/apps/SimpleStreamer/main.cpp b/apps/SimpleStreamer/main.cpp
index 9505047..323d579 100644
--- a/apps/SimpleStreamer/main.cpp
+++ b/apps/SimpleStreamer/main.cpp
@@ -57,8 +57,8 @@
bool deflectInteraction = false;
bool deflectCompressImage = true;
unsigned int deflectCompressionQuality = 75;
-char* deflectHostname = NULL;
-std::string deflectStreamName = "SimpleStreamer";
+char* deflectHost = NULL;
+std::string deflectStreamId = "SimpleStreamer";
deflect::Stream* deflectStream = NULL;
void syntax( char* app );
@@ -78,7 +78,7 @@ int main( int argc, char** argv )
{
readCommandLineArguments( argc, argv );
- if( deflectHostname == NULL )
+ if( deflectHost == NULL )
syntax( argv[0] );
initGLWindow( argc, argv );
@@ -101,7 +101,7 @@ void readCommandLineArguments( int argc, char** argv )
case 'n':
if( i + 1 < argc )
{
- deflectStreamName = argv[i+1];
+ deflectStreamId = argv[i+1];
++i;
}
break;
@@ -116,7 +116,7 @@ void readCommandLineArguments( int argc, char** argv )
}
}
else if( i == argc - 1 )
- deflectHostname = argv[i];
+ deflectHost = argv[i];
}
}
@@ -147,7 +147,7 @@ void initGLWindow( int argc, char** argv )
void initDeflectStream()
{
- deflectStream = new deflect::Stream( deflectStreamName, deflectHostname );
+ deflectStream = new deflect::Stream( deflectStreamId, deflectHost );
if( !deflectStream->isConnected( ))
{
std::cerr << "Could not connect to host!" << std::endl;
@@ -166,11 +166,11 @@ void initDeflectStream()
void syntax( char* app )
{
- std::cerr << "syntax: " << app << " [options] " << std::endl;
+ std::cerr << "syntax: " << app << " [options] " << std::endl;
std::cerr << "options:" << std::endl;
- std::cerr << " -n set stream name (default SimpleStreamer)" << std::endl;
- std::cerr << " -i enable interaction events (default disabled)" << std::endl;
- std::cerr << " -u enable uncompressed streaming (default disabled)" << std::endl;
+ std::cerr << " -n set stream identifier (default: 'SimpleStreamer')" << std::endl;
+ std::cerr << " -i enable interaction events (default: OFF)" << std::endl;
+ std::cerr << " -u enable uncompressed streaming (default: OFF)" << std::endl;
exit( 1 );
}
diff --git a/deflect/ServerWorker.cpp b/deflect/ServerWorker.cpp
index 3b0f513..5041f06 100644
--- a/deflect/ServerWorker.cpp
+++ b/deflect/ServerWorker.cpp
@@ -81,8 +81,8 @@ ServerWorker::~ServerWorker()
// We still want to remove this source so that the stream does not get stuck
// if other senders are still active / resp. the window gets closed if no
// more senders contribute to it.
- if( !_streamUri.isEmpty( ))
- emit removeStreamSource( _streamUri, _sourceId );
+ if( !_streamId.isEmpty( ))
+ emit removeStreamSource( _streamId, _sourceId );
if( _tcpSocket->state() == QAbstractSocket::ConnectedState )
_sendQuit();
@@ -103,7 +103,7 @@ void ServerWorker::initConnection()
void ServerWorker::closeConnection( const QString uri )
{
- if( uri != _streamUri )
+ if( uri != _streamId )
return;
Event closeEvent;
@@ -116,7 +116,7 @@ void ServerWorker::closeConnection( const QString uri )
void ServerWorker::replyToEventRegistration( const QString uri,
const bool success )
{
- if( uri != _streamUri )
+ if( uri != _streamId )
return;
_registeredToEvents = success;
@@ -196,39 +196,38 @@ void ServerWorker::_handleMessage( const MessageHeader& messageHeader,
const QString uri( messageHeader.uri );
if( uri.isEmpty( ))
{
- std::cerr << "Warning: rejecting streamer with empty uri"
- << std::endl;
- closeConnection( _streamUri );
+ std::cerr << "Warning: rejecting streamer with empty id" << std::endl;
+ closeConnection( _streamId );
return;
}
- if( uri != _streamUri &&
+ if( uri != _streamId &&
messageHeader.type != MESSAGE_TYPE_PIXELSTREAM_OPEN )
{
- std::cerr << "Warning: ingnoring message with incorrect stream uri: '"
+ std::cerr << "Warning: ingnoring message with incorrect stream id: '"
<< messageHeader.uri << "', expected: '"
- << _streamUri.toStdString() << "'" << std::endl;
+ << _streamId.toStdString() << "'" << std::endl;
return;
}
switch( messageHeader.type )
{
case MESSAGE_TYPE_QUIT:
- emit removeStreamSource( _streamUri, _sourceId );
- _streamUri = QString();
+ emit removeStreamSource( _streamId, _sourceId );
+ _streamId = QString();
break;
case MESSAGE_TYPE_PIXELSTREAM_OPEN:
- if( !_streamUri.isEmpty( ))
+ if( !_streamId.isEmpty( ))
{
std::cerr << "Warning: PixelStream already opened!" << std::endl;
return;
}
- _streamUri = uri;
- emit addStreamSource( _streamUri, _sourceId );
+ _streamId = uri;
+ emit addStreamSource( _streamId, _sourceId );
break;
case MESSAGE_TYPE_PIXELSTREAM_FINISH_FRAME:
- emit receivedFrameFinished( _streamUri, _sourceId );
+ emit receivedFrameFinished( _streamId, _sourceId );
break;
case MESSAGE_TYPE_PIXELSTREAM:
@@ -239,7 +238,7 @@ void ServerWorker::_handleMessage( const MessageHeader& messageHeader,
{
const SizeHints* hints =
reinterpret_cast< const SizeHints* >( byteArray.data( ));
- emit receivedSizeHints( _streamUri, SizeHints( *hints ));
+ emit receivedSizeHints( _streamId, SizeHints( *hints ));
break;
}
@@ -251,7 +250,7 @@ void ServerWorker::_handleMessage( const MessageHeader& messageHeader,
{
const bool exclusive =
(messageHeader.type == MESSAGE_TYPE_BIND_EVENTS_EX);
- emit registerToEvents( _streamUri, exclusive, this );
+ emit registerToEvents( _streamId, exclusive, this );
}
break;
@@ -272,7 +271,7 @@ void ServerWorker::_handlePixelStreamMessage( const QByteArray& byteArray )
byteArray.right( byteArray.size() - sizeof( SegmentParameters ));
segment.imageData = imageData;
- emit( receivedSegment( _streamUri, _sourceId, segment ));
+ emit( receivedSegment( _streamId, _sourceId, segment ));
}
void ServerWorker::_sendProtocolVersion()
diff --git a/deflect/ServerWorker.h b/deflect/ServerWorker.h
index 1a2db81..75a04a9 100644
--- a/deflect/ServerWorker.h
+++ b/deflect/ServerWorker.h
@@ -92,7 +92,7 @@ private slots:
private:
QTcpSocket* _tcpSocket;
- QString _streamUri;
+ QString _streamId;
int _sourceId;
bool _registeredToEvents;
diff --git a/deflect/Socket.cpp b/deflect/Socket.cpp
index d967dc4..2ea6d75 100644
--- a/deflect/Socket.cpp
+++ b/deflect/Socket.cpp
@@ -56,8 +56,9 @@ namespace deflect
const unsigned short Socket::defaultPortNumber = DEFAULT_PORT_NUMBER;
-Socket::Socket( const std::string& hostname, const unsigned short port )
- : _socket( new QTcpSocket( ))
+Socket::Socket( const std::string& host, const unsigned short port )
+ : _host( host )
+ , _socket( new QTcpSocket( ))
, _remoteProtocolVersion( INVALID_NETWORK_PROTOCOL_VERSION )
{
// disable warnings which occur if no QCoreApplication is present during
@@ -69,7 +70,7 @@ Socket::Socket( const std::string& hostname, const unsigned short port )
log->setEnabled( QtWarningMsg, false );
}
- _connect( hostname, port );
+ _connect( host, port );
QObject::connect( _socket, &QTcpSocket::disconnected,
this, &Socket::disconnected );
@@ -80,6 +81,11 @@ Socket::~Socket()
delete _socket;
}
+const std::string& Socket::getHost() const
+{
+ return _host;
+}
+
bool Socket::isConnected() const
{
return _socket->state() == QTcpSocket::ConnectedState;
@@ -185,17 +191,17 @@ bool Socket::_receiveHeader( MessageHeader& messageHeader )
return stream.status() == QDataStream::Ok;
}
-bool Socket::_connect( const std::string& hostname, const unsigned short port )
+bool Socket::_connect( const std::string& host, const unsigned short port )
{
// make sure we're disconnected
_socket->disconnectFromHost();
// open connection
- _socket->connectToHost( hostname.c_str(), port );
+ _socket->connectToHost( host.c_str(), port );
if( !_socket->waitForConnected( RECEIVE_TIMEOUT_MS ))
{
- std::cerr << "could not connect to host " << hostname << ":" << port
+ std::cerr << "could not connect to host " << host << ":" << port
<< std::endl;
return false;
}
@@ -204,7 +210,7 @@ bool Socket::_connect( const std::string& hostname, const unsigned short port )
if( _checkProtocolVersion( ))
return true;
- std::cerr << "Protocol version check failed for host: " << hostname << ":"
+ std::cerr << "Protocol version check failed for host: " << host << ":"
<< port << std::endl;
_socket->disconnectFromHost();
return false;
diff --git a/deflect/Socket.h b/deflect/Socket.h
index 448b53b..228f7b9 100644
--- a/deflect/Socket.h
+++ b/deflect/Socket.h
@@ -70,15 +70,18 @@ class Socket : public QObject
/**
* Construct a Socket and connect to host.
- * @param hostname The target host (IP address or hostname)
+ * @param host The target host (IP address or hostname)
* @param port The target port
*/
- DEFLECT_API Socket( const std::string& hostname,
+ DEFLECT_API Socket( const std::string& host,
unsigned short port = defaultPortNumber );
/** Destruct a Socket, disconnecting from host. */
DEFLECT_API ~Socket();
+ /** Get the host passed to the constructor. */
+ const std::string& getHost() const;
+
/** Is the Socket connected */
DEFLECT_API bool isConnected() const;
@@ -118,11 +121,12 @@ class Socket : public QObject
void disconnected();
private:
+ const std::string _host;
QTcpSocket* _socket;
int32_t _remoteProtocolVersion;
mutable QMutex _socketMutex;
- bool _connect( const std::string &hostname, const unsigned short port );
+ bool _connect( const std::string &host, const unsigned short port );
bool _checkProtocolVersion();
bool _receiveHeader( MessageHeader& messageHeader );
diff --git a/deflect/Stream.cpp b/deflect/Stream.cpp
index 03b0404..9496c6d 100644
--- a/deflect/Stream.cpp
+++ b/deflect/Stream.cpp
@@ -54,9 +54,20 @@
namespace deflect
{
-Stream::Stream( const std::string& name, const std::string& address,
+Stream::Stream()
+ : _impl( new StreamPrivate( "", "", Socket::defaultPortNumber ))
+{
+ if( isConnected( ))
+ {
+ _impl->socket.connect( &_impl->socket, &Socket::disconnected,
+ [this]() { disconnected(); });
+ _impl->sendOpen();
+ }
+}
+
+Stream::Stream( const std::string& id, const std::string& host,
const unsigned short port )
- : _impl( new StreamPrivate( name, address, port ))
+ : _impl( new StreamPrivate( id, host, port ))
{
if( isConnected( ))
{
@@ -75,6 +86,16 @@ bool Stream::isConnected() const
return _impl->socket.isConnected();
}
+const std::string& Stream::getId() const
+{
+ return _impl->id;
+}
+
+const std::string& Stream::getHost() const
+{
+ return _impl->socket.getHost();
+}
+
bool Stream::send( const ImageWrapper& image )
{
return _impl->send( image );
@@ -104,7 +125,7 @@ bool Stream::registerForEvents( const bool exclusive )
const MessageType type = exclusive ? MESSAGE_TYPE_BIND_EVENTS_EX :
MESSAGE_TYPE_BIND_EVENTS;
- MessageHeader mh( type, 0, _impl->name );
+ MessageHeader mh( type, 0, _impl->id );
// Send the bind message
if( !_impl->socket.send( mh, QByteArray( )))
diff --git a/deflect/Stream.h b/deflect/Stream.h
index 85574d3..a04c5c8 100644
--- a/deflect/Stream.h
+++ b/deflect/Stream.h
@@ -79,6 +79,17 @@ class StreamPrivate;
class Stream
{
public:
+ /**
+ * Open a new connection to the Server using environment variables.
+ *
+ * DEFLECT_HOST The address of the target Server instance (required).
+ * DEFLECT_ID The identifier for the stream. If not provided, a random
+ * unique identifier will be used.
+ * @throw std::runtime_error if DEFLECT_HOST was not provided.
+ * @version 1.3
+ */
+ DEFLECT_API Stream();
+
/**
* Open a new connection to the Server.
*
@@ -86,18 +97,22 @@ class Stream
* isConnected().
*
* Different Streams can contribute to a single window by using the same
- * name as identifier. All the Streams which contribute to the same window
- * should be created before any of them starts sending images.
+ * identifier. All the Streams which contribute to the same window should be
+ * created before any of them starts sending images.
*
- * @param name An identifier for the stream which cannot be empty.
- * @param address Address of the target Server instance, can be a
- * hostname like "localhost" or an IP in string format like
- * "192.168.1.83".
+ * @param id The identifier for the stream. If left empty, the environment
+ * variable DEFLECT_ID will be used. If both values are empty,
+ * a random unique identifier will be used.
+ * @param host The address of the target Server instance. It can be a
+ * hostname like "localhost" or an IP in string format like
+ * "192.168.1.83". If left empty, the environment variable
+ * DEFLECT_HOST will be used instead.
* @param port Port of the Server instance, default 1701.
+ * @throw std::runtime_error if no host was provided.
* @version 1.0
*/
- DEFLECT_API Stream( const std::string& name, const std::string& address,
- const unsigned short port = 1701 );
+ DEFLECT_API Stream( const std::string& id, const std::string& host,
+ unsigned short port = 1701 );
/** Destruct the Stream, closing the connection. @version 1.0 */
DEFLECT_API virtual ~Stream();
@@ -105,6 +120,12 @@ class Stream
/** @return true if the stream is connected, false otherwise. @version 1.0*/
DEFLECT_API bool isConnected() const;
+ /** @return the identifier defined by the constructor. @version 1.3 */
+ DEFLECT_API const std::string& getId() const;
+
+ /** @return the host defined by the constructor. @version 1.3 */
+ DEFLECT_API const std::string& getHost() const;
+
/** Emitted after the stream was disconnected. @version 1.0 */
boost::signals2::signal< void() > disconnected;
@@ -147,8 +168,8 @@ class Stream
*
* This method must be called everytime this Stream instance has finished
* sending its image(s) for the current frame. The receiver will display
- * the images once all the senders which use the same name have finished a
- * frame.
+ * the images once all the senders which use the same identifier have
+ * finished a frame.
*
* @note A call to finishFrame() while an asyncSend() is pending is
* undefined.
@@ -172,7 +193,7 @@ class Stream
* This method is synchronous and waits for a registration reply from the
* Server before returning.
*
- * @param exclusive Binds only one stream source for the same name
+ * @param exclusive Binds only one stream source for the same identifier.
* @return true if the registration could be or was already established.
* @version 1.0
*/
diff --git a/deflect/StreamPrivate.cpp b/deflect/StreamPrivate.cpp
index 45fcb3b..58c5bbc 100644
--- a/deflect/StreamPrivate.cpp
+++ b/deflect/StreamPrivate.cpp
@@ -50,22 +50,52 @@
#include
#include
-#define SEGMENT_SIZE 512
+
+#include
+
+namespace
+{
+const unsigned int SEGMENT_SIZE = 512;
+const char* STREAM_ID_ENV_VAR = "DEFLECT_ID";
+const char* STREAM_HOST_ENV_VAR = "DEFLECT_HOST";
+}
+
+std::string _getStreamHost( const std::string& host )
+{
+ if( !host.empty( ))
+ return host;
+
+ const QString streamHost = qgetenv( STREAM_HOST_ENV_VAR ).constData();
+ if( !streamHost.isEmpty( ))
+ return streamHost.toStdString();
+
+ throw std::runtime_error( "No host provided" );
+}
+
+std::string _getStreamId( const std::string& id )
+{
+ if( !id.empty( ))
+ return id;
+
+ const QString streamId = qgetenv( STREAM_ID_ENV_VAR ).constData();
+ if( !streamId.isEmpty( ))
+ return streamId.toStdString();
+
+ return QString( "%1_%2" ).arg( QHostInfo::localHostName(),
+ QString::number( rand(), 16 )).toStdString();
+}
namespace deflect
{
-StreamPrivate::StreamPrivate( const std::string &name_,
- const std::string& address,
+StreamPrivate::StreamPrivate( const std::string& id_,
+ const std::string& host,
const unsigned short port )
- : name( name_ )
- , socket( address, port )
+ : id( _getStreamId( id_ ))
+ , socket( _getStreamHost( host ), port )
, registeredForEvents( false )
{
imageSegmenter.setNominalSegmentDimensions( SEGMENT_SIZE, SEGMENT_SIZE );
-
- if( name.empty( ))
- throw std::runtime_error( "Invalid Stream name: " + name );
}
StreamPrivate::~StreamPrivate()
@@ -80,13 +110,13 @@ StreamPrivate::~StreamPrivate()
void StreamPrivate::sendOpen()
{
- const MessageHeader mh( MESSAGE_TYPE_PIXELSTREAM_OPEN, 0, name );
+ const MessageHeader mh( MESSAGE_TYPE_PIXELSTREAM_OPEN, 0, id );
socket.send( mh, QByteArray( ));
}
void StreamPrivate::sendClose()
{
- const MessageHeader mh( MESSAGE_TYPE_QUIT, 0, name );
+ const MessageHeader mh( MESSAGE_TYPE_QUIT, 0, id );
socket.send( mh, QByteArray( ));
}
@@ -116,7 +146,7 @@ Stream::Future StreamPrivate::asyncSend( const ImageWrapper& image )
bool StreamPrivate::finishFrame()
{
// Open a window for the PixelStream
- const MessageHeader mh( MESSAGE_TYPE_PIXELSTREAM_FINISH_FRAME, 0, name );
+ const MessageHeader mh( MESSAGE_TYPE_PIXELSTREAM_FINISH_FRAME, 0, id );
return socket.send( mh, QByteArray( ));
}
@@ -125,7 +155,7 @@ bool StreamPrivate::sendPixelStreamSegment( const Segment& segment )
// Create message header
const uint32_t segmentSize( sizeof( SegmentParameters ) +
segment.imageData.size( ));
- const MessageHeader mh( MESSAGE_TYPE_PIXELSTREAM, segmentSize, name );
+ const MessageHeader mh( MESSAGE_TYPE_PIXELSTREAM, segmentSize, id );
// This byte array will hold the message to be sent over the socket
QByteArray message;
@@ -142,7 +172,7 @@ bool StreamPrivate::sendPixelStreamSegment( const Segment& segment )
bool StreamPrivate::sendSizeHints( const SizeHints& hints )
{
- const MessageHeader mh( MESSAGE_TYPE_SIZE_HINTS, sizeof( hints ), name );
+ const MessageHeader mh( MESSAGE_TYPE_SIZE_HINTS, sizeof( hints ), id );
QByteArray message;
message.append( (const char*)( &hints ), sizeof( hints ) );
diff --git a/deflect/StreamPrivate.h b/deflect/StreamPrivate.h
index 927f680..da776cc 100644
--- a/deflect/StreamPrivate.h
+++ b/deflect/StreamPrivate.h
@@ -67,16 +67,12 @@ class StreamPrivate
/**
* Create a new stream and open a new connection to the deflect::Server.
*
- * It can be a hostname like "localhost" or an IP in string format,
- * 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 name the unique stream name
- * @param address Address of the target Server instance.
+ * @param id the unique stream identifier
+ * @param host Address of the target Server instance.
* @param port Port of the target Server instance.
*/
- StreamPrivate( const std::string& name, const std::string& address,
- const unsigned short port );
+ StreamPrivate( const std::string& id, const std::string& host,
+ unsigned short port );
/** Destructor, close the Stream. */
~StreamPrivate();
@@ -103,10 +99,8 @@ class StreamPrivate
bool finishFrame();
/**
- * Send an existing PixelStreamSegment via the Socket.
- * @param socket The Socket instance
- * @param segment A pixel stream segement with valid parameters and imageData
- * @param senderName Used to identifiy the sender on the receiver side
+ * Send a Segment through the Stream.
+ * @param segment An image segment with valid parameters and data
* @return true if the message could be sent
*/
DEFLECT_API bool sendPixelStreamSegment( const Segment& segment );
@@ -115,7 +109,7 @@ class StreamPrivate
bool sendSizeHints( const SizeHints& hints );
/** The stream identifier. */
- const std::string name;
+ const std::string id;
/** The communication socket instance */
Socket socket;
diff --git a/deflect/qt/QmlStreamer.cpp b/deflect/qt/QmlStreamer.cpp
index fed18ed..4bc58ea 100644
--- a/deflect/qt/QmlStreamer.cpp
+++ b/deflect/qt/QmlStreamer.cpp
@@ -47,8 +47,8 @@ namespace qt
QmlStreamer::QmlStreamer( const QString& qmlFile,
const std::string& streamHost,
- const std::string& streamName )
- : _impl( new Impl( qmlFile, streamHost, streamName ))
+ const std::string& streamId )
+ : _impl( new Impl( qmlFile, streamHost, streamId ))
{
connect( _impl.get(), &Impl::streamClosed,
this, &QmlStreamer::streamClosed );
diff --git a/deflect/qt/QmlStreamer.h b/deflect/qt/QmlStreamer.h
index 24f03ee..0fb32fc 100644
--- a/deflect/qt/QmlStreamer.h
+++ b/deflect/qt/QmlStreamer.h
@@ -69,15 +69,15 @@ class QmlStreamer : public QObject
* Construct a new qml streamer by loading the QML, accessible by
* getRootItem() and sets up the Deflect stream.
*
- * @param qmlFile URL to QML file to load
- * @param streamHost hostname of the Deflect server
- * @param streamName name of the Deflect stream (optional). Setting this
- * value overrides the 'objectName' property of the root QML item.
- * If neither is provided, "QmlStreamer" is used instead.
+ * @param qmlFile URL to QML file to load.
+ * @param streamHost host where the Deflect server is running.
+ * @param streamId identifier for the Deflect stream (optional). Setting
+ * this value overrides the 'objectName' property of the root QML
+ * item. If neither is provided, "QmlStreamer" is used instead.
*/
DEFLECTQT_API QmlStreamer( const QString& qmlFile,
const std::string& streamHost,
- const std::string& streamName = std::string( ));
+ const std::string& streamId = std::string( ));
DEFLECTQT_API ~QmlStreamer();
diff --git a/deflect/qt/QmlStreamerImpl.cpp b/deflect/qt/QmlStreamerImpl.cpp
index 980dd24..0bd5ae3 100644
--- a/deflect/qt/QmlStreamerImpl.cpp
+++ b/deflect/qt/QmlStreamerImpl.cpp
@@ -53,7 +53,7 @@
namespace
{
-const std::string DEFAULT_STREAM_NAME( "QmlStreamer" );
+const std::string DEFAULT_STREAM_ID( "QmlStreamer" );
}
class RenderControl : public QQuickRenderControl
@@ -80,7 +80,7 @@ namespace qt
{
QmlStreamer::Impl::Impl( const QString& qmlFile, const std::string& streamHost,
- const std::string& streamName )
+ const std::string& streamId )
: QWindow()
, _context( new QOpenGLContext )
, _offscreenSurface( new QOffscreenSurface )
@@ -97,7 +97,7 @@ QmlStreamer::Impl::Impl( const QString& qmlFile, const std::string& streamHost,
, _eventHandler( nullptr )
, _streaming( false )
, _streamHost( streamHost )
- , _streamName( streamName )
+ , _streamId( streamId )
{
setSurfaceType( QSurface::OpenGLSurface );
@@ -328,19 +328,19 @@ bool QmlStreamer::Impl::_setupRootItem()
return true;
}
-std::string QmlStreamer::Impl::_getDeflectStreamName() const
+std::string QmlStreamer::Impl::_getDeflectStreamIdentifier() const
{
- if( !_streamName.empty( ))
- return _streamName;
+ if( !_streamId.empty( ))
+ return _streamId;
- const std::string streamName = _rootItem->objectName().toStdString();
- return streamName.empty() ? DEFAULT_STREAM_NAME : streamName;
+ const std::string streamId = _rootItem->objectName().toStdString();
+ return streamId.empty() ? DEFAULT_STREAM_ID : streamId;
}
bool QmlStreamer::Impl::_setupDeflectStream()
{
if( !_stream )
- _stream = new Stream( _getDeflectStreamName(), _streamHost );
+ _stream = new Stream( _getDeflectStreamIdentifier(), _streamHost );
if( !_stream->isConnected( ))
return false;
diff --git a/deflect/qt/QmlStreamerImpl.h b/deflect/qt/QmlStreamerImpl.h
index 8cd737f..fdd514f 100644
--- a/deflect/qt/QmlStreamerImpl.h
+++ b/deflect/qt/QmlStreamerImpl.h
@@ -71,7 +71,7 @@ class QmlStreamer::Impl : public QWindow
public:
Impl( const QString& qmlFile, const std::string& streamHost,
- const std::string& streamName );
+ const std::string& streamId );
~Impl();
@@ -101,7 +101,7 @@ private slots:
void streamClosed();
private:
- std::string _getDeflectStreamName() const;
+ std::string _getDeflectStreamIdentifier() const;
bool _setupDeflectStream();
void _updateSizes( const QSize& size );
@@ -119,7 +119,7 @@ private slots:
EventReceiver* _eventHandler;
bool _streaming;
const std::string _streamHost;
- const std::string _streamName;
+ const std::string _streamId;
SizeHints _sizeHints;
};
diff --git a/doc/Changelog.md b/doc/Changelog.md
index 3354d58..55a105f 100644
--- a/doc/Changelog.md
+++ b/doc/Changelog.md
@@ -8,6 +8,8 @@ Changelog {#Changelog}
QmlStreamer: correcly quit application when stream is closed.
* [99](https://github.com/BlueBrain/Deflect/pull/99):
Fix incomplete socket send under certain timing conditions
+* [98](https://github.com/BlueBrain/Deflect/pull/98):
+ Streams can be constructed based on the DEFLECT_ID and DEFLECT_HOST ENV_VARs.
* [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.
diff --git a/tests/cpp/ServerTests.cpp b/tests/cpp/ServerTests.cpp
index 6cadabb..6603263 100644
--- a/tests/cpp/ServerTests.cpp
+++ b/tests/cpp/ServerTests.cpp
@@ -43,18 +43,23 @@ namespace ut = boost::unit_test;
#include "MinimalGlobalQtApp.h"
-#include
+#include
#include
+#include
#include
#include
#include
+namespace
+{
+const QString testStreamId( "teststream" );
+}
+
BOOST_GLOBAL_FIXTURE( MinimalGlobalQtApp );
BOOST_AUTO_TEST_CASE( testSizeHintsReceivedByServer )
{
- const QString testURI( "teststream" );
deflect::SizeHints testHints;
testHints.maxWidth = 500;
testHints.preferredHeight= 200;
@@ -63,12 +68,15 @@ BOOST_AUTO_TEST_CASE( testSizeHintsReceivedByServer )
QWaitCondition received;
QMutex mutex;
+ QString streamId;
+ deflect::SizeHints sizeHints;
+
deflect::Server* server = new deflect::Server( 0 /* OS-chosen port */ );
server->connect( server, &deflect::Server::receivedSizeHints,
- [&]( QString uri, deflect::SizeHints hints )
+ [&]( const QString id, const deflect::SizeHints hints )
{
- BOOST_CHECK( uri == testURI );
- BOOST_CHECK( hints == testHints );
+ streamId = id;
+ sizeHints = hints;
mutex.lock();
received.wakeAll();
mutex.unlock();
@@ -79,7 +87,7 @@ BOOST_AUTO_TEST_CASE( testSizeHintsReceivedByServer )
serverThread.start();
{
- deflect::Stream stream( testURI.toStdString(), "localhost",
+ deflect::Stream stream( testStreamId.toStdString(), "localhost",
server->serverPort());
BOOST_CHECK( stream.isConnected( ));
stream.sendSizeHints( testHints );
@@ -89,6 +97,56 @@ BOOST_AUTO_TEST_CASE( testSizeHintsReceivedByServer )
received.wait( &mutex, 2000 /*ms*/ );
mutex.unlock();
+ BOOST_CHECK_EQUAL( streamId.toStdString(), testStreamId.toStdString( ));
+ BOOST_CHECK( sizeHints == testHints );
+
+ serverThread.quit();
+ serverThread.wait();
+}
+
+BOOST_AUTO_TEST_CASE( testRegisterForEventReceivedByServer )
+{
+ QThread serverThread;
+ deflect::Server* server = new deflect::Server( 0 /* OS-chosen port */ );
+ server->moveToThread( &serverThread );
+ serverThread.connect( &serverThread, &QThread::finished,
+ server, &deflect::Server::deleteLater );
+ serverThread.start();
+
+ QWaitCondition received;
+ QMutex mutex;
+
+ QString streamId;
+ bool exclusiveBind = false;
+ deflect::EventReceiver* eventReceiver = nullptr;
+
+ server->connect( server, &deflect::Server::registerToEvents,
+ [&]( const QString id, const bool exclusive,
+ deflect::EventReceiver* receiver )
+ {
+ streamId = id;
+ exclusiveBind = exclusive;
+ eventReceiver = receiver;
+ mutex.lock();
+ received.wakeAll();
+ mutex.unlock();
+ });
+
+ {
+ deflect::Stream stream( testStreamId.toStdString(), "localhost",
+ server->serverPort( ));
+ BOOST_REQUIRE( stream.isConnected( ));
+ // Just send an event to check the streamId received by the server
+ stream.registerForEvents( true );
+ }
+ mutex.lock();
+ received.wait( &mutex, 2000 /*ms*/ );
+ mutex.unlock();
+
+ BOOST_CHECK_EQUAL( streamId.toStdString(), testStreamId.toStdString( ));
+ BOOST_CHECK_EQUAL( exclusiveBind, true );
+ BOOST_CHECK( eventReceiver );
+
serverThread.quit();
serverThread.wait();
}
diff --git a/tests/cpp/StreamTests.cpp b/tests/cpp/StreamTests.cpp
new file mode 100644
index 0000000..fee7e10
--- /dev/null
+++ b/tests/cpp/StreamTests.cpp
@@ -0,0 +1,119 @@
+/*********************************************************************/
+/* Copyright (c) 2016, EPFL/Blue Brain Project */
+/* Raphael Dumusc */
+/* All rights reserved. */
+/* */
+/* Redistribution and use in source and binary forms, with or */
+/* without modification, are permitted provided that the following */
+/* conditions are met: */
+/* */
+/* 1. Redistributions of source code must retain the above */
+/* copyright notice, this list of conditions and the following */
+/* disclaimer. */
+/* */
+/* 2. Redistributions in binary form must reproduce the above */
+/* copyright notice, this list of conditions and the following */
+/* disclaimer in the documentation and/or other materials */
+/* provided with the distribution. */
+/* */
+/* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY OF TEXAS AT */
+/* AUSTIN ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, */
+/* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF */
+/* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE */
+/* DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OF TEXAS AT */
+/* AUSTIN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, */
+/* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES */
+/* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE */
+/* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR */
+/* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF */
+/* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */
+/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT */
+/* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE */
+/* POSSIBILITY OF SUCH DAMAGE. */
+/* */
+/* The views and conclusions contained in the software and */
+/* documentation are those of the authors and should not be */
+/* interpreted as representing official policies, either expressed */
+/* or implied, of Ecole polytechnique federale de Lausanne. */
+/*********************************************************************/
+
+#define BOOST_TEST_MODULE Stream
+#include
+namespace ut = boost::unit_test;
+
+#include
+
+#include
+#include
+#include
+
+namespace
+{
+const char* STREAM_ID_ENV_VAR = "DEFLECT_ID";
+const char* STREAM_HOST_ENV_VAR = "DEFLECT_HOST";
+}
+
+BOOST_AUTO_TEST_CASE( testParameterizedConstructorWithValues )
+{
+ const deflect::Stream stream( "mystream", "somehost" );
+ BOOST_CHECK_EQUAL( stream.getId(), "mystream" );
+ BOOST_CHECK_EQUAL( stream.getHost(), "somehost" );
+}
+
+BOOST_AUTO_TEST_CASE( testDefaultConstructorReadsEnvironmentVariables )
+{
+ qputenv( STREAM_ID_ENV_VAR, "mystream" );
+ qputenv( STREAM_HOST_ENV_VAR, "somehost" );
+ deflect::Stream stream;
+ BOOST_CHECK_EQUAL( stream.getId(), "mystream" );
+ BOOST_CHECK_EQUAL( stream.getHost(), "somehost" );
+ qunsetenv( STREAM_ID_ENV_VAR );
+ qunsetenv( STREAM_HOST_ENV_VAR );
+}
+
+BOOST_AUTO_TEST_CASE( testParameterizedConstructorReadsEnvironmentVariables )
+{
+ qputenv( STREAM_ID_ENV_VAR, "mystream" );
+ qputenv( STREAM_HOST_ENV_VAR, "somehost" );
+ const deflect::Stream stream( "", "" );
+ BOOST_CHECK_EQUAL( stream.getId(), "mystream" );
+ BOOST_CHECK_EQUAL( stream.getHost(), "somehost" );
+ qunsetenv( STREAM_ID_ENV_VAR );
+ qunsetenv( STREAM_HOST_ENV_VAR );
+}
+
+BOOST_AUTO_TEST_CASE( testWhenSteamHostNotProvidedThenThrow )
+{
+ BOOST_REQUIRE( QString( qgetenv( STREAM_HOST_ENV_VAR )).isEmpty( ));
+ BOOST_CHECK_THROW( std::make_shared(),
+ std::runtime_error );
+ BOOST_CHECK_THROW( deflect::Stream stream( "mystream", "" ),
+ std::runtime_error );
+}
+
+BOOST_AUTO_TEST_CASE( testWhenSteamHostProvidedThenNoThrow )
+{
+ BOOST_CHECK_NO_THROW( deflect::Stream stream( "mystream", "somehost" ));
+ qputenv( STREAM_HOST_ENV_VAR, "somehost" );
+ BOOST_CHECK_NO_THROW( std::make_shared( ));
+ BOOST_CHECK_NO_THROW( deflect::Stream stream( "mystream", "" ));
+ qunsetenv( STREAM_HOST_ENV_VAR );
+}
+
+BOOST_AUTO_TEST_CASE( testWhenNoSteamIdProvidedThenARandomOneIsGenerated )
+{
+ BOOST_REQUIRE( QString( qgetenv( STREAM_ID_ENV_VAR )).isEmpty( ));
+ {
+ deflect::Stream stream( "", "somehost" );
+ BOOST_CHECK( !stream.getId().empty( ));
+ BOOST_CHECK_NE( deflect::Stream( "", "host").getId(),
+ deflect::Stream( "", "host").getId( ));
+ }
+ {
+ qputenv( STREAM_HOST_ENV_VAR, "somehost" );
+ deflect::Stream stream;
+ BOOST_CHECK( !stream.getId().empty( ));
+ BOOST_CHECK_NE( deflect::Stream().getId(), deflect::Stream().getId( ));
+ qunsetenv( STREAM_HOST_ENV_VAR );
+ }
+}
diff --git a/tests/cpp/perf/benchmarkStreamer.cpp b/tests/cpp/perf/benchmarkStreamer.cpp
index 46d5804..67ff5e7 100644
--- a/tests/cpp/perf/benchmarkStreamer.cpp
+++ b/tests/cpp/perf/benchmarkStreamer.cpp
@@ -109,8 +109,8 @@ struct BenchmarkOptions
using namespace boost::program_options;
desc.add_options()
("help", "produce help message")
- ("name", value()->default_value( "BenchmarkStreamer" ),
- "identifier for the stream")
+ ("id", value()->default_value( "BenchmarkStreamer" ),
+ "identifier for the stream")
("width", value()->default_value( 0 ),
"width of the stream in pixel")
("height", value()->default_value( 0 ),
@@ -119,7 +119,7 @@ struct BenchmarkOptions
"number of frames")
("framerate", value()->default_value( 0 ),
"framerate at which to send frames (default: unlimited)")
- ("hostname", value()->default_value( "localhost" ),
+ ("host", value()->default_value( "localhost" ),
"Target Deflect server host")
("compress", "compress segments using jpeg")
("precompute", "send precomputed segments (no encoding time)")
@@ -147,12 +147,12 @@ struct BenchmarkOptions
}
getHelp = vm.count("help");
- name = vm["name"].as();
+ id = vm["id"].as();
width = vm["width"].as();
height = vm["height"].as();
nframes = vm["nframes"].as();
framerate = vm["framerate"].as();
- hostname = vm["hostname"].as();
+ host = vm["host"].as();
compress = vm.count("compress");
precompute = vm.count("precompute");
quality = vm["quality"].as();
@@ -161,11 +161,11 @@ struct BenchmarkOptions
boost::program_options::options_description desc;
bool getHelp;
- std::string name;
+ std::string id;
unsigned int width, height;
unsigned int nframes;
unsigned int framerate;
- std::string hostname;
+ std::string host;
bool compress;
bool precompute;
unsigned int quality;
@@ -188,7 +188,7 @@ class Application
public:
explicit Application( const BenchmarkOptions& options )
: _options( options )
- , _stream( new deflect::Stream( options.name, options.hostname ))
+ , _stream( new deflect::Stream( options.id, options.host ))
{
generateNoiseImage( _options.width, _options.height );
generateJpegSegments();