Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions deflect/ImageJpegCompressor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include "ImageWrapper.h"

#include <iostream>
#include <sstream>

namespace deflect
{
Expand Down Expand Up @@ -72,7 +73,8 @@ int _getTurboJpegFormat(const PixelFormat pixelFormat)
case ABGR:
return TJPF_XBGR;
default:
std::cerr << "unknown pixel format" << std::endl;
throw std::invalid_argument("unknown pixel format " +
std::to_string((int)pixelFormat));
return TJPF_RGB;
}
}
Expand All @@ -88,7 +90,8 @@ int _getTurboJpegSubsamp(const ChromaSubsampling subsampling)
case ChromaSubsampling::YUV420:
return TJSAMP_420;
default:
std::cerr << "unknown subsampling format" << std::endl;
throw std::invalid_argument("unknown subsampling format " +
std::to_string((int)subsampling));
return TJSAMP_444;
}
}
Expand All @@ -100,6 +103,10 @@ QByteArray ImageJpegCompressor::computeJpeg(const ImageWrapper& sourceImage,
// though it does not modify it. It can "safely" be cast to non-const
// pointer to comply with the incorrect API.
unsigned char* tjSrcBuffer = (unsigned char*)sourceImage.data;
if (!tjSrcBuffer)
throw std::invalid_argument(
"libjpeg-turbo image conversion failure: source image is NULL");

tjSrcBuffer +=
imageRegion.y() * sourceImage.width * sourceImage.getBytesPerPixel();
tjSrcBuffer += imageRegion.x() * sourceImage.getBytesPerPixel();
Expand All @@ -124,8 +131,9 @@ QByteArray ImageJpegCompressor::computeJpeg(const ImageWrapper& sourceImage,
tjJpegQual, tjFlags);
if (err != 0)
{
std::cerr << "libjpeg-turbo image conversion failure" << std::endl;
return QByteArray();
std::stringstream msg;
msg << "libjpeg-turbo image conversion failure: " << tjGetErrorStr();
throw std::runtime_error(msg.str());
}

return QByteArray((const char*)ptr, tjJpegSize);
Expand Down
3 changes: 3 additions & 0 deletions deflect/ImageJpegCompressor.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ class ImageJpegCompressor
* @param sourceImage The source image containing uncompressed image data.
* @param imageRegion The region of the image to be compressed. Must not
* exceed image dimensions.
* @return compressed image
* @throw std::invalid_argument if sourceImage.data is nullptr
* @throw std::runtime_error if JPEG compression failed
*/
DEFLECT_API QByteArray computeJpeg(const ImageWrapper& sourceImage,
const QRect& imageRegion);
Expand Down
48 changes: 38 additions & 10 deletions deflect/ImageSegmenter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,19 +69,37 @@ bool ImageSegmenter::generate(const ImageWrapper& image, const Handler& handler)
return _generateRaw(image, handler);
}

Segment ImageSegmenter::compressSingleSegment(const ImageWrapper& image)
Segment ImageSegmenter::createSingleSegment(const ImageWrapper& image)
{
#ifdef DEFLECT_USE_LIBJPEGTURBO
auto segments = _generateSegments(image);
if (segments.size() > 1)
throw std::runtime_error(
"compressSingleSegment only works for small images");
_computeJpeg(segments[0], false);
return segments[0];
"createSingleSegment only works for small images");

auto& segment = segments[0];

if (image.compressionPolicy == COMPRESSION_OFF)
{
segment.imageData.reserve(segment.parameters.width *
segment.parameters.height *
image.getBytesPerPixel());
segment.parameters.dataType = DataType::rgba;
segment.imageData.append((const char*)image.data,
int(image.getBufferSize()));
}
else
{
#ifdef DEFLECT_USE_LIBJPEGTURBO
_computeJpeg(segment, false);
if (segment.exception)
std::rethrow_exception(segment.exception);
#else
throw std::runtime_error(
"LibJpegTurbo not available, needed for compressSingleSegment");
throw std::runtime_error(
"LibJpegTurbo not available, needed for createSingleSegment");
#endif
}

return segment;
}

void ImageSegmenter::setNominalSegmentDimensions(const uint width,
Expand Down Expand Up @@ -136,8 +154,17 @@ void ImageSegmenter::_computeJpeg(Segment& segment, const bool sendSegment)
// turbojpeg handles need to be per thread, and this function is called from
// multiple threads by QtConcurrent::map
static QThreadStorage<ImageJpegCompressor> compressor;
segment.imageData =
compressor.localData().computeJpeg(*segment.sourceImage, imageRegion);
try
{
segment.imageData =
compressor.localData().computeJpeg(*segment.sourceImage,
imageRegion);
}
catch (...)
{
segment.exception = std::current_exception();
}

segment.parameters.dataType = DataType::jpeg;
if (sendSegment)
_sendQueue.enqueue(segment);
Expand Down Expand Up @@ -205,7 +232,8 @@ Segments ImageSegmenter::_generateSegments(const ImageWrapper& image) const
if (image.view == View::side_by_side)
{
if (image.width % 2 != 0)
throw std::runtime_error("side_by_side image width must be even!");
throw std::invalid_argument(
"side_by_side image width must be even!");

// create copy of segments for right view
auto segmentsRight = segments;
Expand Down
5 changes: 4 additions & 1 deletion deflect/ImageSegmenter.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,12 @@ class ImageSegmenter
*
* @param image The image to be compressed
* @return the compressed segment
* @throw std::invalid_argument if image is too big or invalid JPEG
* compression arguments
* @throw std::runtime_error if JPEG compression failed
* @threadsafe
*/
DEFLECT_API Segment compressSingleSegment(const ImageWrapper& image);
DEFLECT_API Segment createSingleSegment(const ImageWrapper& image);

private:
struct SegmentationInfo
Expand Down
2 changes: 1 addition & 1 deletion deflect/ImageWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ struct ImageWrapper
//@{
CompressionPolicy compressionPolicy; /**< Is the image to be compressed
(default: auto). @version 1.0 */
unsigned int compressionQuality; /**< Compression quality (0 worst,
unsigned int compressionQuality; /**< Compression quality (1 worst,
100 best, default: 75).
@version 1.0 */
ChromaSubsampling subsampling; /**< Chrominance sub-sampling.
Expand Down
19 changes: 17 additions & 2 deletions deflect/Observer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
/*********************************************************************/

#include "Observer.h"
#include "NetworkProtocol.h"
#include "StreamPrivate.h"

#include <QDataStream>
Expand All @@ -49,8 +50,10 @@

namespace deflect
{
Observer::Observer()
: _impl(new StreamPrivate("", "", Socket::defaultPortNumber, true))
const unsigned short Observer::defaultPortNumber = DEFAULT_PORT_NUMBER;

Observer::Observer(const unsigned short port)
: _impl(new StreamPrivate("", "", port, true))
{
}

Expand Down Expand Up @@ -169,4 +172,16 @@ void Observer::setDisconnectedCallback(const std::function<void()> callback)
{
_impl->disconnectedCallback = callback;
}

void Observer::sendSizeHints(const SizeHints& hints)
{
_impl->sendWorker.enqueueSizeHints(hints);
}

bool Observer::sendData(const char* data, const size_t count)
{
return _impl->sendWorker
.enqueueData(QByteArray::fromRawData(data, int(count)))
.get();
}
}
35 changes: 31 additions & 4 deletions deflect/Observer.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,16 +69,21 @@ class StreamPrivate;
class Observer
{
public:
/** The default communication port */
static const unsigned short defaultPortNumber;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use this constant also in the 2 constructors with params (Observer + Stream)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


/**
* Open a new connection to the Server using environment variables.
*
* DEFLECT_HOST The address of the target Server instance (required).
* DEFLECT_ID The identifier for the stream. If not provided, a random
* unique identifier will be used.
* @throw std::runtime_error if DEFLECT_HOST was not provided.
* @param port Port of the Server instance, default 1701.
* @throw std::runtime_error if DEFLECT_HOST was not provided or no
* connection to server could be established
* @version 1.3
*/
DEFLECT_API Observer();
DEFLECT_API explicit Observer(unsigned short port = defaultPortNumber);

/**
* Open a new connection to the Server.
Expand All @@ -97,11 +102,12 @@ class Observer
* "192.168.1.83". If left empty, the environment variable
* DEFLECT_HOST will be used instead.
* @param port Port of the Server instance, default 1701.
* @throw std::runtime_error if no host was provided.
* @throw std::runtime_error if no host was provided or no
* connection to server could be established
* @version 1.0
*/
DEFLECT_API Observer(const std::string& id, const std::string& host,
unsigned short port = 1701);
unsigned short port = defaultPortNumber);

/** Destruct the Observer, closing the connection. @version 1.0 */
DEFLECT_API virtual ~Observer();
Expand Down Expand Up @@ -199,6 +205,27 @@ class Observer
*/
DEFLECT_API void setDisconnectedCallback(std::function<void()> callback);

/**
* Send size hints to the stream server to indicate sizes that should be
* respected by resize operations on the server side.
*
* @note blocks until all pending asynchonous send operations are finished.
* @param hints the new size hints for the server
* @version 1.2
*/
DEFLECT_API void sendSizeHints(const SizeHints& hints);

/**
* Send data to the Server.
*
* @note blocks until all pending asynchonous send operations are finished.
* @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
*/
DEFLECT_API bool sendData(const char* data, size_t count);

protected:
Observer(const Observer&) = delete;
const Observer& operator=(const Observer&) = delete;
Expand Down
3 changes: 3 additions & 0 deletions deflect/Segment.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ struct Segment

/** @internal raw, uncompressed source image, used for compression */
const ImageWrapper* sourceImage = nullptr;

/** @internal holds potential exception from compression thread */
std::exception_ptr exception;
};
}

Expand Down
2 changes: 0 additions & 2 deletions deflect/Socket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,6 @@ const int RECEIVE_TIMEOUT_MS = 1000;

namespace deflect
{
const unsigned short Socket::defaultPortNumber = DEFAULT_PORT_NUMBER;

Socket::Socket(const std::string& host, const unsigned short port)
: _host(host)
, _socket(new QTcpSocket(this)) // Ensure that _socket parent is
Expand Down
6 changes: 1 addition & 5 deletions deflect/Socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,12 @@ class Socket : public QObject
Q_OBJECT

public:
/** The default communication port */
static const unsigned short defaultPortNumber;

/**
* Construct a Socket and connect to host.
* @param host The target host (IP address or hostname)
* @param port The target port
*/
DEFLECT_API Socket(const std::string& host,
unsigned short port = defaultPortNumber);
DEFLECT_API Socket(const std::string& host, unsigned short port);

/** Destruct a Socket, disconnecting from host. */
DEFLECT_API ~Socket() = default;
Expand Down
16 changes: 2 additions & 14 deletions deflect/Stream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@

namespace deflect
{
Stream::Stream()
: Observer(new StreamPrivate("", "", Socket::defaultPortNumber, false))
Stream::Stream(const unsigned short port)
: Observer(new StreamPrivate("", "", port, false))
{
}

Expand Down Expand Up @@ -73,16 +73,4 @@ Stream::Future Stream::sendAndFinish(const ImageWrapper& image)
{
return _impl->sendWorker.enqueueImage(image, true);
}

void Stream::sendSizeHints(const SizeHints& hints)
{
_impl->sendWorker.enqueueSizeHints(hints);
}

bool Stream::sendData(const char* data, const size_t count)
{
return _impl->sendWorker
.enqueueData(QByteArray::fromRawData(data, int(count)))
.get();
}
}
Loading