diff --git a/CMakeLists.txt b/CMakeLists.txt index 84f051d..4d8cd37 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,10 @@ -# Copyright (c) 2013-2017, EPFL/Blue Brain Project +# Copyright (c) 2013-2018, EPFL/Blue Brain Project # Raphael Dumusc # Daniel Nachbaur cmake_minimum_required(VERSION 3.1 FATAL_ERROR) -project(Deflect VERSION 0.14.1) +project(Deflect VERSION 1.0.0) set(Deflect_VERSION_ABI 7) list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/CMake/common) diff --git a/README.md b/README.md index fe73288..0bb1033 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Welcome to Deflect, a C++ library for streaming pixels to other Deflect-based applications, for example [Tide](https://github.com/BlueBrain/Tide). -Deflect offers a stable API marked with version 1.7 (for the client part). +Deflect offers a stable API marked with version 1.0 (for the client part). ## Overview @@ -55,5 +55,5 @@ environments are tested: * Linux: Ubuntu 16.04 and RHEL 6 (Makefile, Ninja; x64) * Mac OS X: 10.7 - 10.10 (Makefile, Ninja; x86_64) -The [latest API documentation](http://bluebrain.github.io/Deflect-0.14/index.html) +The [latest API documentation](http://bluebrain.github.io/Deflect-1.0/index.html) can be found on [bluebrain.github.io](http://bluebrain.github.io). diff --git a/deflect/CMakeLists.txt b/deflect/CMakeLists.txt index 918a9f2..8f39c0a 100644 --- a/deflect/CMakeLists.txt +++ b/deflect/CMakeLists.txt @@ -1,51 +1,39 @@ -# Copyright (c) 2013-2017, EPFL/Blue Brain Project +# Copyright (c) 2013-2018, EPFL/Blue Brain Project # Raphael Dumusc # Daniel Nachbaur set(DEFLECT_PUBLIC_HEADERS - config.h Event.h - EventReceiver.h - Frame.h ImageWrapper.h Observer.h - Segment.h - SegmentParameters.h - Server.h SizeHints.h Stream.h types.h ) set(DEFLECT_HEADERS - FrameDispatcher.h + moodycamel/blockingconcurrentqueue.h + moodycamel/concurrentqueue.h ImageSegmenter.h MessageHeader.h MTQueue.h NetworkProtocol.h - ReceiveBuffer.h - ServerWorker.h + Segment.h + SegmentParameters.h Socket.h - SourceBuffer.h StreamPrivate.h TaskBuilder.h ) set(DEFLECT_SOURCES Event.cpp - Frame.cpp - FrameDispatcher.cpp ImageSegmenter.cpp ImageWrapper.cpp MessageHeader.cpp MetaTypeRegistration.cpp Observer.cpp - ReceiveBuffer.cpp - Server.cpp - ServerWorker.cpp Socket.cpp - SourceBuffer.cpp Stream.cpp StreamPrivate.cpp StreamSendWorker.cpp @@ -61,23 +49,19 @@ if(APPLE) endif() if(DEFLECT_USE_LIBJPEGTURBO) - list(APPEND DEFLECT_PUBLIC_HEADERS - SegmentDecoder.h - ) list(APPEND DEFLECT_HEADERS ImageJpegCompressor.h - ImageJpegDecompressor.h ) list(APPEND DEFLECT_SOURCES ImageJpegCompressor.cpp - ImageJpegDecompressor.cpp - SegmentDecoder.cpp ) - list(APPEND DEFLECT_LINK_LIBRARIES ${LibJpegTurbo_LIBRARIES}) + list(APPEND DEFLECT_LINK_LIBRARIES PRIVATE ${LibJpegTurbo_LIBRARIES}) endif() common_library(Deflect) +add_subdirectory(server) + if(Qt5Qml_FOUND AND Qt5Quick_FOUND AND NOT Qt5Quick_VERSION VERSION_LESS 5.4) add_subdirectory(qt) endif() diff --git a/deflect/Event.h b/deflect/Event.h index 9c77539..f1d544f 100644 --- a/deflect/Event.h +++ b/deflect/Event.h @@ -144,48 +144,32 @@ struct Event }; /** The type of event */ - EventType type; + EventType type = EVT_NONE; /** @name Mouse and touch events */ //@{ - double mouseX; /**< Normalized X mouse/touch position relative to the - window */ - double mouseY; /**< Normalized Y mouse/touch position relative to the - window */ - double dx; /**< Normalized horizontal delta for pan/pinch events / - delta in pixels for wheel events */ - double dy; /**< Normalized vertical delta for pan/pinch events / - delta in pixels for wheel events */ - bool mouseLeft; /**< State of the left mouse button (pressed=true) */ - bool mouseRight; /**< State of the right mouse button (pressed=true) */ - bool mouseMiddle; /**< State of the middle mouse button (pressed=true) */ + double mouseX = 0.0; /**< Normalized X mouse/touch position relative to the + window */ + double mouseY = 0.0; /**< Normalized Y mouse/touch position relative to the + window */ + double dx = 0.0; /**< Normalized horizontal delta for pan/pinch events / + delta in pixels for wheel events */ + double dy = 0.0; /**< Normalized vertical delta for pan/pinch events / + delta in pixels for wheel events */ + bool mouseLeft = false; /**< State of left mouse button (pressed=true) */ + bool mouseRight = false; /**< State of right mouse button (pressed=true) */ + bool mouseMiddle = false; /**< State of middle mouse button (pressed=true)*/ //@} /** @name Keyboard events */ //@{ - int key; /**< The key code, see QKeyEvent::key() / number of fingers + int key = 0; /**< The key code, see QKeyEvent::key() / number of fingers for gestures / point id for touch events */ - int modifiers; /**< The keyboard modifiers, see QKeyEvent::modifiers() */ + int modifiers = 0; /**< The keyboard modifiers, see QKeyEvent::modifiers()*/ char text[UNICODE_TEXT_SIZE]; /**< Carries unicode for key, see - QKeyEvent::text() */ + QKeyEvent::text() */ //@} - /** Construct a new event. @version 1.0 */ - Event() - : type(EVT_NONE) - , mouseX(0) - , mouseY(0) - , dx(0) - , dy(0) - , mouseLeft(false) - , mouseRight(false) - , mouseMiddle(false) - , key(0) - , modifiers(0) - , text() - { - } - /** The size of the QDataStream serialized output. */ static const uint32_t serializedSize; }; diff --git a/deflect/ImageJpegCompressor.h b/deflect/ImageJpegCompressor.h index 9c07e63..5ba65b8 100644 --- a/deflect/ImageJpegCompressor.h +++ b/deflect/ImageJpegCompressor.h @@ -51,7 +51,7 @@ namespace deflect { /** - * Perform JPEG compression for a PixelStreamSegment + * Perform JPEG compression for a region of an image. */ class ImageJpegCompressor { diff --git a/deflect/ImageSegmenter.cpp b/deflect/ImageSegmenter.cpp index 08637ad..b54a2de 100644 --- a/deflect/ImageSegmenter.cpp +++ b/deflect/ImageSegmenter.cpp @@ -1,5 +1,5 @@ /*********************************************************************/ -/* Copyright (c) 2013-2017, EPFL/Blue Brain Project */ +/* Copyright (c) 2013-2018, EPFL/Blue Brain Project */ /* Raphael Dumusc */ /* Stefan.Eilemann@epfl.ch */ /* All rights reserved. */ @@ -53,14 +53,11 @@ namespace deflect { -namespace -{ -bool _isOnRightSideOfSideBySideImage(const Segment& segment) +bool ImageSegmenter::_isOnRightSideOfSideBySideImage(const SegmentTask& segment) { return segment.sourceImage->view == View::side_by_side && segment.view == View::right_eye; } -} bool ImageSegmenter::generate(const ImageWrapper& image, Handler handler) { @@ -71,7 +68,7 @@ bool ImageSegmenter::generate(const ImageWrapper& image, Handler handler) Segment ImageSegmenter::createSingleSegment(const ImageWrapper& image) { - auto segments = _generateSegments(image); + auto segments = _generateSegmentTasks(image); if (segments.size() > 1) throw std::runtime_error( "createSingleSegment only works for small images"); @@ -83,7 +80,7 @@ Segment ImageSegmenter::createSingleSegment(const ImageWrapper& image) segment.imageData.reserve(segment.parameters.width * segment.parameters.height * image.getBytesPerPixel()); - segment.parameters.dataType = DataType::rgba; + segment.parameters.format = Format::rgba; segment.imageData.append((const char*)image.data, int(image.getBufferSize())); } @@ -114,7 +111,7 @@ bool ImageSegmenter::_generateJpeg(const ImageWrapper& image, { #ifdef DEFLECT_USE_LIBJPEGTURBO // The resulting Jpeg segments - auto segments = _generateSegments(image); + auto segments = _generateSegmentTasks(image); // start creating JPEGs for each segment, in parallel QtConcurrent::map(segments, std::bind(&ImageSegmenter::_computeJpeg, this, @@ -129,8 +126,13 @@ bool ImageSegmenter::_generateJpeg(const ImageWrapper& image, { bool result = true; for (; i < segments.size(); ++i) - if (!handler(_sendQueue.dequeue())) + { + const auto segment = _sendQueue.dequeue(); + if (segment.exception) + std::rethrow_exception(segment.exception); + if (!handler(segment)) result = false; + } return result; } catch (...) @@ -155,7 +157,7 @@ bool ImageSegmenter::_generateJpeg(const ImageWrapper& image, #endif } -void ImageSegmenter::_computeJpeg(Segment& segment, const bool sendSegment) +void ImageSegmenter::_computeJpeg(SegmentTask& segment, const bool sendSegment) { #ifdef DEFLECT_USE_LIBJPEGTURBO QRect imageRegion(segment.parameters.x - segment.sourceImage->x, @@ -179,7 +181,7 @@ void ImageSegmenter::_computeJpeg(Segment& segment, const bool sendSegment) segment.exception = std::current_exception(); } - segment.parameters.dataType = DataType::jpeg; + segment.parameters.format = Format::jpeg; if (sendSegment) _sendQueue.enqueue(segment); #endif @@ -188,13 +190,13 @@ void ImageSegmenter::_computeJpeg(Segment& segment, const bool sendSegment) bool ImageSegmenter::_generateRaw(const ImageWrapper& image, const Handler& handler) const { - auto segments = _generateSegments(image); + auto segments = _generateSegmentTasks(image); for (auto& segment : segments) { segment.imageData.reserve(segment.parameters.width * segment.parameters.height * image.getBytesPerPixel()); - segment.parameters.dataType = DataType::rgba; + segment.parameters.format = Format::rgba; if (segments.size() == 1) { @@ -230,17 +232,18 @@ bool ImageSegmenter::_generateRaw(const ImageWrapper& image, return true; } -Segments ImageSegmenter::_generateSegments(const ImageWrapper& image) const +ImageSegmenter::SegmentTasks ImageSegmenter::_generateSegmentTasks( + const ImageWrapper& image) const { - Segments segments; + SegmentTasks segments; for (const auto& params : _makeSegmentParameters(image)) { - Segment segment; + SegmentTask segment; segment.parameters = params; segment.view = image.view == View::side_by_side ? View::left_eye : image.view; - segment.sourceImage = ℑ segment.rowOrder = image.rowOrder; + segment.sourceImage = ℑ segments.push_back(segment); } @@ -262,7 +265,7 @@ Segments ImageSegmenter::_generateSegments(const ImageWrapper& image) const return segments; } -SegmentParametersList ImageSegmenter::_makeSegmentParameters( +ImageSegmenter::SegmentParametersList ImageSegmenter::_makeSegmentParameters( const ImageWrapper& image) const { const auto info = _makeSegmentationInfo(image); diff --git a/deflect/ImageSegmenter.h b/deflect/ImageSegmenter.h index fc9525e..9fa1d2b 100644 --- a/deflect/ImageSegmenter.h +++ b/deflect/ImageSegmenter.h @@ -1,5 +1,5 @@ /*********************************************************************/ -/* Copyright (c) 2013-2017, EPFL/Blue Brain Project */ +/* Copyright (c) 2013-2018, EPFL/Blue Brain Project */ /* Raphael Dumusc */ /* Stefan.Eilemann@epfl.ch */ /* All rights reserved. */ @@ -66,13 +66,13 @@ class ImageSegmenter /** * Generate segments. * - * The generation might be parallelized, calling the handler function - * concurrently in multiple threads for different segments. When one handler - * fails, the remaining handlers may or may not be executed. + * The generation might be parallelized, but the Handler callback is always + * executed from the calling thread. When one handle() fails, the remaining + * handle() calls may or may not be executed. * - * @param image The image to be segmented + * @param image The image to be segmented. * @param handler the function to handle the generated segment. - * @return true if all image handlers returned true, false on failure + * @return true if all image handlers returned true, false on failure. * @see setNominalSegmentDimensions() */ DEFLECT_API bool generate(const ImageWrapper& image, Handler handler); @@ -117,11 +117,24 @@ class ImageSegmenter uint lastHeight = 0; }; + struct SegmentTask : Segment + { + /** Uncompressed source image used for compression */ + const ImageWrapper* sourceImage = nullptr; + + /** Holds potential exception from compression thread */ + std::exception_ptr exception; + }; + static bool _isOnRightSideOfSideBySideImage(const SegmentTask& segment); + bool _generateJpeg(const ImageWrapper& image, const Handler& handler); - void _computeJpeg(Segment& task, bool sendSegment); + void _computeJpeg(SegmentTask& segment, bool sendSegment); bool _generateRaw(const ImageWrapper& image, const Handler& handler) const; - Segments _generateSegments(const ImageWrapper& image) const; + using SegmentTasks = std::vector; + SegmentTasks _generateSegmentTasks(const ImageWrapper& image) const; + + using SegmentParametersList = std::vector; SegmentParametersList _makeSegmentParameters( const ImageWrapper& image) const; SegmentationInfo _makeSegmentationInfo(const ImageWrapper& image) const; @@ -129,7 +142,7 @@ class ImageSegmenter uint _nominalSegmentWidth = 0; uint _nominalSegmentHeight = 0; - MTQueue _sendQueue; + MTQueue _sendQueue; }; } #endif diff --git a/deflect/ImageWrapper.cpp b/deflect/ImageWrapper.cpp index 8cc6cc5..eb3389a 100644 --- a/deflect/ImageWrapper.cpp +++ b/deflect/ImageWrapper.cpp @@ -1,5 +1,5 @@ /*********************************************************************/ -/* Copyright (c) 2013-2017, EPFL/Blue Brain Project */ +/* Copyright (c) 2013-2018, EPFL/Blue Brain Project */ /* Raphael Dumusc */ /* All rights reserved. */ /* */ @@ -73,25 +73,4 @@ size_t ImageWrapper::getBufferSize() const { return width * height * getBytesPerPixel(); } - -void ImageWrapper::swapYAxis(void* data, const unsigned int width, - const unsigned int height, const unsigned int bpp) -{ - unsigned char* src = (unsigned char*)data; - - size_t bytesPerLine = width * bpp; - size_t bufferSize = bytesPerLine * height; - - unsigned char* tmp = new unsigned char[bufferSize]; - - for (size_t y = 0; y < height; ++y) - { - memcpy((void*)(&tmp[y * bytesPerLine]), - (const void*)&src[(height - y - 1) * bytesPerLine], - bytesPerLine); - } - memcpy(data, (const void*)tmp, bufferSize); - - delete[] tmp; -} } diff --git a/deflect/ImageWrapper.h b/deflect/ImageWrapper.h index 27720bb..2598e71 100644 --- a/deflect/ImageWrapper.h +++ b/deflect/ImageWrapper.h @@ -1,5 +1,5 @@ /*********************************************************************/ -/* Copyright (c) 2013-2017, EPFL/Blue Brain Project */ +/* Copyright (c) 2013-2018, EPFL/Blue Brain Project */ /* Raphael Dumusc */ /* All rights reserved. */ /* */ @@ -128,12 +128,12 @@ struct ImageWrapper 100 best, default: 75). @version 1.0 */ ChromaSubsampling subsampling; /**< Chrominance sub-sampling. - (default: YUV444). @version 1.6 */ + (default: YUV444). @version 1.0 */ //@} /** * The view that this image represents. - * @version 1.6 + * @version 1.0 */ View view = View::mono; @@ -145,11 +145,10 @@ struct ImageWrapper * * All images that form a frame (possibly from multiple Streams) must have * the same row order, otherwise the frame is invalid. This is because the - * frame's segments need to be reordered in addtion to flipping them + * frame's tiles need to be reordered in addtion to flipping them * individually. * - * This is an alternative to calling swapYAxis() before sending. - * @version 1.7 + * @version 1.0 */ RowOrder rowOrder = RowOrder::top_down; @@ -164,22 +163,6 @@ struct ImageWrapper * @version 1.0 */ DEFLECT_API size_t getBufferSize() const; - - /** - * Swap an image along the Y axis. - * - * Used to switch between OpenGL convention (origin in bottom-left corner) - * and "standard" image format (origin in top-left corner). - * @param data The image buffer to be modified, containing width*height*bpp - * bytes - * @param width The width of the image - * @param height The height of the image - * @param bpp The number of bytes per pixel (RGB=3, ARGB=4, etc.) - * @version 1.0 - */ - DEFLECT_API static void swapYAxis(void* data, const unsigned int width, - const unsigned int height, - const unsigned int bpp); }; } diff --git a/deflect/MessageHeader.h b/deflect/MessageHeader.h index 090cd6c..7c4ae3b 100644 --- a/deflect/MessageHeader.h +++ b/deflect/MessageHeader.h @@ -1,5 +1,5 @@ /*********************************************************************/ -/* Copyright (c) 2013-2017, EPFL/Blue Brain Project */ +/* Copyright (c) 2013-2018, EPFL/Blue Brain Project */ /* Raphael.Dumusc@epfl.ch */ /* Daniel.Nachbaur@epfl.ch */ /* Redistribution and use in source and binary forms, with or */ diff --git a/deflect/MetaTypeRegistration.cpp b/deflect/MetaTypeRegistration.cpp index 736d7e8..344a996 100644 --- a/deflect/MetaTypeRegistration.cpp +++ b/deflect/MetaTypeRegistration.cpp @@ -41,6 +41,8 @@ #include "Event.h" #include "Segment.h" #include "SizeHints.h" +#include "server/Tile.h" +#include "server/types.h" #include "types.h" #include @@ -55,12 +57,15 @@ struct MetaTypeRegistration MetaTypeRegistration() { qRegisterMetaType("size_t"); - qRegisterMetaType("deflect::BoolPromisePtr"); qRegisterMetaType("deflect::Segment"); qRegisterMetaType("deflect::SizeHints"); qRegisterMetaType("deflect::Event"); - qRegisterMetaType("deflect::FramePtr"); qRegisterMetaType("deflect::View"); + qRegisterMetaType( + "deflect::server::BoolPromisePtr"); + qRegisterMetaType( + "deflect::server::FramePtr"); + qRegisterMetaType("deflect::server::Tile"); } }; diff --git a/deflect/Observer.h b/deflect/Observer.h index 1328b29..0e41f03 100644 --- a/deflect/Observer.h +++ b/deflect/Observer.h @@ -55,16 +55,13 @@ namespace deflect class StreamPrivate; /** - * Connect to a deflect::Server and register for events. + * Connect to a deflect Server and register for events. * * In case a dedicated event handling w/o the need for streaming images is * required, the Observer class can be used, in contrast to the deflect::Stream. * * On the server side the observer also opens and closes the stream as regular * deflect::Streams would do. - * - * This class is new since version 1.7 while sharing the same API as - * deflect::Stream prior 1.7. */ class Observer { @@ -81,7 +78,7 @@ class Observer * unique identifier will be used. * @throw std::runtime_error if DEFLECT_HOST was not provided or no * connection to server could be established - * @version 1.7 + * @version 1.0 */ DEFLECT_API Observer(); @@ -94,7 +91,7 @@ class Observer * @param port Port of the Server instance. * @throw std::runtime_error if DEFLECT_HOST was not provided or no * connection to server could be established - * @version 1.3 + * @version 1.0 */ DEFLECT_API explicit Observer(unsigned short port); @@ -131,13 +128,13 @@ class Observer */ DEFLECT_API bool isConnected() const; - /** @return the identifier defined by the constructor. @version 1.3 */ + /** @return the identifier defined by the constructor. @version 1.0 */ DEFLECT_API const std::string& getId() const; - /** @return the host defined by the constructor. @version 1.3 */ + /** @return the host defined by the constructor. @version 1.0 */ DEFLECT_API const std::string& getHost() const; - /** @return the remote port the observer is connected to. @version 1.7 */ + /** @return the remote port the observer is connected to. @version 1.0 */ DEFLECT_API unsigned short getPort() const; /** @@ -217,7 +214,7 @@ class Observer * * @param callback the function to call * @note replaces the previous disconnected signal - * @version 1.5 + * @version 1.0 */ DEFLECT_API void setDisconnectedCallback(std::function callback); @@ -227,7 +224,7 @@ class Observer * * @note blocks until all pending asynchonous send operations are finished. * @param hints the new size hints for the server - * @version 1.2 + * @version 1.0 */ DEFLECT_API void sendSizeHints(const SizeHints& hints); @@ -238,7 +235,7 @@ class Observer * @param data the pointer to the data buffer. * @param count the number of bytes to send. * @return true if the data could be sent, false otherwise - * @version 1.3 + * @version 1.0 */ DEFLECT_API bool sendData(const char* data, size_t count); diff --git a/deflect/Segment.h b/deflect/Segment.h index ae409d0..fafbcfb 100644 --- a/deflect/Segment.h +++ b/deflect/Segment.h @@ -1,5 +1,5 @@ /*********************************************************************/ -/* Copyright (c) 2013-2017, EPFL/Blue Brain Project */ +/* Copyright (c) 2013-2018, EPFL/Blue Brain Project */ /* Raphael Dumusc */ /* All rights reserved. */ /* */ @@ -46,28 +46,18 @@ namespace deflect { -struct ImageWrapper; - /** - * Image data and parameters for a single segment of a PixelStream. + * Image data and parameters for a segment of a larger image. */ struct Segment { - /** Parameters of the segment. */ SegmentParameters parameters; - - View view = View::mono; //!< Eye pass for the segment - - /** Image data of the segment. */ QByteArray imageData; - RowOrder rowOrder = RowOrder::top_down; //!< imageData row order - - /** @internal raw, uncompressed source image, used for compression */ - const ImageWrapper* sourceImage = nullptr; + /** Extra parameters sent separately for network protocol compatiblity. */ - /** @internal holds potential exception from compression thread */ - std::exception_ptr exception; + View view = View::mono; //!< Eye pass for the segment + RowOrder rowOrder = RowOrder::top_down; //!< Row order of imageData }; } diff --git a/deflect/SegmentParameters.h b/deflect/SegmentParameters.h index a6e2559..3490306 100644 --- a/deflect/SegmentParameters.h +++ b/deflect/SegmentParameters.h @@ -1,5 +1,5 @@ /*********************************************************************/ -/* Copyright (c) 2013-2017, EPFL/Blue Brain Project */ +/* Copyright (c) 2013-2018, EPFL/Blue Brain Project */ /* Raphael Dumusc */ /* All rights reserved. */ /* */ @@ -43,7 +43,7 @@ #ifdef _WIN32 typedef unsigned __int32 uint32_t; #else -#include +#include #endif #include @@ -51,20 +51,8 @@ typedef unsigned __int32 uint32_t; namespace deflect { /** - * The possible formats for segment data. - * @version 1.6 - */ -enum class DataType : std::uint8_t -{ - rgba = 0, // equivalent to old compressed=false property - jpeg = 1, // equivalent to old compressed=true property - yuv444, - yuv422, - yuv420 -}; - -/** - * Parameters for a Frame Segment. + * Parameters for a Segment of an image. + * @version 1.0 */ struct SegmentParameters { @@ -80,8 +68,15 @@ struct SegmentParameters uint32_t height = 0u; /**< The height in pixels. */ //@} - /** Data format of the Segment. @version 1.6 */ - DataType dataType = DataType::jpeg; + /** Format in which the segment data is stored. */ + Format format = Format::jpeg; + + /** + * WARNING: + * Extending this struct breaks compatibility with current + * NETWORK_PROTOCOL_VERSION == 8. This is due to the use of + * sizeof(SegmentParameters) in (de)serialization code. + */ }; } diff --git a/deflect/SizeHints.h b/deflect/SizeHints.h index 08c25ec..0805877 100644 --- a/deflect/SizeHints.h +++ b/deflect/SizeHints.h @@ -40,15 +40,13 @@ #ifndef DEFLECT_SIZEHINTS_H #define DEFLECT_SIZEHINTS_H -#include - namespace deflect { /** * A struct that contains hints about minimum, maximum and preferred sizes of a * streamer which can be interpreted by the stream server accordingly. * - * @version 1.2 + * @version 1.0 */ struct SizeHints { @@ -75,7 +73,7 @@ struct SizeHints }; /** @return true if rhs and this are equal for all sizes. */ -inline bool operator==(const SizeHints& lhs, const SizeHints& rhs) NOEXCEPT +inline bool operator==(const SizeHints& lhs, const SizeHints& rhs) noexcept { return lhs.minWidth == rhs.minWidth && lhs.minHeight == rhs.minHeight && lhs.maxWidth == rhs.maxWidth && lhs.maxHeight == rhs.maxHeight && @@ -84,7 +82,7 @@ inline bool operator==(const SizeHints& lhs, const SizeHints& rhs) NOEXCEPT } /** @return true if rhs and this not equal for any size. */ -inline bool operator!=(const SizeHints& lhs, const SizeHints& rhs) NOEXCEPT +inline bool operator!=(const SizeHints& lhs, const SizeHints& rhs) noexcept { return !(lhs == rhs); } diff --git a/deflect/Stream.h b/deflect/Stream.h index 3007638..37ecbc2 100644 --- a/deflect/Stream.h +++ b/deflect/Stream.h @@ -1,5 +1,5 @@ /*********************************************************************/ -/* Copyright (c) 2013-2017, EPFL/Blue Brain Project */ +/* Copyright (c) 2013-2018, EPFL/Blue Brain Project */ /* Raphael Dumusc */ /* Stefan.Eilemann@epfl.ch */ /* Daniel.Nachbaur@epfl.ch */ @@ -50,7 +50,7 @@ namespace deflect { /** - * Stream visual data to a deflect::Server. + * Stream visual data to a deflect Server. * * A Stream can be subdivided into one or more images and eye passes. This * allows to have different applications each responsible for sending one part @@ -71,7 +71,7 @@ class Stream : public Observer * unique identifier will be used. * @throw std::runtime_error if DEFLECT_HOST was not provided or no * connection to server could be established - * @version 1.7 + * @version 1.0 */ DEFLECT_API Stream(); @@ -84,7 +84,7 @@ class Stream : public Observer * @param port Port of the Server instance. * @throw std::runtime_error if DEFLECT_HOST was not provided or no * connection to server could be established - * @version 1.3 + * @version 1.0 */ DEFLECT_API explicit Stream(unsigned short port); @@ -118,7 +118,7 @@ class Stream : public Observer /** @name Asynchronous send API */ //@{ - /** Future signaling success of asyncSend(). @version 1.5 */ + /** Future signaling success of asyncSend(). @version 1.0 */ using Future = std::future; /** @@ -131,7 +131,7 @@ class Stream : public Observer * @throw std::invalid_argument if invalid JPEG compression arguments * @throw std::runtime_error if pending finishFrame() has not been completed * @throw std::runtime_error if JPEG compression failed - * @version 1.6 + * @version 1.0 * @sa finishFrame() */ DEFLECT_API Future send(const ImageWrapper& image); @@ -146,7 +146,7 @@ class Stream : public Observer * stereo rendering. * * @sa send() - * @version 1.6 + * @version 1.0 */ DEFLECT_API Future finishFrame(); @@ -165,12 +165,9 @@ class Stream : public Observer * @throw std::runtime_error if pending finishFrame() has not been completed * @throw std::runtime_error if JPEG compression failed * @see send() - * @version 1.6 + * @version 1.0 */ DEFLECT_API Future sendAndFinish(const ImageWrapper& image); - - /** @deprecated */ - Future asyncSend(const ImageWrapper& image) { return sendAndFinish(image); } //@} private: diff --git a/deflect/StreamEventThread.cpp b/deflect/StreamEventThread.cpp deleted file mode 100644 index 546dbe9..0000000 --- a/deflect/StreamEventThread.cpp +++ /dev/null @@ -1,200 +0,0 @@ -/*********************************************************************/ -/* Copyright (c) 2013, 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 The University of Texas at Austin. */ -/*********************************************************************/ - -#include "StreamEventThread.h" - -StreamEventThread::StreamEventThread() -{ -} - -void StreamEventThread::storeNewInteractionState(InteractionState state) -{ - QMutexLocker lock(&interactionStateMutex_); - interactionStates_.enqueue(state); -} - -bool StreamEventThread::hasPendingInteractionState() const -{ - QMutexLocker lock(&interactionStateMutex_); - return !interactionStates_.empty(); -} - -InteractionState StreamEventThread::retrieveInteractionState() -{ - QMutexLocker lock(&interactionStateMutex_); - - if (interactionStates_.empty()) - return InteractionState(); - - return interactionStates_.dequeue(); -} - -void StreamEventThread::run() -{ - put_flog(LOG_DEBUG, "started"); - - while (true) - { - // break here if we had a failure - if (!sendMessage_()) - break; - - MESSAGE_TYPE type; - receiveMessage_(type); - - // make sure the socket is still connected - if (socket_->state() != QAbstractSocket::ConnectedState) - { - put_flog(LOG_ERROR, "socket disconnected"); - break; - } - - // flush the socket - socket_->flush(); - - // break if disconnect() was called - { - QMutexLocker locker(&disconnectFlagMutex_); - - if (disconnectFlag_ == true) - { - break; - } - } - } - - // delete the socket - delete socket_; - socket_ = NULL; - - put_flog(LOG_DEBUG, "finished"); -} - -bool StreamEventThread::queueMessage(QByteArray message) -{ - // only queue the message if we're connected - if (!isConnected()) - { - return false; - } - - { - QMutexLocker locker(&sendMessagesQueueMutex_); - sendMessagesQueue_.push(message); - } - - sendMessage_(); - - return true; -} - -bool StreamEventThread::sendMessage_() -{ - // send a message if available - QByteArray sendMessage; - - { - QMutexLocker locker(&sendMessagesQueueMutex_); - - if (sendMessagesQueue_.size() > 0) - { - sendMessage = sendMessagesQueue_.front(); - sendMessagesQueue_.pop(); - } - } - - if (!sendMessage.isEmpty()) - { - bool success = socketSendMessage(sendMessage); - - if (!success) - { - put_flog(LOG_ERROR, "error sending message"); - return false; - } - } - return true; -} - -bool StreamEventThread::receiveMessage_(MESSAGE_TYPE& type) -{ - type = MESSAGE_TYPE_NONE; - - if (socket_->bytesAvailable() < (int)sizeof(MessageHeader)) - return false; - - MessageHeader messageHeader; - QByteArray message; - - bool success = socketReceiveMessage(messageHeader, message); - - if (!success) - { - put_flog(LOG_ERROR, "error receiving message"); - return false; - } - - type = messageHeader.type; - - // handle the message - if (type == MESSAGE_TYPE_ACK) - { - } - else if (type == MESSAGE_TYPE_INTERACTION) - { - InteractionState interactionState = - *(InteractionState*)(message.data()); - emit received(interactionState); - } - else if (type == MESSAGE_TYPE_BIND_INTERACTION_REPLY) - { - bool success = *(bool*)(message.data()); - emit receivedInteractionBindReply(success); - } - else if (type == MESSAGE_TYPE_QUIT) - { - put_flog(LOG_INFO, "Received QUIT - TODO implement this action"); - } - else - { - put_flog(LOG_ERROR, "unknown message header type %i", type); - return false; - } - return true; -} diff --git a/deflect/StreamPrivate.h b/deflect/StreamPrivate.h index 11ed490..aaec71e 100644 --- a/deflect/StreamPrivate.h +++ b/deflect/StreamPrivate.h @@ -57,7 +57,7 @@ class StreamPrivate { public: /** - * Create a new stream and open a new connection to the deflect::Server. + * Create a new stream and open a new connection to the deflect Server. * * @param id the unique stream identifier * @param host Address of the target Server instance. @@ -98,8 +98,6 @@ class StreamPrivate Stream::Future send(const SizeHints& hints); Stream::Future send(QByteArray&& data); Stream::Future sendImage(const ImageWrapper& image, bool finish); - Stream::Future sendSingleSegment(Segment&& segment, RowOrder orientation, - bool finish); Stream::Future sendFinishFrame(); /** @internal Called by StreamSendWorker when finishFrame was processed. */ diff --git a/deflect/StreamSendWorker.cpp b/deflect/StreamSendWorker.cpp index 14ea127..a9cc25f 100644 --- a/deflect/StreamSendWorker.cpp +++ b/deflect/StreamSendWorker.cpp @@ -1,5 +1,5 @@ /*********************************************************************/ -/* Copyright (c) 2013-2017, EPFL/Blue Brain Project */ +/* Copyright (c) 2013-2018, EPFL/Blue Brain Project */ /* Daniel.Nachbaur@epfl.ch */ /* Raphael Dumusc */ /* All rights reserved. */ @@ -192,9 +192,6 @@ bool StreamSendWorker::_sendClose() bool StreamSendWorker::_sendSegment(const Segment& segment) { - if (segment.exception) - std::rethrow_exception(segment.exception); - if (segment.view != _currentView) { if (!_sendImageView(segment.view)) diff --git a/deflect/qt/CMakeLists.txt b/deflect/qt/CMakeLists.txt index c4f097f..0d394d5 100644 --- a/deflect/qt/CMakeLists.txt +++ b/deflect/qt/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (c) 2015-2016, EPFL/Blue Brain Project +# Copyright (c) 2015-2018, EPFL/Blue Brain Project # Daniel Nachbaur # Raphael Dumusc @@ -7,6 +7,7 @@ set(DEFLECTQT_HEADERS EventReceiver.h QmlGestures.h QmlStreamerImpl.h + types.h ) set(DEFLECTQT_PUBLIC_HEADERS OffscreenQuickView.h @@ -24,9 +25,12 @@ set(DEFLECTQT_SOURCES ) set(DEFLECTQT_LINK_LIBRARIES - PUBLIC Deflect Qt5::Quick PRIVATE Qt5::Qml) + PUBLIC Deflect Qt5::Quick PRIVATE Qt5::Qml +) + set(DEFLECTQT_INCLUDE_NAME deflect/qt) -set(DEFLECTQT_NAMESPACE deflectqt) +set(DEFLECTQT_OMIT_VERSION_HEADERS ON) + common_library(DeflectQt) if(DEFLECT_QMLSTREAMER_MULTITHREADED) diff --git a/deflect/qt/QmlStreamer.h b/deflect/qt/QmlStreamer.h index ec47fec..6540d8a 100644 --- a/deflect/qt/QmlStreamer.h +++ b/deflect/qt/QmlStreamer.h @@ -40,7 +40,7 @@ #ifndef DELFECT_QT_QMLSTREAMER_H #define DELFECT_QT_QMLSTREAMER_H -#include +#include #include #include @@ -71,7 +71,7 @@ namespace qt * switch to a "mouse" interaction mode. This allows users to interact within * a WebGL canevas or select text instead of scrolling the page. */ -class DEFLECTQT_API QmlStreamer : public QObject +class DEFLECT_API QmlStreamer : public QObject { Q_OBJECT diff --git a/deflect/config.h b/deflect/qt/types.h similarity index 91% rename from deflect/config.h rename to deflect/qt/types.h index 15dad53..f891393 100644 --- a/deflect/config.h +++ b/deflect/qt/types.h @@ -1,5 +1,5 @@ /*********************************************************************/ -/* Copyright (c) 2015, EPFL/Blue Brain Project */ +/* Copyright (c) 2018, EPFL/Blue Brain Project */ /* Raphael Dumusc */ /* All rights reserved. */ /* */ @@ -37,15 +37,19 @@ /* or implied, of The University of Texas at Austin. */ /*********************************************************************/ -#ifndef DEFLECT_CONFIG_H -#define DEFLECT_CONFIG_H +#ifndef DEFLECT_QT_TYPES_H +#define DEFLECT_QT_TYPES_H -#ifndef NOEXCEPT -#ifdef _MSC_VER -#define NOEXCEPT -#else -#define NOEXCEPT noexcept -#endif -#endif +#include + +namespace deflect +{ +namespace qt +{ +class QuickRenderer; +class QmlStreamer; +class TouchInjector; +} +} #endif diff --git a/deflect/server/CMakeLists.txt b/deflect/server/CMakeLists.txt new file mode 100644 index 0000000..f05dd32 --- /dev/null +++ b/deflect/server/CMakeLists.txt @@ -0,0 +1,50 @@ + +# Copyright (c) 2018, EPFL/Blue Brain Project +# Raphael Dumusc + +set(DEFLECTSERVER_PUBLIC_HEADERS + EventReceiver.h + Frame.h + Server.h + Tile.h + types.h +) +set(DEFLECTSERVER_HEADERS + FrameDispatcher.h + ServerWorker.h + ReceiveBuffer.h + SourceBuffer.h +) +set(DEFLECTSERVER_SOURCES + Frame.cpp + FrameDispatcher.cpp + Server.cpp + ServerWorker.cpp + ReceiveBuffer.cpp + SourceBuffer.cpp +) + +set(DEFLECTSERVER_LINK_LIBRARIES + PUBLIC Deflect Qt5::Core PRIVATE Qt5::Network +) + +if(DEFLECT_USE_LIBJPEGTURBO) + list(APPEND DEFLECTSERVER_PUBLIC_HEADERS + TileDecoder.h + ) + list(APPEND DEFLECTSERVER_HEADERS + ImageJpegDecompressor.h + ) + list(APPEND DEFLECTSERVER_SOURCES + ImageJpegDecompressor.cpp + TileDecoder.cpp + ) + list(APPEND DEFLECTSERVER_LINK_LIBRARIES PRIVATE ${LibJpegTurbo_LIBRARIES}) +endif() + +set(DEFLECTSERVER_INCLUDE_NAME deflect/server) +set(DEFLECTSERVER_OMIT_VERSION_HEADERS ON) +# avoid conflict between server.h and Server.h on case-insensitive file systems +set(DEFLECTSERVER_OMIT_LIBRARY_HEADER ON) + +common_library(DeflectServer) diff --git a/deflect/EventReceiver.h b/deflect/server/EventReceiver.h similarity index 95% rename from deflect/EventReceiver.h rename to deflect/server/EventReceiver.h index 8998b13..c786822 100644 --- a/deflect/EventReceiver.h +++ b/deflect/server/EventReceiver.h @@ -37,8 +37,8 @@ /* or implied, of The University of Texas at Austin. */ /*********************************************************************/ -#ifndef DEFLECT_EVENTRECEIVER_H -#define DEFLECT_EVENTRECEIVER_H +#ifndef DEFLECT_SERVER_EVENTRECEIVER_H +#define DEFLECT_SERVER_EVENTRECEIVER_H #include #include @@ -47,6 +47,8 @@ namespace deflect { +namespace server +{ /** * Interface for classes to register as receivers for events. */ @@ -55,10 +57,12 @@ class DEFLECT_API EventReceiver : public QObject Q_OBJECT public: - virtual ~EventReceiver() {} + virtual ~EventReceiver() = default; + public slots: virtual void processEvent(deflect::Event event) = 0; }; } +} #endif diff --git a/deflect/Frame.cpp b/deflect/server/Frame.cpp similarity index 82% rename from deflect/Frame.cpp rename to deflect/server/Frame.cpp index a50a3d0..818a332 100644 --- a/deflect/Frame.cpp +++ b/deflect/server/Frame.cpp @@ -1,6 +1,6 @@ /*********************************************************************/ -/* Copyright (c) 2015, EPFL/Blue Brain Project */ -/* Raphael Dumusc */ +/* Copyright (c) 2015-2018, EPFL/Blue Brain Project */ +/* Raphael Dumusc */ /* All rights reserved. */ /* */ /* Redistribution and use in source and binary forms, with or */ @@ -41,16 +41,16 @@ namespace deflect { +namespace server +{ QSize Frame::computeDimensions() const { QSize size(0, 0); - for (const auto& segment : segments) + for (const auto& tile : tiles) { - const auto& params = segment.parameters; - size.setWidth(std::max(size.width(), (int)(params.width + params.x))); - size.setHeight( - std::max(size.height(), (int)(params.height + params.y))); + size.setWidth(std::max(size.width(), (int)(tile.width + tile.x))); + size.setHeight(std::max(size.height(), (int)(tile.height + tile.y))); } return size; @@ -58,17 +58,18 @@ QSize Frame::computeDimensions() const RowOrder Frame::determineRowOrder() const { - if (segments.empty()) - throw std::runtime_error("frame has no segements"); + if (tiles.empty()) + throw std::runtime_error("frame has no tiles"); - const auto frameRowOrder = segments[0].rowOrder; + const auto frameRowOrder = tiles[0].rowOrder; - for (const auto& segment : segments) + for (const auto& tile : tiles) { - if (segment.rowOrder != frameRowOrder) + if (tile.rowOrder != frameRowOrder) throw std::runtime_error("frame has incoherent row orders"); } return frameRowOrder; } } +} diff --git a/deflect/Frame.h b/deflect/server/Frame.h similarity index 87% rename from deflect/Frame.h rename to deflect/server/Frame.h index 9d6ca22..06f99a5 100644 --- a/deflect/Frame.h +++ b/deflect/server/Frame.h @@ -1,5 +1,5 @@ /*********************************************************************/ -/* Copyright (c) 2014-2017, EPFL/Blue Brain Project */ +/* Copyright (c) 2014-2018, EPFL/Blue Brain Project */ /* Raphael Dumusc */ /* All rights reserved. */ /* */ @@ -37,39 +37,40 @@ /* or implied, of The University of Texas at Austin. */ /*********************************************************************/ -#ifndef DEFLECT_FRAME_H -#define DEFLECT_FRAME_H +#ifndef DEFLECT_SERVER_FRAME_H +#define DEFLECT_SERVER_FRAME_H -#include #include -#include +#include #include #include namespace deflect { +namespace server +{ /** * A frame for a PixelStream. */ -class Frame +struct Frame { -public: - /** The full set of segments for this frame. */ - Segments segments; + /** The full set of tiles for this frame. */ + Tiles tiles; /** The PixelStream uri to which this frame is associated. */ QString uri; - /** Get the total dimensions of this frame. */ + /** @return the total dimensions of this frame. */ DEFLECT_API QSize computeDimensions() const; /** - * @return the row order of all frame segments - * @throws std::runtime_error if not all segments have the same RowOrder + * @return the row order of all frame tiles. + * @throws std::runtime_error if not all tiles have the same RowOrder. */ DEFLECT_API RowOrder determineRowOrder() const; }; } +} #endif diff --git a/deflect/FrameDispatcher.cpp b/deflect/server/FrameDispatcher.cpp similarity index 90% rename from deflect/FrameDispatcher.cpp rename to deflect/server/FrameDispatcher.cpp index 691dfde..e839266 100644 --- a/deflect/FrameDispatcher.cpp +++ b/deflect/server/FrameDispatcher.cpp @@ -1,5 +1,5 @@ /*********************************************************************/ -/* Copyright (c) 2013-2017, EPFL/Blue Brain Project */ +/* Copyright (c) 2013-2018, EPFL/Blue Brain Project */ /* Raphael Dumusc */ /* All rights reserved. */ /* */ @@ -47,6 +47,8 @@ namespace deflect { +namespace server +{ class FrameDispatcher::Impl { public: @@ -59,12 +61,12 @@ class FrameDispatcher::Impl ReceiveBuffer& buffer = streamBuffers[uri]; while (buffer.hasCompleteFrame()) - frame->segments = buffer.popFrame(); + frame->tiles = buffer.popFrame(); - assert(!frame->segments.empty()); + assert(!frame->tiles.empty()); if (frame->determineRowOrder() == RowOrder::bottom_up) - mirrorSegmentsPositionsVertically(*frame); + mirrorTilesPositionsVertically(*frame); // receiver will request a new frame once this frame was consumed buffer.setAllowedToSend(false); @@ -72,11 +74,11 @@ class FrameDispatcher::Impl return frame; } - void mirrorSegmentsPositionsVertically(Frame& frame) const + void mirrorTilesPositionsVertically(Frame& frame) const { const auto height = frame.computeDimensions().height(); - for (auto& s : frame.segments) - s.parameters.y = height - s.parameters.y - s.parameters.height; + for (auto& tile : frame.tiles) + tile.y = height - tile.y - tile.height; } typedef std::map StreamBuffers; @@ -136,12 +138,11 @@ void FrameDispatcher::removeObserver(QString uri) deleteStream(uri); } -void FrameDispatcher::processSegment(const QString uri, - const size_t sourceIndex, - deflect::Segment segment) +void FrameDispatcher::processTile(const QString uri, const size_t sourceIndex, + deflect::server::Tile tile) { if (_impl->streamBuffers.count(uri)) - _impl->streamBuffers[uri].insert(segment, sourceIndex); + _impl->streamBuffers[uri].insert(tile, sourceIndex); } void FrameDispatcher::processFrameFinished(const QString uri, @@ -191,3 +192,4 @@ void FrameDispatcher::deleteStream(const QString uri) } } } +} diff --git a/deflect/FrameDispatcher.h b/deflect/server/FrameDispatcher.h similarity index 89% rename from deflect/FrameDispatcher.h rename to deflect/server/FrameDispatcher.h index 67266f9..9ef105d 100644 --- a/deflect/FrameDispatcher.h +++ b/deflect/server/FrameDispatcher.h @@ -1,5 +1,5 @@ /*********************************************************************/ -/* Copyright (c) 2013-2017, EPFL/Blue Brain Project */ +/* Copyright (c) 2013-2018, EPFL/Blue Brain Project */ /* Raphael Dumusc */ /* All rights reserved. */ /* */ @@ -37,20 +37,21 @@ /* or implied, of The University of Texas at Austin. */ /*********************************************************************/ -#ifndef DEFLECT_FRAMEDISPATCHER_H -#define DEFLECT_FRAMEDISPATCHER_H +#ifndef DEFLECT_SERVER_FRAMEDISPATCHER_H +#define DEFLECT_SERVER_FRAMEDISPATCHER_H -#include #include -#include +#include #include #include namespace deflect { +namespace server +{ /** - * Gather segments from multiple sources and dispatch full frames. + * Gather Tiles from multiple sources and dispatch full frames. */ class FrameDispatcher : public QObject { @@ -65,7 +66,7 @@ class FrameDispatcher : public QObject public slots: /** - * Add a source of Segments for a Stream. + * Add a source of Tiles for a Stream. * * @param uri Identifier for the stream * @param sourceIndex Identifier for the source in this stream @@ -73,7 +74,7 @@ public slots: void addSource(QString uri, size_t sourceIndex); /** - * Remove a source of Segments for a Stream. + * Remove a source of Tiles for a Stream. * * @param uri Identifier for the stream * @param sourceIndex Identifier for the source in this stream @@ -81,7 +82,7 @@ public slots: void removeSource(QString uri, size_t sourceIndex); /** - * Add a stream source as an observer which does not contribute segments. + * Add a stream source as an observer which does not contribute tiles. * Emits pixelStreamOpened() if no other observer or source is present. * * @param uri Identifier for the stream @@ -97,17 +98,17 @@ public slots: void removeObserver(QString uri); /** - * Process a new Segment. + * Process a new Tile. * * @param uri Identifier for the stream * @param sourceIndex Identifier for the source in the stream - * @param segment to process + * @param tile to process */ - void processSegment(QString uri, size_t sourceIndex, - deflect::Segment segment); + void processTile(QString uri, size_t sourceIndex, + deflect::server::Tile tile); /** - * The given source has finished sending segments for the current frame. + * The given source has finished sending Tiles for the current frame. * * @param uri Identifier for the stream * @param sourceIndex Identifier for the source in the stream @@ -162,12 +163,13 @@ public slots: * * @param frame The latest frame available for a stream */ - void sendFrame(deflect::FramePtr frame); + void sendFrame(deflect::server::FramePtr frame); private: class Impl; std::unique_ptr _impl; }; } +} #endif diff --git a/deflect/ImageJpegDecompressor.cpp b/deflect/server/ImageJpegDecompressor.cpp similarity index 99% rename from deflect/ImageJpegDecompressor.cpp rename to deflect/server/ImageJpegDecompressor.cpp index 940dbec..f025476 100644 --- a/deflect/ImageJpegDecompressor.cpp +++ b/deflect/server/ImageJpegDecompressor.cpp @@ -62,6 +62,8 @@ deflect::ChromaSubsampling _getSubsamp(const int tjJpegSubsamp) namespace deflect { +namespace server +{ ImageJpegDecompressor::ImageJpegDecompressor() : _tjHandle(tjInitDecompress()) { @@ -141,3 +143,4 @@ ImageJpegDecompressor::YUVData ImageJpegDecompressor::decompressToYUV( #endif } +} diff --git a/deflect/ImageJpegDecompressor.h b/deflect/server/ImageJpegDecompressor.h similarity index 96% rename from deflect/ImageJpegDecompressor.h rename to deflect/server/ImageJpegDecompressor.h index 13db414..20f15cb 100644 --- a/deflect/ImageJpegDecompressor.h +++ b/deflect/server/ImageJpegDecompressor.h @@ -37,12 +37,12 @@ /* or implied, of The University of Texas at Austin. */ /*********************************************************************/ -#ifndef DEFLECT_IMAGEJPEGDECOMPRESSOR_H -#define DEFLECT_IMAGEJPEGDECOMPRESSOR_H +#ifndef DEFLECT_SERVER_IMAGEJPEGDECOMPRESSOR_H +#define DEFLECT_SERVER_IMAGEJPEGDECOMPRESSOR_H #include #include -#include +#include #include @@ -50,6 +50,8 @@ namespace deflect { +namespace server +{ /** * JPEG header information. */ @@ -107,5 +109,6 @@ class ImageJpegDecompressor tjhandle _tjHandle; }; } +} #endif diff --git a/deflect/ReceiveBuffer.cpp b/deflect/server/ReceiveBuffer.cpp similarity index 92% rename from deflect/ReceiveBuffer.cpp rename to deflect/server/ReceiveBuffer.cpp index e004961..c2dc707 100644 --- a/deflect/ReceiveBuffer.cpp +++ b/deflect/server/ReceiveBuffer.cpp @@ -1,5 +1,5 @@ /*********************************************************************/ -/* Copyright (c) 2013-2017, EPFL/Blue Brain Project */ +/* Copyright (c) 2013-2018, EPFL/Blue Brain Project */ /* Raphael Dumusc */ /* All rights reserved. */ /* */ @@ -40,6 +40,7 @@ #include "ReceiveBuffer.h" #include + namespace { const size_t MAX_QUEUE_SIZE = 150; // stream blocked for ~5 seconds at 30Hz @@ -47,6 +48,8 @@ const size_t MAX_QUEUE_SIZE = 150; // stream blocked for ~5 seconds at 30Hz namespace deflect { +namespace server +{ bool ReceiveBuffer::addSource(const size_t sourceIndex) { assert(!_sourceBuffers.count(sourceIndex)); @@ -74,11 +77,11 @@ size_t ReceiveBuffer::getSourceCount() const return _sourceBuffers.size(); } -void ReceiveBuffer::insert(const Segment& segment, const size_t sourceIndex) +void ReceiveBuffer::insert(const Tile& tile, const size_t sourceIndex) { assert(_sourceBuffers.count(sourceIndex)); - _sourceBuffers[sourceIndex].insert(segment); + _sourceBuffers[sourceIndex].insert(tile); } void ReceiveBuffer::finishFrameForSource(const size_t sourceIndex) @@ -107,16 +110,16 @@ bool ReceiveBuffer::hasCompleteFrame() const return !_sourceBuffers.empty(); } -Segments ReceiveBuffer::popFrame() +Tiles ReceiveBuffer::popFrame() { - Segments frame; + Tiles frame; for (auto& kv : _sourceBuffers) { auto& buffer = kv.second; if (buffer.getBackFrameIndex() > _lastFrameComplete) { - const auto& segments = buffer.getSegments(); - frame.insert(frame.end(), segments.begin(), segments.end()); + const auto& tiles = buffer.getTiles(); + frame.insert(frame.end(), tiles.begin(), tiles.end()); buffer.pop(); } } @@ -134,3 +137,4 @@ bool ReceiveBuffer::isAllowedToSend() const return _allowedToSend; } } +} diff --git a/deflect/ReceiveBuffer.h b/deflect/server/ReceiveBuffer.h similarity index 84% rename from deflect/ReceiveBuffer.h rename to deflect/server/ReceiveBuffer.h index bfd483f..298df6e 100644 --- a/deflect/ReceiveBuffer.h +++ b/deflect/server/ReceiveBuffer.h @@ -1,5 +1,5 @@ /*********************************************************************/ -/* Copyright (c) 2013-2017, EPFL/Blue Brain Project */ +/* Copyright (c) 2013-2018, EPFL/Blue Brain Project */ /* Raphael Dumusc */ /* All rights reserved. */ /* */ @@ -37,32 +37,30 @@ /* or implied, of The University of Texas at Austin. */ /*********************************************************************/ -#ifndef DEFLECT_RECEIVEBUFFER_H -#define DEFLECT_RECEIVEBUFFER_H +#ifndef DEFLECT_SERVER_RECEIVEBUFFER_H +#define DEFLECT_SERVER_RECEIVEBUFFER_H -#include -#include #include -#include - -#include +#include #include #include namespace deflect { +namespace server +{ /** - * Buffer Segments from (multiple) sources. + * Buffer Tiles from (multiple) sources. * - * The buffer aggregates segments coming from different sources and delivers + * The buffer aggregates tiles coming from different sources and delivers * complete frames. */ class ReceiveBuffer { public: /** - * Add a source of segments. + * Add a source of tiles. * @param sourceIndex Unique source identifier * @return false if the source was already added or if * finishFrameForSource() has already been called for all existing @@ -71,7 +69,7 @@ class ReceiveBuffer DEFLECT_API bool addSource(size_t sourceIndex); /** - * Remove a source of segments. + * Remove a source of tiles. * @param sourceIndex Unique source identifier */ DEFLECT_API void removeSource(size_t sourceIndex); @@ -80,14 +78,14 @@ class ReceiveBuffer DEFLECT_API size_t getSourceCount() const; /** - * Insert a segment for the current frame and source. - * @param segment The segment to insert + * Insert a tile for the current frame and source. + * @param tile The tile to insert * @param sourceIndex Unique source identifier */ - DEFLECT_API void insert(const Segment& segment, size_t sourceIndex); + DEFLECT_API void insert(const Tile& tile, size_t sourceIndex); /** - * Call when the source has finished sending segments for the current frame. + * Call when the source has finished sending tiles for the current frame. * @param sourceIndex Unique source identifier * @throw std::runtime_error if the buffer exceeds its maximum size */ @@ -98,9 +96,9 @@ class ReceiveBuffer /** * Get the finished frame. - * @return A collection of segments that form a frame + * @return A collection of tiles that form a frame */ - DEFLECT_API Segments popFrame(); + DEFLECT_API Tiles popFrame(); /** Allow this buffer to be used by the next * FrameDispatcher::sendLatestFrame */ @@ -117,5 +115,6 @@ class ReceiveBuffer bool _allowedToSend = false; }; } +} #endif diff --git a/deflect/Server.cpp b/deflect/server/Server.cpp similarity index 55% rename from deflect/Server.cpp rename to deflect/server/Server.cpp index 2a1630e..8f57742 100644 --- a/deflect/Server.cpp +++ b/deflect/server/Server.cpp @@ -1,5 +1,5 @@ /*********************************************************************/ -/* Copyright (c) 2013-2017, EPFL/Blue Brain Project */ +/* Copyright (c) 2013-2018, EPFL/Blue Brain Project */ /* Raphael Dumusc */ /* Daniel.Nachbaur@epfl.ch */ /* All rights reserved. */ @@ -41,41 +41,105 @@ #include "Server.h" #include "FrameDispatcher.h" -#include "NetworkProtocol.h" #include "ServerWorker.h" +#include "deflect/NetworkProtocol.h" -#include #include +#include +#include + #include namespace deflect { +namespace server +{ const int Server::defaultPortNumber = DEFAULT_PORT_NUMBER; -class Server::Impl +class Server::Impl : public QTcpServer { public: - Impl(QObject* parent) - : frameDispatcher( - new FrameDispatcher(parent)) // will be deleted by parent + Impl(const int port, Server* parent_) + : QTcpServer(parent_) + , server{parent_} + , frameDispatcher{new FrameDispatcher{parent_}} { + setProxy(QNetworkProxy::NoProxy); + if (!listen(QHostAddress::Any, port)) + { + const auto err = + QString("could not listen on port: %1. QTcpServer: %2") + .arg(port) + .arg(QTcpServer::errorString()); + throw std::runtime_error(err.toStdString()); + } } - FrameDispatcher* frameDispatcher; -}; + ~Impl() + { + for (auto child : children()) + { + if (auto workerThread = qobject_cast(child)) + { + workerThread->quit(); + workerThread->wait(); + } + } + } -Server::Server(const int port) - : _impl(new Impl(this)) -{ - setProxy(QNetworkProxy::NoProxy); - if (!listen(QHostAddress::Any, port)) + /** Re-implemented handling of connections from QTCPSocket. */ + void incomingConnection(const qintptr socketHandle) final { - const auto err = QString("could not listen on port: %1. QTcpServer: %2") - .arg(port) - .arg(QTcpServer::errorString()); - throw std::runtime_error(err.toStdString()); + auto workerThread = new QThread(this); + auto worker = new ServerWorker(socketHandle); + + worker->moveToThread(workerThread); + + connect(workerThread, &QThread::started, worker, + &ServerWorker::initConnection); + connect(worker, &ServerWorker::connectionClosed, workerThread, + &QThread::quit); + + // Make sure the thread will be deleted + connect(workerThread, &QThread::finished, worker, + &ServerWorker::deleteLater); + connect(workerThread, &QThread::finished, workerThread, + &QThread::deleteLater); + + // public signals/slots, forwarding from/to worker + connect(worker, &ServerWorker::registerToEvents, server, + &Server::registerToEvents); + connect(worker, &ServerWorker::receivedSizeHints, server, + &Server::receivedSizeHints); + connect(worker, &ServerWorker::receivedData, server, + &Server::receivedData); + connect(server, &Server::_closePixelStream, worker, + &ServerWorker::closeConnection); + + // FrameDispatcher + connect(worker, &ServerWorker::addStreamSource, frameDispatcher, + &FrameDispatcher::addSource); + connect(worker, &ServerWorker::receivedTile, frameDispatcher, + &FrameDispatcher::processTile); + connect(worker, &ServerWorker::receivedFrameFinished, frameDispatcher, + &FrameDispatcher::processFrameFinished); + connect(worker, &ServerWorker::removeStreamSource, frameDispatcher, + &FrameDispatcher::removeSource); + connect(worker, &ServerWorker::addObserver, frameDispatcher, + &FrameDispatcher::addObserver); + connect(worker, &ServerWorker::removeObserver, frameDispatcher, + &FrameDispatcher::removeObserver); + + workerThread->start(); } + Server* server = nullptr; + FrameDispatcher* frameDispatcher = nullptr; // owned by QObject's parent +}; + +Server::Server(const int port) + : _impl(new Impl(port, this)) +{ // Forward FrameDispatcher signals connect(_impl->frameDispatcher, &FrameDispatcher::pixelStreamOpened, this, &Server::pixelStreamOpened); @@ -92,14 +156,12 @@ Server::Server(const int port) Server::~Server() { - for (QObject* child : children()) - { - if (QThread* workerThread = qobject_cast(child)) - { - workerThread->quit(); - workerThread->wait(); - } - } + _impl.release(); // avoid double-deletion of child QObject +} + +quint16 Server::getPort() const +{ + return _impl->serverPort(); } void Server::requestFrame(const QString uri) @@ -112,48 +174,5 @@ void Server::closePixelStream(const QString uri) emit _closePixelStream(uri); _impl->frameDispatcher->deleteStream(uri); } - -void Server::incomingConnection(const qintptr socketHandle) -{ - QThread* workerThread = new QThread(this); - ServerWorker* worker = new ServerWorker(socketHandle); - - worker->moveToThread(workerThread); - - connect(workerThread, &QThread::started, worker, - &ServerWorker::initConnection); - connect(worker, &ServerWorker::connectionClosed, workerThread, - &QThread::quit); - - // Make sure the thread will be deleted - connect(workerThread, &QThread::finished, worker, - &ServerWorker::deleteLater); - connect(workerThread, &QThread::finished, workerThread, - &QThread::deleteLater); - - // public signals/slots, forwarding from/to worker - connect(worker, &ServerWorker::registerToEvents, this, - &Server::registerToEvents); - connect(worker, &ServerWorker::receivedSizeHints, this, - &Server::receivedSizeHints); - connect(worker, &ServerWorker::receivedData, this, &Server::receivedData); - connect(this, &Server::_closePixelStream, worker, - &ServerWorker::closeConnection); - - // FrameDispatcher - connect(worker, &ServerWorker::addStreamSource, _impl->frameDispatcher, - &FrameDispatcher::addSource); - connect(worker, &ServerWorker::receivedSegment, _impl->frameDispatcher, - &FrameDispatcher::processSegment); - connect(worker, &ServerWorker::receivedFrameFinished, - _impl->frameDispatcher, &FrameDispatcher::processFrameFinished); - connect(worker, &ServerWorker::removeStreamSource, _impl->frameDispatcher, - &FrameDispatcher::removeSource); - connect(worker, &ServerWorker::addObserver, _impl->frameDispatcher, - &FrameDispatcher::addObserver); - connect(worker, &ServerWorker::removeObserver, _impl->frameDispatcher, - &FrameDispatcher::removeObserver); - - workerThread->start(); } } diff --git a/deflect/Server.h b/deflect/server/Server.h similarity index 91% rename from deflect/Server.h rename to deflect/server/Server.h index 960a94e..ad7c392 100644 --- a/deflect/Server.h +++ b/deflect/server/Server.h @@ -1,5 +1,5 @@ /*********************************************************************/ -/* Copyright (c) 2013-2017, EPFL/Blue Brain Project */ +/* Copyright (c) 2013-2018, EPFL/Blue Brain Project */ /* Raphael Dumusc */ /* Daniel.Nachbaur@epfl.ch */ /* All rights reserved. */ @@ -38,17 +38,19 @@ /* or implied, of The University of Texas at Austin. */ /*********************************************************************/ -#ifndef DEFLECT_SERVER_H -#define DEFLECT_SERVER_H +#ifndef DEFLECT_SERVER_SERVER_H +#define DEFLECT_SERVER_SERVER_H #include #include -#include +#include -#include +#include namespace deflect { +namespace server +{ /** * Listen to incoming connections from multiple Stream clients. * @@ -57,7 +59,7 @@ namespace deflect * The server integrates a flow-control mechanism to ensure that new frames are * dispatched only as fast as the application is capable of processing them. */ -class DEFLECT_API Server : public QTcpServer +class DEFLECT_API Server : public QObject { Q_OBJECT @@ -76,6 +78,9 @@ class DEFLECT_API Server : public QTcpServer /** Stop the server and close all open pixel stream connections. */ ~Server(); + /** @return the port on which the server is running. */ + quint16 getPort() const; + public slots: /** * Request the dispatching of the next frame for a given pixel stream. @@ -130,7 +135,7 @@ public slots: * * @param frame The latest frame that was received for a stream. */ - void receivedFrame(deflect::FramePtr frame); + void receivedFrame(deflect::server::FramePtr frame); /** * Emitted when a remote client wants to register for receiving events. @@ -141,8 +146,8 @@ public slots: * @param success the promise that must receive the success of the operation */ void registerToEvents(QString uri, bool exclusive, - deflect::EventReceiver* receiver, - deflect::BoolPromisePtr success); + deflect::server::EventReceiver* receiver, + deflect::server::BoolPromisePtr success); /** * Emitted when a remote client sends size hints for displaying the stream. @@ -164,12 +169,11 @@ public slots: class Impl; std::unique_ptr _impl; - /** Re-implemented handling of connections from QTCPSocket. */ - void incomingConnection(qintptr socketHandle) final; - signals: + /** @internal */ void _closePixelStream(QString uri); }; } +} #endif diff --git a/deflect/ServerWorker.cpp b/deflect/server/ServerWorker.cpp similarity index 92% rename from deflect/ServerWorker.cpp rename to deflect/server/ServerWorker.cpp index ee4e3ab..49f9d29 100644 --- a/deflect/ServerWorker.cpp +++ b/deflect/server/ServerWorker.cpp @@ -1,5 +1,5 @@ /*********************************************************************/ -/* Copyright (c) 2013-2017, EPFL/Blue Brain Project */ +/* Copyright (c) 2013-2018, EPFL/Blue Brain Project */ /* Raphael Dumusc */ /* Daniel.Nachbaur@epfl.ch */ /* All rights reserved. */ @@ -40,13 +40,14 @@ #include "ServerWorker.h" -#include "NetworkProtocol.h" - -#include -#include +#include "deflect/NetworkProtocol.h" +#include "deflect/SegmentParameters.h" #include +#include +#include + namespace { const int RECEIVE_TIMEOUT_MS = 3000; @@ -54,14 +55,13 @@ const int RECEIVE_TIMEOUT_MS = 3000; namespace deflect { +namespace server +{ ServerWorker::ServerWorker(const int socketDescriptor) : _tcpSocket{new QTcpSocket(this)} // Ensure that _tcpSocket parent is // *this* so it gets moved to thread , _sourceId{socketDescriptor} , _clientProtocolVersion{NETWORK_PROTOCOL_VERSION} - , _registeredToEvents{false} - , _activeView{View::mono} - , _activeRowOrder{RowOrder::top_down} { if (!_tcpSocket->setSocketDescriptor(socketDescriptor)) { @@ -151,9 +151,9 @@ void ServerWorker::_processMessages() void ServerWorker::_receiveMessage() { - const MessageHeader mh = _receiveMessageHeader(); - const QByteArray messageByteArray = _receiveMessageBody(mh.size); - _handleMessage(mh, messageByteArray); + const auto messageHeader = _receiveMessageHeader(); + const auto messageBody = _receiveMessageBody(messageHeader.size); + _handleMessage(messageHeader, messageBody); } MessageHeader ServerWorker::_receiveMessageHeader() @@ -249,9 +249,8 @@ void ServerWorker::_handleMessage(const MessageHeader& messageHeader, case MESSAGE_TYPE_SIZE_HINTS: { - const SizeHints* hints = - reinterpret_cast(byteArray.data()); - emit receivedSizeHints(_streamId, SizeHints(*hints)); + const auto hints = reinterpret_cast(byteArray.data()); + emit receivedSizeHints(_streamId, *hints); break; } @@ -313,16 +312,19 @@ void ServerWorker::_parseClientProtocolVersion(const QByteArray& message) void ServerWorker::_handlePixelStreamMessage(const QByteArray& message) { - Segment segment; + Tile tile; const auto data = message.data(); - segment.parameters = *reinterpret_cast(data); - segment.imageData = - message.right(message.size() - sizeof(SegmentParameters)); - segment.view = _activeView; - segment.rowOrder = _activeRowOrder; - - emit(receivedSegment(_streamId, _sourceId, segment)); + const auto params = reinterpret_cast(data); + tile.x = params->x; + tile.y = params->y; + tile.width = params->width; + tile.height = params->height; + tile.imageData = message.right(message.size() - sizeof(SegmentParameters)); + tile.view = _activeView; + tile.rowOrder = _activeRowOrder; + + emit(receivedTile(_streamId, _sourceId, tile)); } void ServerWorker::_sendProtocolVersion() @@ -381,3 +383,4 @@ bool ServerWorker::_isConnected() const return _tcpSocket->state() == QTcpSocket::ConnectedState; } } +} diff --git a/deflect/ServerWorker.h b/deflect/server/ServerWorker.h similarity index 85% rename from deflect/ServerWorker.h rename to deflect/server/ServerWorker.h index 2f9a86b..d55a5cd 100644 --- a/deflect/ServerWorker.h +++ b/deflect/server/ServerWorker.h @@ -1,5 +1,5 @@ /*********************************************************************/ -/* Copyright (c) 2013-2017, EPFL/Blue Brain Project */ +/* Copyright (c) 2013-2018, EPFL/Blue Brain Project */ /* Raphael Dumusc */ /* Daniel.Nachbaur@epfl.ch */ /* All rights reserved. */ @@ -38,21 +38,22 @@ /* or implied, of The University of Texas at Austin. */ /*********************************************************************/ -#ifndef DEFLECT_SERVER_WORKER_H -#define DEFLECT_SERVER_WORKER_H +#ifndef DEFLECT_SERVER_SERVERWORKER_H +#define DEFLECT_SERVER_SERVERWORKER_H #include -#include #include -#include #include -#include +#include +#include #include #include namespace deflect { +namespace server +{ class ServerWorker : public EventReceiver { Q_OBJECT @@ -74,13 +75,13 @@ public slots: void addObserver(QString uri); void removeObserver(QString uri); - void receivedSegment(QString uri, size_t sourceIndex, - deflect::Segment segment); + void receivedTile(QString uri, size_t sourceIndex, + deflect::server::Tile tile); void receivedFrameFinished(QString uri, size_t sourceIndex); void registerToEvents(QString uri, bool exclusive, - deflect::EventReceiver* receiver, - deflect::BoolPromisePtr success); + deflect::server::EventReceiver* receiver, + deflect::server::BoolPromisePtr success); void receivedSizeHints(QString uri, deflect::SizeHints hints); @@ -95,18 +96,18 @@ private slots: void _processMessages(); private: - QTcpSocket* _tcpSocket; + QTcpSocket* _tcpSocket = nullptr; QString _streamId; - int _sourceId; - int _clientProtocolVersion; - bool _observer{false}; + int _sourceId = -1; + int _clientProtocolVersion = -1; + bool _observer = false; - bool _registeredToEvents; + bool _registeredToEvents = false; QQueue _events; - View _activeView; - RowOrder _activeRowOrder; + View _activeView = View::mono; + RowOrder _activeRowOrder = RowOrder::top_down; void _receiveMessage(); MessageHeader _receiveMessageHeader(); @@ -126,5 +127,6 @@ private slots: bool _isConnected() const; }; } +} #endif diff --git a/deflect/SourceBuffer.cpp b/deflect/server/SourceBuffer.cpp similarity index 86% rename from deflect/SourceBuffer.cpp rename to deflect/server/SourceBuffer.cpp index 0c9edea..1c546d1 100644 --- a/deflect/SourceBuffer.cpp +++ b/deflect/server/SourceBuffer.cpp @@ -1,6 +1,6 @@ /*********************************************************************/ -/* Copyright (c) 2017, EPFL/Blue Brain Project */ -/* Raphael Dumusc */ +/* Copyright (c) 2017-2018, EPFL/Blue Brain Project */ +/* Raphael Dumusc */ /* All rights reserved. */ /* */ /* Redistribution and use in source and binary forms, with or */ @@ -43,14 +43,16 @@ namespace deflect { +namespace server +{ SourceBuffer::SourceBuffer() { - _segments.push(Segments()); + _tiles.push(Tiles()); } -const Segments& SourceBuffer::getSegments() const +const Tiles& SourceBuffer::getTiles() const { - return _segments.front(); + return _tiles.front(); } FrameIndex SourceBuffer::getBackFrameIndex() const @@ -60,27 +62,28 @@ FrameIndex SourceBuffer::getBackFrameIndex() const bool SourceBuffer::isBackFrameEmpty() const { - return _segments.back().empty(); + return _tiles.back().empty(); } void SourceBuffer::pop() { - _segments.pop(); + _tiles.pop(); } void SourceBuffer::push() { - _segments.push(Segments()); + _tiles.push(Tiles()); ++_backFrameIndex; } -void SourceBuffer::insert(const Segment& segment) +void SourceBuffer::insert(const Tile& tile) { - _segments.back().push_back(segment); + _tiles.back().push_back(tile); } size_t SourceBuffer::getQueueSize() const { - return _segments.size(); + return _tiles.size(); +} } } diff --git a/deflect/SourceBuffer.h b/deflect/server/SourceBuffer.h similarity index 82% rename from deflect/SourceBuffer.h rename to deflect/server/SourceBuffer.h index c7ee888..5a264a1 100644 --- a/deflect/SourceBuffer.h +++ b/deflect/server/SourceBuffer.h @@ -1,6 +1,6 @@ /*********************************************************************/ -/* Copyright (c) 2017, EPFL/Blue Brain Project */ -/* Raphael Dumusc */ +/* Copyright (c) 2017-2018, EPFL/Blue Brain Project */ +/* Raphael Dumusc */ /* All rights reserved. */ /* */ /* Redistribution and use in source and binary forms, with or */ @@ -37,22 +37,22 @@ /* or implied, of Ecole polytechnique federale de Lausanne. */ /*********************************************************************/ -#ifndef DEFLECT_SOURCEBUFFER_H -#define DEFLECT_SOURCEBUFFER_H +#ifndef DEFLECT_SERVER_SOURCEBUFFER_H +#define DEFLECT_SERVER_SOURCEBUFFER_H -#include -#include -#include +#include #include #include namespace deflect { +namespace server +{ using FrameIndex = unsigned int; /** - * Buffer for a single source of segments. + * Buffer for a single source of tiles. */ class SourceBuffer { @@ -60,17 +60,17 @@ class SourceBuffer /** Construct an empty buffer. */ SourceBuffer(); - /** @return the segments at the front of the queue. */ - const Segments& getSegments() const; + /** @return the tiles at the front of the queue. */ + const Tiles& getTiles() const; /** @return the frame index of the back of the buffer. */ FrameIndex getBackFrameIndex() const; - /** @return true if the back frame has no segments. */ + /** @return true if the back frame has no tiles. */ bool isBackFrameEmpty() const; - /** Insert a segment into the back frame. */ - void insert(const Segment& segment); + /** Insert a tile into the back frame. */ + void insert(const Tile& tile); /** Push a new frame to the back. */ void push(); @@ -82,12 +82,13 @@ class SourceBuffer size_t getQueueSize() const; private: - /** The collections of segments for each mono/left/right view. */ - std::queue _segments; + /** The collections of tiles for each mono/left/right view. */ + std::queue _tiles; /** The current indices of the mono/left/right frame for this source. */ FrameIndex _backFrameIndex = 0u; }; } +} #endif diff --git a/deflect/server/Tile.h b/deflect/server/Tile.h new file mode 100644 index 0000000..951c80f --- /dev/null +++ b/deflect/server/Tile.h @@ -0,0 +1,85 @@ +/*********************************************************************/ +/* Copyright (c) 2013-2018, 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 The University of Texas at Austin. */ +/*********************************************************************/ + +#ifndef DEFLECT_SERVER_TILE_H +#define DEFLECT_SERVER_TILE_H + +#include + +#include + +namespace deflect +{ +namespace server +{ +/** + * A Tile is a sub-region of an image in a Frame. + */ +struct Tile +{ + /** @name Coordinates */ + //@{ + uint32_t x = 0u; /**< The x position in pixels. */ + uint32_t y = 0u; /**< The y position in pixels. */ + //@} + + /** @name Dimensions */ + //@{ + uint32_t width = 0u; /**< The width in pixels. */ + uint32_t height = 0u; /**< The height in pixels. */ + //@} + + /** Image data. */ + QByteArray imageData; + + /** @name Image data parameters */ + //@{ + Format format = Format::jpeg; //!< Format in which the data is stored + RowOrder rowOrder = RowOrder::top_down; //!< Row order of imageData + //@} + + /** @name Metadata */ + //@{ + View view = View::mono; //!< Eye pass for the Tile + //@} +}; +} +} + +#endif diff --git a/deflect/SegmentDecoder.cpp b/deflect/server/TileDecoder.cpp similarity index 69% rename from deflect/SegmentDecoder.cpp rename to deflect/server/TileDecoder.cpp index 7b5c3fe..db82557 100644 --- a/deflect/SegmentDecoder.cpp +++ b/deflect/server/TileDecoder.cpp @@ -1,5 +1,5 @@ /*********************************************************************/ -/* Copyright (c) 2013-2017, EPFL/Blue Brain Project */ +/* Copyright (c) 2013-2018, EPFL/Blue Brain Project */ /* Raphael Dumusc */ /* All rights reserved. */ /* */ @@ -37,19 +37,21 @@ /* or implied, of The University of Texas at Austin. */ /*********************************************************************/ -#include "SegmentDecoder.h" +#include "TileDecoder.h" #include "ImageJpegDecompressor.h" -#include "Segment.h" - -#include +#include "Tile.h" #include #include +#include + namespace deflect { -class SegmentDecoder::Impl +namespace server +{ +class TileDecoder::Impl { public: Impl() {} @@ -60,67 +62,66 @@ class SegmentDecoder::Impl QFuture decodingFuture; }; -SegmentDecoder::SegmentDecoder() +TileDecoder::TileDecoder() : _impl(new Impl) { } -SegmentDecoder::~SegmentDecoder() +TileDecoder::~TileDecoder() { } -ChromaSubsampling SegmentDecoder::decodeType(const Segment& segment) +ChromaSubsampling TileDecoder::decodeType(const Tile& tile) { - if (segment.parameters.dataType != DataType::jpeg) - throw std::runtime_error("Segment is not in JPEG format"); + if (tile.format != Format::jpeg) + throw std::runtime_error("Tile is not in JPEG format"); - return _impl->decompressor.decompressHeader(segment.imageData).subsampling; + return _impl->decompressor.decompressHeader(tile.imageData).subsampling; } -size_t _getExpectedSize(const DataType dataType, - const SegmentParameters& params) +size_t _getExpectedSize(const Format format, const Tile& tile) { - const size_t imageSize = params.height * params.width; - switch (dataType) + const size_t imageSize = tile.height * tile.width; + switch (format) { - case DataType::rgba: + case Format::rgba: return imageSize * 4; - case DataType::yuv444: + case Format::yuv444: return imageSize * 3; - case DataType::yuv422: + case Format::yuv422: return imageSize * 2; - case DataType::yuv420: + case Format::yuv420: return imageSize + (imageSize >> 1); default: return 0; }; } -void _decodeSegment(ImageJpegDecompressor* decompressor, Segment* segment, - const bool skipRgbConversion) +void _decodeTile(ImageJpegDecompressor* decompressor, Tile* tile, + const bool skipRgbConversion) { - if (segment->parameters.dataType != DataType::jpeg) + if (tile->format != Format::jpeg) return; QByteArray decodedData; - DataType dataType; + Format format; try { #ifndef DEFLECT_USE_LEGACY_LIBJPEGTURBO if (skipRgbConversion) { - const auto yuv = decompressor->decompressToYUV(segment->imageData); + const auto yuv = decompressor->decompressToYUV(tile->imageData); decodedData = yuv.first; switch (yuv.second) { case ChromaSubsampling::YUV444: - dataType = DataType::yuv444; + format = Format::yuv444; break; case ChromaSubsampling::YUV422: - dataType = DataType::yuv422; + format = Format::yuv422; break; case ChromaSubsampling::YUV420: - dataType = DataType::yuv420; + format = Format::yuv420; break; default: throw std::runtime_error("unexpected ChromaSubsampling mode"); @@ -131,8 +132,8 @@ void _decodeSegment(ImageJpegDecompressor* decompressor, Segment* segment, Q_UNUSED(skipRgbConversion); #endif { - decodedData = decompressor->decompress(segment->imageData); - dataType = DataType::rgba; + decodedData = decompressor->decompress(tile->imageData); + format = Format::rgba; } } catch (const std::runtime_error&) @@ -140,29 +141,29 @@ void _decodeSegment(ImageJpegDecompressor* decompressor, Segment* segment, throw; } - const auto expectedSize = _getExpectedSize(dataType, segment->parameters); + const auto expectedSize = _getExpectedSize(format, *tile); if (size_t(decodedData.size()) != expectedSize) - throw std::runtime_error("unexpected segment size"); + throw std::runtime_error("unexpected tile size"); - segment->imageData = decodedData; - segment->parameters.dataType = dataType; + tile->imageData = decodedData; + tile->format = format; } -void SegmentDecoder::decode(Segment& segment) +void TileDecoder::decode(Tile& tile) { - _decodeSegment(&_impl->decompressor, &segment, false); + _decodeTile(&_impl->decompressor, &tile, false); } #ifndef DEFLECT_USE_LEGACY_LIBJPEGTURBO -void SegmentDecoder::decodeToYUV(Segment& segment) +void TileDecoder::decodeToYUV(Tile& tile) { - _decodeSegment(&_impl->decompressor, &segment, true); + _decodeTile(&_impl->decompressor, &tile, true); } #endif -void SegmentDecoder::startDecoding(Segment& segment) +void TileDecoder::startDecoding(Tile& tile) { // drop frames if we're currently processing if (isRunning()) @@ -174,11 +175,10 @@ void SegmentDecoder::startDecoding(Segment& segment) } _impl->decodingFuture = - QtConcurrent::run(_decodeSegment, &_impl->decompressor, &segment, - false); + QtConcurrent::run(_decodeTile, &_impl->decompressor, &tile, false); } -void SegmentDecoder::waitDecoding() +void TileDecoder::waitDecoding() { try { @@ -189,12 +189,13 @@ void SegmentDecoder::waitDecoding() // Let Qt throws a QUnhandledException and rewrite the error message. // QtConcurrent::run can only forward QException subclasses, which does // not even work on 5.7.1: https://bugreports.qt.io/browse/QTBUG-58021 - throw std::runtime_error("Segment decoding failed"); + throw std::runtime_error("Tile decoding failed"); } } -bool SegmentDecoder::isRunning() const +bool TileDecoder::isRunning() const { return _impl->decodingFuture.isRunning(); } } +} diff --git a/deflect/SegmentDecoder.h b/deflect/server/TileDecoder.h similarity index 74% rename from deflect/SegmentDecoder.h rename to deflect/server/TileDecoder.h index afde1ae..a214ab0 100644 --- a/deflect/SegmentDecoder.h +++ b/deflect/server/TileDecoder.h @@ -1,5 +1,5 @@ /*********************************************************************/ -/* Copyright (c) 2013-2017, EPFL/Blue Brain Project */ +/* Copyright (c) 2013-2018, EPFL/Blue Brain Project */ /* Raphael Dumusc */ /* All rights reserved. */ /* */ @@ -37,73 +37,75 @@ /* or implied, of The University of Texas at Austin. */ /*********************************************************************/ -#ifndef DEFLECT_SEGMENTDECODER_H -#define DEFLECT_SEGMENTDECODER_H +#ifndef DEFLECT_SERVER_TILEDECODER_H +#define DEFLECT_SERVER_TILEDECODER_H #include #include -#include +#include namespace deflect { +namespace server +{ /** - * Decode a Segment's image asynchronously. + * Decode a Tile's image asynchronously. */ -class SegmentDecoder +class TileDecoder { public: /** Construct a Decoder */ - DEFLECT_API SegmentDecoder(); + DEFLECT_API TileDecoder(); /** Destruct a Decoder */ - DEFLECT_API ~SegmentDecoder(); + DEFLECT_API ~TileDecoder(); /** - * Decode the data type of a JPEG segment. + * Decode the data type of a JPEG tile. * - * @param segment The segment to decode. + * @param tile The tile to decode. * @throw std::runtime_error if a decompression error occured */ - DEFLECT_API ChromaSubsampling decodeType(const Segment& segment); + DEFLECT_API ChromaSubsampling decodeType(const Tile& tile); /** - * Decode a JPEG segment to RGB. + * Decode a JPEG tile to RGB. * - * @param segment The segment to decode. Upon success, its imageData member - * will hold the decompressed RGB image and its "dataType" flag will - * be set to DataType::rgba. + * @param tile The tile to decode. Upon success, its imageData member + * will hold the decompressed RGB image and its "format" flag will + * be set to Format::rgba. * @throw std::runtime_error if a decompression error occured */ - DEFLECT_API void decode(Segment& segment); + DEFLECT_API void decode(Tile& tile); #ifndef DEFLECT_USE_LEGACY_LIBJPEGTURBO /** - * Decode a JPEG segment to YUV, skipping the YUV -> RGB step. + * Decode a JPEG tile to YUV, skipping the YUV -> RGB step. * - * @param segment The segment to decode. Upon success, its imageData member - * will hold the decompressed YUV image and its "dataType" flag will - * be set to the matching DataType::yuv4**. + * @param tile The tile to decode. Upon success, its imageData member + * will hold the decompressed YUV image and its "format" flag will + * be set to the matching Format::yuv4**. * @throw std::runtime_error if a decompression error occured */ - DEFLECT_API void decodeToYUV(Segment& segment); + DEFLECT_API void decodeToYUV(Tile& tile); #endif /** - * Start decoding a segment. + * Start decoding a tile. * * This function will silently ignore the request if a decoding is already * in progress. - * @param segment The segement to decode. The segment will be modified by + * @param tile The tile to decode. The tile will be modified by * this function. It must remain valid and should not be accessed * until the decoding procedure has completed. * @see isRunning() */ - DEFLECT_API void startDecoding(Segment& segment); + DEFLECT_API void startDecoding(Tile& tile); /** - * Waits for the decoding of a segment to finish, initiated by + * Waits for the decoding of a tile to finish, initiated by * startDecoding(). * @throw std::runtime_error if a decompression error occured */ @@ -117,5 +119,6 @@ class SegmentDecoder std::unique_ptr _impl; }; } +} #endif diff --git a/deflect/StreamEventThread.h b/deflect/server/types.h similarity index 70% rename from deflect/StreamEventThread.h rename to deflect/server/types.h index 39aca20..ebc361c 100644 --- a/deflect/StreamEventThread.h +++ b/deflect/server/types.h @@ -1,5 +1,5 @@ /*********************************************************************/ -/* Copyright (c) 2013, EPFL/Blue Brain Project */ +/* Copyright (c) 2018, EPFL/Blue Brain Project */ /* Raphael Dumusc */ /* All rights reserved. */ /* */ @@ -37,52 +37,27 @@ /* or implied, of The University of Texas at Austin. */ /*********************************************************************/ -#ifndef DEFLECT_STREAMEVENTTHREAD_H -#define DEFLECT_STREAMEVENTTHREAD_H +#ifndef DEFLECT_SERVER_TYPES_H +#define DEFLECT_SERVER_TYPES_H -#include +#include -#include -#include -#include - -/** - * This class might serve as an EventProcessing thread to read InteractionStates - * from the Socket. - * - * !!! IT IS NOT IMPLEMENTED YET !!! - */ -class StreamEventThread : public QObject +namespace deflect { - Q_OBJECT - -public: - StreamEventThread(); - - // queue a message to be sent (non-blocking) - bool queueMessage(QByteArray message); - - // current interaction state - mutable QMutex interactionStateMutex_; - QQueue interactionStates_; - - bool hasPendingInteractionState() const; - InteractionState retrieveInteractionState(); - - // thread execution - void run() final; - -public slots: - void storeNewInteractionState(InteractionState state); - void setInteractionBound(bool success); - -private: - // mutex and queue for messages to send - QMutex sendMessagesQueueMutex_; - std::queue sendMessagesQueue_; - - bool sendMessage_(); - bool receiveMessage_(MESSAGE_TYPE& type); -}; +namespace server +{ +class EventReceiver; +class FrameDispatcher; +class TileDecoder; +class Server; + +struct Frame; +struct Tile; + +using Tiles = std::vector; +using BoolPromisePtr = std::shared_ptr>; +using FramePtr = std::shared_ptr; +} +} #endif diff --git a/deflect/types.h b/deflect/types.h index 53d6c2d..4e568b7 100644 --- a/deflect/types.h +++ b/deflect/types.h @@ -1,5 +1,5 @@ /*********************************************************************/ -/* Copyright (c) 2014-2017, EPFL/Blue Brain Project */ +/* Copyright (c) 2014-2018, EPFL/Blue Brain Project */ /* Raphael Dumusc */ /* Daniel.Nachbaur@epfl.ch */ /* All rights reserved. */ @@ -41,8 +41,6 @@ #ifndef DEFLECT_TYPES_H #define DEFLECT_TYPES_H -#include - #include #include #include @@ -73,6 +71,16 @@ enum class RowOrder bottom_up /**< OpenGL image with (0,0) at the bottom-left corner. */ }; +/** The possible formats for image data. */ +enum class Format : std::uint8_t +{ + rgba = 0, + jpeg = 1, + yuv444, + yuv422, + yuv420 +}; + /** Cast an enum class value to its underlying type. */ template constexpr typename std::underlying_type::type as_underlying_type(E e) @@ -80,6 +88,7 @@ constexpr typename std::underlying_type::type as_underlying_type(E e) return static_cast::type>(e); } +/** Make a ready future with the given value. */ template std::future make_ready_future(T&& value) { @@ -88,6 +97,7 @@ std::future make_ready_future(T&& value) return promise.get_future(); } +/** Make a ready future with the given exception. */ template std::future make_exception_future(std::exception_ptr&& e) { @@ -95,18 +105,15 @@ std::future make_exception_future(std::exception_ptr&& e) promise.set_exception(std::move(e)); return promise.get_future(); } + +/** Make a ready future with the given exception. */ template std::future make_exception_future(Exception&& e) { return make_exception_future(std::make_exception_ptr(std::move(e))); } -class EventReceiver; -class Frame; -class FrameDispatcher; class ImageSegmenter; -class SegmentDecoder; -class Server; class Stream; struct Event; @@ -116,17 +123,7 @@ struct Segment; struct SegmentParameters; struct SizeHints; -using BoolPromisePtr = std::shared_ptr>; -using FramePtr = std::shared_ptr; using Segments = std::vector; -using SegmentParametersList = std::vector; - -namespace qt -{ -class QuickRenderer; -class QmlStreamer; -class TouchInjector; -} /** @internal */ namespace test diff --git a/doc/Changelog.md b/doc/Changelog.md index cf4c804..1e2e134 100644 --- a/doc/Changelog.md +++ b/doc/Changelog.md @@ -1,6 +1,14 @@ Changelog {#Changelog} ============ +## Deflect 1.0 + +### 1.0.0 (git master) +* [194](https://github.com/BlueBrain/Deflect/pull/194): + Reset API versioning to 1.0, remove deprecated functions (Stream::asyncSend, + ImageWrapper::swapYAxis). Moved all server-specific classes to a separate + namespace. + ## Deflect 0.14 ### 0.14.1 (01-05-2018) @@ -124,7 +132,7 @@ Changelog {#Changelog} are available in Qml from a "deflectgestures" context property. * [119](https://github.com/BlueBrain/Deflect/pull/119): Added deflect::Stream::sendData() to allow sending user-defined information - to the deflect::Server. + to the deflect Server. * [118](https://github.com/BlueBrain/Deflect/pull/118): New "pan" event for multi-finger pan gestures. * [117](https://github.com/BlueBrain/Deflect/pull/117): diff --git a/tests/cpp/CMakeLists.txt b/tests/cpp/CMakeLists.txt index 810fbba..97ff24b 100644 --- a/tests/cpp/CMakeLists.txt +++ b/tests/cpp/CMakeLists.txt @@ -1,13 +1,13 @@ -# Copyright (c) 2013-2015, EPFL/Blue Brain Project -# Daniel Nachbaur -# Raphael Dumusc +# Copyright (c) 2013-2018, EPFL/Blue Brain Project +# Daniel Nachbaur +# Raphael Dumusc # # Change this number when adding tests to force a CMake run: 0 -set(TEST_LIBRARIES Deflect DeflectMock ${Boost_LIBRARIES} Qt5::Widgets) +set(TEST_LIBRARIES DeflectServer DeflectMock ${Boost_LIBRARIES} Qt5::Widgets) add_definitions(-DBOOST_PROGRAM_OPTIONS_DYN_LINK) if(NOT DEFLECT_USE_LIBJPEGTURBO) - set(EXCLUDE_FROM_TESTS SegmentDecoderTests.cpp) + set(EXCLUDE_FROM_TESTS TileDecoderTests.cpp) endif() include(CommonCTest) diff --git a/tests/cpp/FrameDispatcherTests.cpp b/tests/cpp/FrameDispatcherTests.cpp index 5d40351..e10143a 100644 --- a/tests/cpp/FrameDispatcherTests.cpp +++ b/tests/cpp/FrameDispatcherTests.cpp @@ -1,6 +1,6 @@ /*********************************************************************/ -/* Copyright (c) 2017, EPFL/Blue Brain Project */ -/* Raphael Dumusc */ +/* Copyright (c) 2017-2018, EPFL/Blue Brain Project */ +/* Raphael Dumusc */ /* All rights reserved. */ /* */ /* Redistribution and use in source and binary forms, with or */ @@ -44,7 +44,7 @@ namespace ut = boost::unit_test; #include "FrameUtils.h" -#include +#include namespace { @@ -53,20 +53,23 @@ const char* streamId = "test"; struct Fixture { - deflect::FrameDispatcher dispatcher; - deflect::FramePtr receivedFrame; + deflect::server::FrameDispatcher dispatcher; + deflect::server::FramePtr receivedFrame; Fixture() { - QObject::connect(&dispatcher, &deflect::FrameDispatcher::sendFrame, - [&](deflect::FramePtr f) { receivedFrame = f; }); + QObject::connect(&dispatcher, + &deflect::server::FrameDispatcher::sendFrame, + [&](deflect::server::FramePtr f) { + receivedFrame = f; + }); dispatcher.addSource(streamId, 0); } - void dispatch(const deflect::Frame& frame) + void dispatch(const deflect::server::Frame& frame) { - for (auto& segment : frame.segments) - dispatcher.processSegment(streamId, 0, segment); + for (auto& tile : frame.tiles) + dispatcher.processTile(streamId, 0, tile); dispatcher.processFrameFinished(streamId, 0); dispatcher.requestFrame(streamId); } @@ -94,15 +97,15 @@ BOOST_FIXTURE_TEST_CASE(dispatch_multiple_frames, Fixture) BOOST_FIXTURE_TEST_CASE(dispatch_frame_bottom_up, Fixture) { auto frame = makeTestFrame(640, 480, 64); - for (auto& segment : frame.segments) - segment.rowOrder = deflect::RowOrder::bottom_up; + for (auto& tile : frame.tiles) + tile.rowOrder = deflect::RowOrder::bottom_up; dispatch(frame); BOOST_REQUIRE(receivedFrame); - // mirror segments positions vertically - for (auto& s : frame.segments) - s.parameters.y = 480 - s.parameters.y - s.parameters.height; + // mirror tiles positions vertically + for (auto& tile : frame.tiles) + tile.y = 480 - tile.y - tile.height; compare(frame, *receivedFrame); } @@ -110,7 +113,7 @@ BOOST_FIXTURE_TEST_CASE(dispatch_frame_bottom_up, Fixture) BOOST_FIXTURE_TEST_CASE(dispatch_frame_with_inconsistent_row_order, Fixture) { auto frame = makeTestFrame(640, 480, 64); - frame.segments[2].rowOrder = deflect::RowOrder::bottom_up; + frame.tiles[2].rowOrder = deflect::RowOrder::bottom_up; dispatch(frame); BOOST_CHECK(!receivedFrame); } diff --git a/tests/cpp/FrameTests.cpp b/tests/cpp/FrameTests.cpp index 4537abe..2e9e9d0 100644 --- a/tests/cpp/FrameTests.cpp +++ b/tests/cpp/FrameTests.cpp @@ -1,6 +1,6 @@ /*********************************************************************/ -/* Copyright (c) 2017, EPFL/Blue Brain Project */ -/* Raphael Dumusc */ +/* Copyright (c) 2017-2018, EPFL/Blue Brain Project */ +/* Raphael Dumusc */ /* All rights reserved. */ /* */ /* Redistribution and use in source and binary forms, with or */ @@ -64,13 +64,13 @@ BOOST_AUTO_TEST_CASE(determine_frame_row_order) auto frame = makeTestFrame(640, 480, 64); BOOST_CHECK(frame.determineRowOrder() == deflect::RowOrder::top_down); - frame.segments[0].rowOrder = deflect::RowOrder::bottom_up; + frame.tiles[0].rowOrder = deflect::RowOrder::bottom_up; BOOST_CHECK_THROW(frame.determineRowOrder(), std::runtime_error); - for (auto& segment : frame.segments) - segment.rowOrder = deflect::RowOrder::bottom_up; + for (auto& tile : frame.tiles) + tile.rowOrder = deflect::RowOrder::bottom_up; BOOST_CHECK(frame.determineRowOrder() == deflect::RowOrder::bottom_up); - frame.segments[0].rowOrder = deflect::RowOrder::top_down; + frame.tiles[0].rowOrder = deflect::RowOrder::top_down; BOOST_CHECK_THROW(frame.determineRowOrder(), std::runtime_error); } diff --git a/tests/cpp/ImageWrapperTests.cpp b/tests/cpp/ImageWrapperTests.cpp index ba58060..85341ce 100644 --- a/tests/cpp/ImageWrapperTests.cpp +++ b/tests/cpp/ImageWrapperTests.cpp @@ -90,95 +90,3 @@ BOOST_AUTO_TEST_CASE(testImageBytesPerPixel) BOOST_CHECK_EQUAL(imageWrapper.getBytesPerPixel(), 4); } } - -BOOST_AUTO_TEST_CASE(testImageReorderGLImageData) -{ - { - // clang-format off - char dataIn[] = - { - 1,1,1, 2,2,2, 3,3,3, 4,4,4, - 5,5,5, 6,6,6, 7,7,7, 8,8,8 - }; - char dataOut[] = - { - 5,5,5, 6,6,6, 7,7,7, 8,8,8, - 1,1,1, 2,2,2, 3,3,3, 4,4,4 - }; - // clang-format on - - deflect::ImageWrapper::swapYAxis(dataIn, 4, 2, 3); - - BOOST_CHECK_EQUAL_COLLECTIONS(dataIn, dataIn + 24, dataOut, - dataOut + 24); - } - - { - // clang-format off - char dataIn[] = - { - 1,1,1, 2,2,2, - 3,3,3, 4,4,4, - 5,5,5, 6,6,6, - 7,7,7, 8,8,8 - }; - char dataOut[] = - { - 7,7,7, 8,8,8, - 5,5,5, 6,6,6, - 3,3,3, 4,4,4, - 1,1,1, 2,2,2 - }; - // clang-format on - - deflect::ImageWrapper::swapYAxis(dataIn, 2, 4, 3); - - BOOST_CHECK_EQUAL_COLLECTIONS(dataIn, dataIn + 24, dataOut, - dataOut + 24); - } - - { - // clang-format off - char dataIn[] = - { - 1,1,1,2, 2,2,3,3, - 3,4,4,4, 5,5,5,6, - 6,6,7,7, 7,8,8,8 - }; - char dataOut[] = - { - 6,6,7,7, 7,8,8,8, - 3,4,4,4, 5,5,5,6, - 1,1,1,2, 2,2,3,3 - }; - // clang-format on - - deflect::ImageWrapper::swapYAxis(dataIn, 2, 3, 4); - - BOOST_CHECK_EQUAL_COLLECTIONS(dataIn, dataIn + 24, dataOut, - dataOut + 24); - } - - { - // clang-format off - char dataIn[] = - { - 1,1,1,2, 2,2,3,3, - 3,4,4,4, 5,5,5,6, - 6,6,7,7, 7,8,8,8 - }; - char dataOut[] = - { - 1,1,1,2, 2,2,3,3, - 3,4,4,4, 5,5,5,6, - 6,6,7,7, 7,8,8,8 - }; - // clang-format on - - deflect::ImageWrapper::swapYAxis(dataIn, 2, 3, 4); - deflect::ImageWrapper::swapYAxis(dataIn, 2, 3, 4); - - BOOST_CHECK_EQUAL_COLLECTIONS(dataIn, dataIn + 24, dataOut, - dataOut + 24); - } -} diff --git a/tests/cpp/ReceiveBufferTests.cpp b/tests/cpp/ReceiveBufferTests.cpp index e09a557..d8f8b7b 100644 --- a/tests/cpp/ReceiveBufferTests.cpp +++ b/tests/cpp/ReceiveBufferTests.cpp @@ -1,5 +1,5 @@ /*********************************************************************/ -/* Copyright (c) 2013-2017, EPFL/Blue Brain Project */ +/* Copyright (c) 2013-2018, EPFL/Blue Brain Project */ /* Raphael Dumusc */ /* All rights reserved. */ /* */ @@ -41,9 +41,8 @@ #include namespace ut = boost::unit_test; -#include -#include -#include +#include +#include inline std::ostream& operator<<(std::ostream& str, const QSize& s) { @@ -53,7 +52,7 @@ inline std::ostream& operator<<(std::ostream& str, const QSize& s) BOOST_AUTO_TEST_CASE(TestAddAndRemoveSources) { - deflect::ReceiveBuffer buffer; + deflect::server::ReceiveBuffer buffer; BOOST_REQUIRE_EQUAL(buffer.getSourceCount(), 0); @@ -77,7 +76,7 @@ BOOST_AUTO_TEST_CASE(TestAddAndRemoveSources) BOOST_AUTO_TEST_CASE(TestAllowedToSend) { - deflect::ReceiveBuffer buffer; + deflect::server::ReceiveBuffer buffer; BOOST_CHECK(!buffer.isAllowedToSend()); @@ -91,98 +90,98 @@ BOOST_AUTO_TEST_CASE(TestCompleteAFrame) { const size_t sourceIndex = 46; - deflect::ReceiveBuffer buffer; + deflect::server::ReceiveBuffer buffer; buffer.addSource(sourceIndex); - deflect::Segment segment; - segment.parameters.x = 0; - segment.parameters.y = 0; - segment.parameters.width = 128; - segment.parameters.height = 256; + deflect::server::Tile tile; + tile.x = 0; + tile.y = 0; + tile.width = 128; + tile.height = 256; - buffer.insert(segment, sourceIndex); + buffer.insert(tile, sourceIndex); BOOST_CHECK(!buffer.hasCompleteFrame()); buffer.finishFrameForSource(sourceIndex); BOOST_CHECK(buffer.hasCompleteFrame()); - deflect::Segments segments = buffer.popFrame(); + const auto tiles = buffer.popFrame(); - BOOST_CHECK_EQUAL(segments.size(), 1); + BOOST_CHECK_EQUAL(tiles.size(), 1); BOOST_CHECK(!buffer.hasCompleteFrame()); - deflect::Frame frame; - frame.segments = segments; + deflect::server::Frame frame; + frame.tiles = tiles; const QSize frameSize = frame.computeDimensions(); - BOOST_CHECK_EQUAL(frameSize.width(), segment.parameters.width); - BOOST_CHECK_EQUAL(frameSize.height(), segment.parameters.height); + BOOST_CHECK_EQUAL(frameSize.width(), tile.width); + BOOST_CHECK_EQUAL(frameSize.height(), tile.height); } -deflect::Segments generateTestSegments( +deflect::server::Tiles generateTestTiles( const deflect::View view = deflect::View::mono) { - deflect::Segments segments; - - deflect::Segment segment1; - segment1.parameters.x = 0; - segment1.parameters.y = 0; - segment1.parameters.width = 128; - segment1.parameters.height = 256; - segment1.view = view; - - deflect::Segment segment2; - segment2.parameters.x = 128; - segment2.parameters.y = 0; - segment2.parameters.width = 64; - segment2.parameters.height = 256; - segment2.view = view; - - deflect::Segment segment3; - segment3.parameters.x = 0; - segment3.parameters.y = 256; - segment3.parameters.width = 128; - segment3.parameters.height = 512; - segment3.view = view; - - deflect::Segment segment4; - segment4.parameters.x = 128; - segment4.parameters.y = 256; - segment4.parameters.width = 64; - segment4.parameters.height = 512; - segment4.view = view; - - segments.push_back(segment1); - segments.push_back(segment2); - segments.push_back(segment3); - segments.push_back(segment4); - - return segments; + deflect::server::Tiles tiles; + + deflect::server::Tile tile1; + tile1.x = 0; + tile1.y = 0; + tile1.width = 128; + tile1.height = 256; + tile1.view = view; + + deflect::server::Tile tile2; + tile2.x = 128; + tile2.y = 0; + tile2.width = 64; + tile2.height = 256; + tile2.view = view; + + deflect::server::Tile tile3; + tile3.x = 0; + tile3.y = 256; + tile3.width = 128; + tile3.height = 512; + tile3.view = view; + + deflect::server::Tile tile4; + tile4.x = 128; + tile4.y = 256; + tile4.width = 64; + tile4.height = 512; + tile4.view = view; + + tiles.push_back(tile1); + tiles.push_back(tile2); + tiles.push_back(tile3); + tiles.push_back(tile4); + + return tiles; } BOOST_AUTO_TEST_CASE(TestCompleteACompositeFrameSingleSource) { const size_t sourceIndex = 46; - deflect::ReceiveBuffer buffer; + deflect::server::ReceiveBuffer buffer; buffer.addSource(sourceIndex); - deflect::Segments testSegments = generateTestSegments(); + const auto testTiles = generateTestTiles(); - buffer.insert(testSegments[0], sourceIndex); - buffer.insert(testSegments[1], sourceIndex); - buffer.insert(testSegments[2], sourceIndex); - buffer.insert(testSegments[3], sourceIndex); + buffer.insert(testTiles[0], sourceIndex); + buffer.insert(testTiles[1], sourceIndex); + buffer.insert(testTiles[2], sourceIndex); + buffer.insert(testTiles[3], sourceIndex); BOOST_CHECK(!buffer.hasCompleteFrame()); buffer.finishFrameForSource(sourceIndex); BOOST_CHECK(buffer.hasCompleteFrame()); - deflect::Segments segments = buffer.popFrame(); - BOOST_CHECK_EQUAL(segments.size(), 4); + const auto tiles = buffer.popFrame(); + BOOST_CHECK_EQUAL(tiles.size(), 4); BOOST_CHECK(!buffer.hasCompleteFrame()); - deflect::Frame frame; - frame.segments = segments; + deflect::server::Frame frame; + frame.tiles = tiles; BOOST_CHECK_EQUAL(frame.computeDimensions(), QSize(192, 768)); } @@ -192,16 +191,16 @@ BOOST_AUTO_TEST_CASE(TestCompleteACompositeFrameMultipleSources) const size_t sourceIndex2 = 819; const size_t sourceIndex3 = 11; - deflect::ReceiveBuffer buffer; + deflect::server::ReceiveBuffer buffer; buffer.addSource(sourceIndex1); buffer.addSource(sourceIndex2); buffer.addSource(sourceIndex3); - deflect::Segments testSegments = generateTestSegments(); + const auto testTiles = generateTestTiles(); - buffer.insert(testSegments[0], sourceIndex1); - buffer.insert(testSegments[1], sourceIndex2); - buffer.insert(testSegments[2], sourceIndex3); + buffer.insert(testTiles[0], sourceIndex1); + buffer.insert(testTiles[1], sourceIndex2); + buffer.insert(testTiles[2], sourceIndex3); BOOST_CHECK(!buffer.hasCompleteFrame()); buffer.finishFrameForSource(sourceIndex1); @@ -210,16 +209,16 @@ BOOST_AUTO_TEST_CASE(TestCompleteACompositeFrameMultipleSources) buffer.finishFrameForSource(sourceIndex2); BOOST_CHECK(!buffer.hasCompleteFrame()); - buffer.insert(testSegments[3], sourceIndex3); + buffer.insert(testTiles[3], sourceIndex3); buffer.finishFrameForSource(sourceIndex3); BOOST_CHECK(buffer.hasCompleteFrame()); - deflect::Segments segments = buffer.popFrame(); - BOOST_CHECK_EQUAL(segments.size(), 4); + const auto tiles = buffer.popFrame(); + BOOST_CHECK_EQUAL(tiles.size(), 4); BOOST_CHECK(!buffer.hasCompleteFrame()); - deflect::Frame frame; - frame.segments = segments; + deflect::server::Frame frame; + frame.tiles = tiles; BOOST_CHECK_EQUAL(frame.computeDimensions(), QSize(192, 768)); } @@ -228,106 +227,106 @@ BOOST_AUTO_TEST_CASE(TestRemoveSourceWhileStreaming) const size_t sourceIndex1 = 46; const size_t sourceIndex2 = 819; - deflect::ReceiveBuffer buffer; + deflect::server::ReceiveBuffer buffer; buffer.addSource(sourceIndex1); buffer.addSource(sourceIndex2); - deflect::Segments testSegments = generateTestSegments(); + const auto testTiles = generateTestTiles(); // First Frame - 2 sources - buffer.insert(testSegments[0], sourceIndex1); - buffer.insert(testSegments[1], sourceIndex1); - buffer.insert(testSegments[2], sourceIndex2); - buffer.insert(testSegments[3], sourceIndex2); + buffer.insert(testTiles[0], sourceIndex1); + buffer.insert(testTiles[1], sourceIndex1); + buffer.insert(testTiles[2], sourceIndex2); + buffer.insert(testTiles[3], sourceIndex2); BOOST_CHECK(!buffer.hasCompleteFrame()); buffer.finishFrameForSource(sourceIndex1); BOOST_CHECK(!buffer.hasCompleteFrame()); buffer.finishFrameForSource(sourceIndex2); BOOST_CHECK(buffer.hasCompleteFrame()); - deflect::Segments segments = buffer.popFrame(); + auto tiles = buffer.popFrame(); - BOOST_CHECK_EQUAL(segments.size(), 4); + BOOST_CHECK_EQUAL(tiles.size(), 4); BOOST_CHECK(!buffer.hasCompleteFrame()); // Second frame - 1 source buffer.removeSource(sourceIndex2); - buffer.insert(testSegments[0], sourceIndex1); - buffer.insert(testSegments[1], sourceIndex1); + buffer.insert(testTiles[0], sourceIndex1); + buffer.insert(testTiles[1], sourceIndex1); BOOST_CHECK(!buffer.hasCompleteFrame()); buffer.finishFrameForSource(sourceIndex1); BOOST_CHECK(buffer.hasCompleteFrame()); - segments = buffer.popFrame(); - BOOST_CHECK_EQUAL(segments.size(), 2); + tiles = buffer.popFrame(); + BOOST_CHECK_EQUAL(tiles.size(), 2); BOOST_CHECK(!buffer.hasCompleteFrame()); - deflect::Frame frame; - frame.segments = segments; + deflect::server::Frame frame; + frame.tiles = tiles; BOOST_CHECK_EQUAL(frame.computeDimensions(), QSize(192, 256)); } -BOOST_AUTO_TEST_CASE(TestOneOfTwoSourceStopsSendingSegments) +BOOST_AUTO_TEST_CASE(TestOneOfTwoSourceStopsSendingTiles) { const size_t sourceIndex1 = 46; const size_t sourceIndex2 = 819; - deflect::ReceiveBuffer buffer; + deflect::server::ReceiveBuffer buffer; buffer.addSource(sourceIndex1); buffer.addSource(sourceIndex2); - deflect::Segments testSegments = generateTestSegments(); + const auto testTiles = generateTestTiles(); // First Frame - 2 sources - buffer.insert(testSegments[0], sourceIndex1); - buffer.insert(testSegments[1], sourceIndex1); - buffer.insert(testSegments[2], sourceIndex2); - buffer.insert(testSegments[3], sourceIndex2); + buffer.insert(testTiles[0], sourceIndex1); + buffer.insert(testTiles[1], sourceIndex1); + buffer.insert(testTiles[2], sourceIndex2); + buffer.insert(testTiles[3], sourceIndex2); BOOST_CHECK(!buffer.hasCompleteFrame()); buffer.finishFrameForSource(sourceIndex1); BOOST_CHECK(!buffer.hasCompleteFrame()); buffer.finishFrameForSource(sourceIndex2); BOOST_CHECK(buffer.hasCompleteFrame()); - deflect::Segments segments = buffer.popFrame(); + const auto tiles = buffer.popFrame(); - BOOST_CHECK_EQUAL(segments.size(), 4); + BOOST_CHECK_EQUAL(tiles.size(), 4); BOOST_CHECK(!buffer.hasCompleteFrame()); - // Next frames - one source stops sending segments + // Next frames - one source stops sending tiles for (int i = 0; i < 150; ++i) { - buffer.insert(testSegments[0], sourceIndex1); - buffer.insert(testSegments[1], sourceIndex1); + buffer.insert(testTiles[0], sourceIndex1); + buffer.insert(testTiles[1], sourceIndex1); BOOST_REQUIRE_NO_THROW(buffer.finishFrameForSource(sourceIndex1)); BOOST_REQUIRE(!buffer.hasCompleteFrame()); } // Test buffer exceeds maximum allowed size - buffer.insert(testSegments[0], sourceIndex1); - buffer.insert(testSegments[1], sourceIndex1); + buffer.insert(testTiles[0], sourceIndex1); + buffer.insert(testTiles[1], sourceIndex1); BOOST_CHECK_THROW(buffer.finishFrameForSource(sourceIndex1), std::runtime_error); } -void _insert(deflect::ReceiveBuffer& buffer, const size_t sourceIndex, - const deflect::Segments& frame) +void _insert(deflect::server::ReceiveBuffer& buffer, const size_t sourceIndex, + const deflect::server::Tiles& frame) { - for (const auto& segment : frame) - buffer.insert(segment, sourceIndex); + for (const auto& tile : frame) + buffer.insert(tile, sourceIndex); } -void _testStereoBuffer(deflect::ReceiveBuffer& buffer) +void _testStereoBuffer(deflect::server::ReceiveBuffer& buffer) { - const auto segments = buffer.popFrame(); - BOOST_CHECK_EQUAL(segments.size(), 8); + const auto tiles = buffer.popFrame(); + BOOST_CHECK_EQUAL(tiles.size(), 8); BOOST_CHECK(!buffer.hasCompleteFrame()); size_t left = 0; size_t right = 0; - for (const auto segment : segments) + for (const auto tile : tiles) { - switch (segment.view) + switch (tile.view) { case deflect::View::left_eye: ++left; @@ -343,8 +342,8 @@ void _testStereoBuffer(deflect::ReceiveBuffer& buffer) BOOST_CHECK_EQUAL(left, 4); BOOST_CHECK_EQUAL(right, 4); - deflect::Frame frame; - frame.segments = segments; + deflect::server::Frame frame; + frame.tiles = tiles; BOOST_CHECK_EQUAL(frame.computeDimensions(), QSize(192, 768)); } @@ -352,15 +351,15 @@ BOOST_AUTO_TEST_CASE(TestStereoOneSource) { const size_t sourceIndex = 46; - deflect::ReceiveBuffer buffer; + deflect::server::ReceiveBuffer buffer; buffer.addSource(sourceIndex); - auto leftSegments = generateTestSegments(deflect::View::left_eye); - _insert(buffer, sourceIndex, leftSegments); + auto leftTiles = generateTestTiles(deflect::View::left_eye); + _insert(buffer, sourceIndex, leftTiles); BOOST_CHECK(!buffer.hasCompleteFrame()); - auto rightSegments = generateTestSegments(deflect::View::right_eye); - _insert(buffer, sourceIndex, rightSegments); + auto rightTiles = generateTestTiles(deflect::View::right_eye); + _insert(buffer, sourceIndex, rightTiles); BOOST_CHECK(!buffer.hasCompleteFrame()); buffer.finishFrameForSource(sourceIndex); @@ -374,22 +373,22 @@ BOOST_AUTO_TEST_CASE(TestStereoTwoSourcesScreenSpaceSplit) const size_t sourceIndex1 = 46; const size_t sourceIndex2 = 819; - deflect::ReceiveBuffer buffer; + deflect::server::ReceiveBuffer buffer; buffer.addSource(sourceIndex1); buffer.addSource(sourceIndex2); - const auto leftSegments = generateTestSegments(deflect::View::left_eye); - const auto rightSegments = generateTestSegments(deflect::View::right_eye); + const auto leftTiles = generateTestTiles(deflect::View::left_eye); + const auto rightTiles = generateTestTiles(deflect::View::right_eye); - _insert(buffer, sourceIndex1, {leftSegments[0], leftSegments[1]}); + _insert(buffer, sourceIndex1, {leftTiles[0], leftTiles[1]}); BOOST_CHECK(!buffer.hasCompleteFrame()); - _insert(buffer, sourceIndex1, {rightSegments[0], rightSegments[1]}); + _insert(buffer, sourceIndex1, {rightTiles[0], rightTiles[1]}); BOOST_CHECK(!buffer.hasCompleteFrame()); - _insert(buffer, sourceIndex2, {leftSegments[2], leftSegments[3]}); + _insert(buffer, sourceIndex2, {leftTiles[2], leftTiles[3]}); BOOST_CHECK(!buffer.hasCompleteFrame()); - _insert(buffer, sourceIndex2, {rightSegments[2], rightSegments[3]}); + _insert(buffer, sourceIndex2, {rightTiles[2], rightTiles[3]}); BOOST_CHECK(!buffer.hasCompleteFrame()); buffer.finishFrameForSource(sourceIndex1); @@ -406,16 +405,16 @@ BOOST_AUTO_TEST_CASE(TestStereoTwoSourcesStereoSplit) const size_t sourceIndex1 = 46; const size_t sourceIndex2 = 819; - deflect::ReceiveBuffer buffer; + deflect::server::ReceiveBuffer buffer; buffer.addSource(sourceIndex1); buffer.addSource(sourceIndex2); - const auto leftSegments = generateTestSegments(deflect::View::left_eye); - const auto rightSegments = generateTestSegments(deflect::View::right_eye); + const auto leftTiles = generateTestTiles(deflect::View::left_eye); + const auto rightTiles = generateTestTiles(deflect::View::right_eye); - _insert(buffer, sourceIndex1, leftSegments); + _insert(buffer, sourceIndex1, leftTiles); BOOST_CHECK(!buffer.hasCompleteFrame()); - _insert(buffer, sourceIndex2, rightSegments); + _insert(buffer, sourceIndex2, rightTiles); BOOST_CHECK(!buffer.hasCompleteFrame()); buffer.finishFrameForSource(sourceIndex1); @@ -434,25 +433,25 @@ BOOST_AUTO_TEST_CASE(TestStereoFourSourcesScreenSpaceAndStereoSplit) const size_t sourceIndex3 = 489; const size_t sourceIndex4 = 113; - deflect::ReceiveBuffer buffer; + deflect::server::ReceiveBuffer buffer; buffer.addSource(sourceIndex1); buffer.addSource(sourceIndex2); buffer.addSource(sourceIndex3); buffer.addSource(sourceIndex4); - const auto leftSegments = generateTestSegments(deflect::View::left_eye); - const auto rightSegments = generateTestSegments(deflect::View::right_eye); + const auto leftTiles = generateTestTiles(deflect::View::left_eye); + const auto rightTiles = generateTestTiles(deflect::View::right_eye); - _insert(buffer, sourceIndex1, {leftSegments[0], leftSegments[1]}); + _insert(buffer, sourceIndex1, {leftTiles[0], leftTiles[1]}); BOOST_CHECK(!buffer.hasCompleteFrame()); - _insert(buffer, sourceIndex2, {rightSegments[0], rightSegments[1]}); + _insert(buffer, sourceIndex2, {rightTiles[0], rightTiles[1]}); BOOST_CHECK(!buffer.hasCompleteFrame()); - _insert(buffer, sourceIndex3, {leftSegments[2], leftSegments[3]}); + _insert(buffer, sourceIndex3, {leftTiles[2], leftTiles[3]}); BOOST_CHECK(!buffer.hasCompleteFrame()); - _insert(buffer, sourceIndex4, {rightSegments[2], rightSegments[3]}); + _insert(buffer, sourceIndex4, {rightTiles[2], rightTiles[3]}); BOOST_CHECK(!buffer.hasCompleteFrame()); buffer.finishFrameForSource(sourceIndex1); diff --git a/tests/cpp/ServerTests.cpp b/tests/cpp/ServerTests.cpp index b3dbdab..f0d3202 100644 --- a/tests/cpp/ServerTests.cpp +++ b/tests/cpp/ServerTests.cpp @@ -45,8 +45,8 @@ namespace ut = boost::unit_test; #include "MinimalGlobalQtApp.h" #include "boost_test_thread_safe.h" -#include #include +#include #include #include @@ -89,13 +89,15 @@ BOOST_AUTO_TEST_CASE(sizeHintsReceivedByServer) BOOST_AUTO_TEST_CASE(registerForEventReceivedByServer) { bool received = false; - setRegisterToEventsCallback([&](const QString id, const bool exclusive, - deflect::EventReceiver* eventReceiver) { - SAFE_BOOST_CHECK_EQUAL(id.toStdString(), testStreamId.toStdString()); - SAFE_BOOST_CHECK(exclusive); - SAFE_BOOST_CHECK(eventReceiver); - received = true; - }); + setRegisterToEventsCallback( + [&](const QString id, const bool exclusive, + deflect::server::EventReceiver* eventReceiver) { + SAFE_BOOST_CHECK_EQUAL(id.toStdString(), + testStreamId.toStdString()); + SAFE_BOOST_CHECK(exclusive); + SAFE_BOOST_CHECK(eventReceiver); + received = true; + }); { deflect::Stream stream(testStreamId.toStdString(), "localhost", @@ -136,8 +138,8 @@ BOOST_AUTO_TEST_CASE(dataReceivedByServer) BOOST_AUTO_TEST_CASE(oneObserverAndOneStream) { - setFrameReceivedCallback([&](deflect::FramePtr frame) { - SAFE_BOOST_CHECK_EQUAL(frame->segments.size(), 1); + setFrameReceivedCallback([&](deflect::server::FramePtr frame) { + SAFE_BOOST_CHECK_EQUAL(frame->tiles.size(), 1); SAFE_BOOST_CHECK_EQUAL(frame->uri.toStdString(), testStreamId.toStdString()); }); @@ -333,8 +335,8 @@ BOOST_AUTO_TEST_CASE(threadedSmallSegmentStream) std::ceil((float)width / segmentSize) * std::ceil((float)height / segmentSize)); - setFrameReceivedCallback([&](deflect::FramePtr frame) { - SAFE_BOOST_CHECK_EQUAL(frame->segments.size(), numSegments); + setFrameReceivedCallback([&](deflect::server::FramePtr frame) { + SAFE_BOOST_CHECK_EQUAL(frame->tiles.size(), numSegments); SAFE_BOOST_CHECK_EQUAL(frame->uri.toStdString(), testStreamId.toStdString()); const auto dim = frame->computeDimensions(); diff --git a/tests/cpp/SegmentDecoderTests.cpp b/tests/cpp/TileDecoderTests.cpp similarity index 80% rename from tests/cpp/SegmentDecoderTests.cpp rename to tests/cpp/TileDecoderTests.cpp index fb6adbd..9e3f3db 100644 --- a/tests/cpp/SegmentDecoderTests.cpp +++ b/tests/cpp/TileDecoderTests.cpp @@ -1,5 +1,5 @@ /*********************************************************************/ -/* Copyright (c) 2013-2017, EPFL/Blue Brain Project */ +/* Copyright (c) 2013-2018, EPFL/Blue Brain Project */ /* Raphael Dumusc */ /* All rights reserved. */ /* */ @@ -37,16 +37,17 @@ /* or implied, of The University of Texas at Austin. */ /*********************************************************************/ -#define BOOST_TEST_MODULE SegmentDecoderTests +#define BOOST_TEST_MODULE TileDecoderTests + #include namespace ut = boost::unit_test; #include -#include #include #include -#include -#include +#include +#include +#include #include #include // std::round @@ -77,9 +78,9 @@ const std::vector expectedVData(8 * 8, _toV(92, 28, 0)); namespace deflect { -inline std::ostream& operator<<(std::ostream& str, const DataType t) +inline std::ostream& operator<<(std::ostream& str, const Format t) { - str << "DataType(" << as_underlying_type(t) << ")"; + str << "Format(" << as_underlying_type(t) << ")"; return str; } inline std::ostream& operator<<(std::ostream& str, const ChromaSubsampling s) @@ -119,7 +120,7 @@ BOOST_AUTO_TEST_CASE(testImageCompressionAndDecompression) BOOST_REQUIRE(jpegData.size() != (int)data.size()); // Decompress image - deflect::ImageJpegDecompressor decompressor; + deflect::server::ImageJpegDecompressor decompressor; QByteArray decodedData = decompressor.decompress(jpegData); // Check decoded image in format RGBA @@ -136,29 +137,28 @@ BOOST_AUTO_TEST_CASE(testImageCompressionAndDecompression) QByteArray decodeToYUVWithDecompressor( const QByteArray& jpegData, const deflect::ChromaSubsampling expected) { - deflect::ImageJpegDecompressor decompressor; + deflect::server::ImageJpegDecompressor decompressor; const auto yuvData = decompressor.decompressToYUV(jpegData); BOOST_CHECK_EQUAL(yuvData.second, expected); return yuvData.first; } -QByteArray decodeToYUVWithSegmentDecoder( - const QByteArray& jpegData, const deflect::ChromaSubsampling expected) +QByteArray decodeToYUVWithTileDecoder(const QByteArray& jpegData, + const deflect::ChromaSubsampling expected) { - deflect::Segment segment; - segment.parameters.width = 8; - segment.parameters.height = 8; - segment.parameters.dataType = deflect::DataType::jpeg; - segment.imageData = - QByteArray::fromRawData(jpegData.data(), jpegData.size()); - - deflect::SegmentDecoder decoder; - BOOST_CHECK_EQUAL(decoder.decodeType(segment), expected); - - decoder.decodeToYUV(segment); - BOOST_CHECK_NE(segment.parameters.dataType, deflect::DataType::jpeg); - BOOST_CHECK_NE(segment.parameters.dataType, deflect::DataType::rgba); - return segment.imageData; + deflect::server::Tile tile; + tile.width = 8; + tile.height = 8; + tile.format = deflect::Format::jpeg; + tile.imageData = QByteArray::fromRawData(jpegData.data(), jpegData.size()); + + deflect::server::TileDecoder decoder; + BOOST_CHECK_EQUAL(decoder.decodeType(tile), expected); + + decoder.decodeToYUV(tile); + BOOST_CHECK_NE(tile.format, deflect::Format::jpeg); + BOOST_CHECK_NE(tile.format, deflect::Format::rgba); + return tile.imageData; } using DecodeFunc = @@ -223,11 +223,11 @@ BOOST_AUTO_TEST_CASE(testImageCompressionAndDecompressionYUV) &decodeToYUVWithDecompressor); testImageDecompressionToYUV(deflect::ChromaSubsampling::YUV444, - &decodeToYUVWithSegmentDecoder); + &decodeToYUVWithTileDecoder); testImageDecompressionToYUV(deflect::ChromaSubsampling::YUV422, - &decodeToYUVWithSegmentDecoder); + &decodeToYUVWithTileDecoder); testImageDecompressionToYUV(deflect::ChromaSubsampling::YUV420, - &decodeToYUVWithSegmentDecoder); + &decodeToYUVWithTileDecoder); } #endif @@ -258,39 +258,45 @@ BOOST_AUTO_TEST_CASE(testImageSegmentationWithCompressionAndDecompression) BOOST_REQUIRE_EQUAL(segments.size(), 1); deflect::Segment& segment = segments.front(); - BOOST_REQUIRE_EQUAL(segment.parameters.dataType, deflect::DataType::jpeg); + BOOST_REQUIRE_EQUAL(segment.parameters.format, deflect::Format::jpeg); BOOST_REQUIRE(segment.imageData.size() != (int)data.size()); // Decompress image - deflect::SegmentDecoder decoder; - decoder.startDecoding(segment); + deflect::server::Tile tile; + tile.width = segment.parameters.width; + tile.height = segment.parameters.height; + tile.format = segment.parameters.format; + tile.imageData = segment.imageData; + + deflect::server::TileDecoder decoder; + decoder.startDecoding(tile); decoder.waitDecoding(); // Check decoded image in format RGBA - BOOST_REQUIRE_EQUAL(segment.parameters.dataType, deflect::DataType::rgba); - BOOST_REQUIRE_EQUAL(segment.imageData.size(), data.size()); + BOOST_REQUIRE_EQUAL(tile.format, deflect::Format::rgba); + BOOST_REQUIRE_EQUAL(tile.imageData.size(), data.size()); - const char* dataOut = segment.imageData.constData(); + const char* dataOut = tile.imageData.constData(); BOOST_CHECK_EQUAL_COLLECTIONS(data.data(), - data.data() + segment.imageData.size(), - dataOut, dataOut + segment.imageData.size()); + data.data() + tile.imageData.size(), dataOut, + dataOut + tile.imageData.size()); } BOOST_AUTO_TEST_CASE(testDecompressionOfInvalidData) { const QByteArray invalidJpegData{"notjpeg923%^#8"}; - deflect::ImageJpegDecompressor decompressor; + deflect::server::ImageJpegDecompressor decompressor; BOOST_CHECK_THROW(decompressor.decompress(invalidJpegData), std::runtime_error); - deflect::Segment segment; - segment.parameters.width = 32; - segment.parameters.height = 32; - segment.imageData = invalidJpegData; + deflect::server::Tile tile; + tile.width = 32; + tile.height = 32; + tile.imageData = invalidJpegData; - deflect::SegmentDecoder decoder; - BOOST_CHECK_THROW(decoder.decode(segment), std::runtime_error); + deflect::server::TileDecoder decoder; + BOOST_CHECK_THROW(decoder.decode(tile), std::runtime_error); - BOOST_CHECK_NO_THROW(decoder.startDecoding(segment)); + BOOST_CHECK_NO_THROW(decoder.startDecoding(tile)); BOOST_CHECK_THROW(decoder.waitDecoding(), std::runtime_error); } diff --git a/tests/cpp/perf/benchmarkStreamer.cpp b/tests/cpp/perf/benchmarkStreamer.cpp index ab5c376..03a2155 100644 --- a/tests/cpp/perf/benchmarkStreamer.cpp +++ b/tests/cpp/perf/benchmarkStreamer.cpp @@ -211,13 +211,10 @@ class Application deflectImage.compressionPolicy = deflect::COMPRESSION_ON; deflectImage.compressionQuality = _options.quality; - static QMutex lock; - const auto appendHandler = [&](const deflect::Segment& segment) { - QMutexLocker locker(&lock); + const auto appendHandler = [this](const deflect::Segment& segment) { _jpegSegments.push_back(segment); return true; }; - return segmenter.generate(deflectImage, appendHandler); } diff --git a/tests/cpp/perf/streamTests.cpp b/tests/cpp/perf/streamTests.cpp index 41846d7..3d2ca14 100644 --- a/tests/cpp/perf/streamTests.cpp +++ b/tests/cpp/perf/streamTests.cpp @@ -44,8 +44,8 @@ namespace ut = boost::unit_test; #include "MinimalGlobalQtApp.h" #include "Timer.h" -#include #include +#include #include @@ -140,7 +140,7 @@ class DCThread : public QThread BOOST_AUTO_TEST_CASE(testSocketConnection) { - deflect::Server server; + deflect::server::Server server; #ifdef NTHREADS QThreadPool::globalInstance()->setMaxThreadCount(NTHREADS); #endif diff --git a/tests/mock/CMakeLists.txt b/tests/mock/CMakeLists.txt index 828c01a..1f5b8b6 100644 --- a/tests/mock/CMakeLists.txt +++ b/tests/mock/CMakeLists.txt @@ -1,7 +1,7 @@ -# Copyright (c) 2013-2017, EPFL/Blue Brain Project -# Daniel Nachbaur -# Raphael Dumusc +# Copyright (c) 2013-2018, EPFL/Blue Brain Project +# Daniel Nachbaur +# Raphael Dumusc # # Generates a mock library used by the cpp unit tests. @@ -21,11 +21,12 @@ set(DEFLECTMOCK_SOURCES MockServer.cpp ) -set(DEFLECTMOCK_LINK_LIBRARIES Deflect ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} - Qt5::Core Qt5::Network) +set(DEFLECTMOCK_LINK_LIBRARIES Deflect DeflectServer Qt5::Core Qt5::Network + ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) set(DEFLECTMOCK_INCLUDE_NAME deflect/mock) set(DEFLECTMOCK_OMIT_LIBRARY_HEADER ON) +set(DEFLECTMOCK_OMIT_VERSION_HEADERS ON) set(DEFLECTMOCK_OMIT_CHECK_TARGETS ON) set(DEFLECTMOCK_OMIT_INSTALL ON) common_library(DeflectMock) diff --git a/tests/mock/DeflectServer.cpp b/tests/mock/DeflectServer.cpp index 3b7ce76..fa37a1e 100644 --- a/tests/mock/DeflectServer.cpp +++ b/tests/mock/DeflectServer.cpp @@ -43,13 +43,13 @@ DeflectServer::DeflectServer() { - _server = new deflect::Server(0 /* OS-chosen port */); + _server = new deflect::server::Server(0 /* OS-chosen port */); _server->moveToThread(&_thread); _thread.connect(&_thread, &QThread::finished, _server, - &deflect::Server::deleteLater); + &deflect::server::Server::deleteLater); _thread.start(); - _server->connect(_server, &deflect::Server::pixelStreamOpened, + _server->connect(_server, &deflect::server::Server::pixelStreamOpened, [&](const QString) { ++_openedStreams; _mutex.lock(); @@ -58,7 +58,7 @@ DeflectServer::DeflectServer() _mutex.unlock(); }); - _server->connect(_server, &deflect::Server::pixelStreamClosed, + _server->connect(_server, &deflect::server::Server::pixelStreamClosed, [&](const QString) { --_openedStreams; _mutex.lock(); @@ -67,7 +67,7 @@ DeflectServer::DeflectServer() _mutex.unlock(); }); - _server->connect(_server, &deflect::Server::receivedSizeHints, + _server->connect(_server, &deflect::server::Server::receivedSizeHints, [&](const QString id, const deflect::SizeHints hints) { if (_sizeHintsCallback) _sizeHintsCallback(id, hints); @@ -77,7 +77,7 @@ DeflectServer::DeflectServer() _mutex.unlock(); }); - _server->connect(_server, &deflect::Server::receivedData, + _server->connect(_server, &deflect::server::Server::receivedData, [&](const QString id, QByteArray data) { if (_dataReceivedCallback) _dataReceivedCallback(id, data); @@ -87,8 +87,8 @@ DeflectServer::DeflectServer() _mutex.unlock(); }); - _server->connect(_server, &deflect::Server::receivedFrame, - [&](deflect::FramePtr frame) { + _server->connect(_server, &deflect::server::Server::receivedFrame, + [&](deflect::server::FramePtr frame) { if (_frameReceivedCallback) _frameReceivedCallback(frame); ++_receivedFrames; @@ -98,10 +98,10 @@ DeflectServer::DeflectServer() _mutex.unlock(); }); - _server->connect(_server, &deflect::Server::registerToEvents, + _server->connect(_server, &deflect::server::Server::registerToEvents, [&](const QString id, const bool exclusive, - deflect::EventReceiver* evtReceiver, - deflect::BoolPromisePtr success) { + deflect::server::EventReceiver* evtReceiver, + deflect::server::BoolPromisePtr success) { if (_registerToEventsCallback) _registerToEventsCallback(id, exclusive, diff --git a/tests/mock/DeflectServer.h b/tests/mock/DeflectServer.h index 62ef3ab..6273386 100644 --- a/tests/mock/DeflectServer.h +++ b/tests/mock/DeflectServer.h @@ -48,8 +48,8 @@ typedef __int32 int32_t; #include #include -#include -#include +#include +#include class DeflectServer { @@ -57,7 +57,7 @@ class DeflectServer explicit DeflectServer(); ~DeflectServer(); - quint16 serverPort() const { return _server->serverPort(); } + quint16 serverPort() const { return _server->getPort(); } void requestFrame(QString uri) { _server->requestFrame(uri); } void waitForMessage(); @@ -71,7 +71,8 @@ class DeflectServer } using RegisterToEventsCallback = - std::function; + std::function; void setRegisterToEventsCallback(const RegisterToEventsCallback& callback) { _registerToEventsCallback = callback; @@ -83,7 +84,8 @@ class DeflectServer _dataReceivedCallback = callback; } - using FrameReceivedCallback = std::function; + using FrameReceivedCallback = + std::function; void setFrameReceivedCallback(const FrameReceivedCallback& callback) { _frameReceivedCallback = callback; @@ -93,7 +95,8 @@ class DeflectServer private: QThread _thread; - deflect::Server* _server; + // destroyed by Object::deleteLater + deflect::server::Server* _server = nullptr; bool _receivedState{false}; QWaitCondition _received; @@ -107,7 +110,7 @@ class DeflectServer DataReceivedCallback _dataReceivedCallback; FrameReceivedCallback _frameReceivedCallback; - deflect::EventReceiver* _eventReceiver{nullptr}; + deflect::server::EventReceiver* _eventReceiver{nullptr}; }; #endif diff --git a/tests/mock/FrameUtils.h b/tests/mock/FrameUtils.h index 36a51d6..ff41485 100644 --- a/tests/mock/FrameUtils.h +++ b/tests/mock/FrameUtils.h @@ -1,6 +1,6 @@ /*********************************************************************/ -/* Copyright (c) 2017, EPFL/Blue Brain Project */ -/* Raphael Dumusc */ +/* Copyright (c) 2017-2018, EPFL/Blue Brain Project */ +/* Raphael Dumusc */ /* All rights reserved. */ /* */ /* Redistribution and use in source and binary forms, with or */ @@ -37,57 +37,55 @@ /* or implied, of The University of Texas at Austin. */ /*********************************************************************/ -#include +#include #include // std::ceil -inline deflect::Frame makeTestFrame(int width, int height, int segmentSize) +inline deflect::server::Frame makeTestFrame(int width, int height, int tileSize) { - const int numSegmentsX = std::ceil((float)width / (float)segmentSize); - const int numSegmentsY = std::ceil((float)height / (float)segmentSize); + const int numTilesX = std::ceil((float)width / (float)tileSize); + const int numTilesY = std::ceil((float)height / (float)tileSize); - const int lastSegmentWidth = - (width % segmentSize) > 0 ? (width % segmentSize) : segmentSize; - const int lastSegmentHeight = - (height % segmentSize) > 0 ? (height % segmentSize) : segmentSize; + const int lastTileWidth = + (width % tileSize) > 0 ? (width % tileSize) : tileSize; + const int lastTileHeight = + (height % tileSize) > 0 ? (height % tileSize) : tileSize; - deflect::Frame frame; - for (int y = 0; y < numSegmentsY; ++y) + deflect::server::Frame frame; + for (int y = 0; y < numTilesY; ++y) { - for (int x = 0; x < numSegmentsX; ++x) + for (int x = 0; x < numTilesX; ++x) { - deflect::Segment segment; - segment.parameters.x = x * segmentSize; - segment.parameters.y = y * segmentSize; - segment.parameters.width = segmentSize; - segment.parameters.height = segmentSize; - if (x == numSegmentsX - 1) - segment.parameters.width = lastSegmentWidth; - if (y == numSegmentsY - 1) - segment.parameters.height = lastSegmentHeight; - frame.segments.push_back(segment); + deflect::server::Tile tile; + tile.x = x * tileSize; + tile.y = y * tileSize; + tile.width = tileSize; + tile.height = tileSize; + if (x == numTilesX - 1) + tile.width = lastTileWidth; + if (y == numTilesY - 1) + tile.height = lastTileHeight; + frame.tiles.push_back(tile); } } return frame; } -inline void compare(const deflect::Frame& frame1, const deflect::Frame& frame2) +inline void compare(const deflect::server::Frame& frame1, + const deflect::server::Frame& frame2) { - BOOST_REQUIRE_EQUAL(frame1.segments.size(), frame2.segments.size()); + BOOST_REQUIRE_EQUAL(frame1.tiles.size(), frame2.tiles.size()); - for (size_t i = 0; i < frame1.segments.size(); ++i) + for (size_t i = 0; i < frame1.tiles.size(); ++i) { - const auto& s1 = frame1.segments[i]; - const auto& s2 = frame2.segments[i]; - BOOST_CHECK(s1.view == s2.view); - BOOST_CHECK(s1.rowOrder == s2.rowOrder); - - const auto& p1 = s1.parameters; - const auto& p2 = s2.parameters; - BOOST_CHECK_EQUAL(p1.x, p2.x); - BOOST_CHECK_EQUAL(p1.y, p2.y); - BOOST_CHECK_EQUAL(p1.width, p2.width); - BOOST_CHECK_EQUAL(p1.height, p2.height); + const auto& t1 = frame1.tiles[i]; + const auto& t2 = frame2.tiles[i]; + BOOST_CHECK(t1.view == t2.view); + BOOST_CHECK(t1.rowOrder == t2.rowOrder); + BOOST_CHECK_EQUAL(t1.x, t2.x); + BOOST_CHECK_EQUAL(t1.y, t2.y); + BOOST_CHECK_EQUAL(t1.width, t2.width); + BOOST_CHECK_EQUAL(t1.height, t2.height); } } diff --git a/tests/mock/MockServer.h b/tests/mock/MockServer.h index 8b72596..1ca2ee6 100644 --- a/tests/mock/MockServer.h +++ b/tests/mock/MockServer.h @@ -44,8 +44,7 @@ typedef __int32 int32_t; #endif -#include -#include +#include #include