#include "muebtransmitter.h" #include <QByteArray> #include <QDebug> #include <QNetworkDatagram> #include <QUdpSocket> #include <QtConcurrent> #include "configuration.h" class MuebTransmitterPrivate { Q_DECLARE_PUBLIC(MuebTransmitter) Q_DISABLE_COPY(MuebTransmitterPrivate) public: explicit MuebTransmitterPrivate(MuebTransmitter* q) : datagram_(QByteArray(), configuration_.target_address(), configuration_.broadcast_animation_port()), q_ptr(q) { qInfo().noquote() << "[MuebTransmitter] UDP Socket will send frame to" << QString("%1:%2") .arg(configuration_.target_address().toString()) .arg(configuration_.broadcast_animation_port()); } void SendFrame(libmueb::Frame frame) { std::uint8_t packet_number{0}; QByteArray reduced_compressed_frame; // Frame color reduction and compression if (configuration_.color_depth() < 5) { reduced_compressed_frame = QtConcurrent::blockingMappedReduced<QByteArray>( frame.constBits(), frame.constBits() + frame.sizeInBytes(), /* Reference: * http://threadlocalmutex.com/?p=48 * http://threadlocalmutex.com/?page_id=60 */ [this](const uchar& color) -> uchar { if (configuration_.color_depth() == 3) { return (color * 225 + 4096) >> 13; } else if (configuration_.color_depth() == 4) { return (color * 15 + 135) >> 8; } return color; }, [this](QByteArray& compressed_colors, const uchar& color) { static bool msb{true}; // Compress 2 color components into 1 byte if (msb) { compressed_colors.append(color << configuration_.factor()); } else { compressed_colors.back() = compressed_colors.back() | color; } msb = !msb; }, QtConcurrent::OrderedReduce | QtConcurrent::SequentialReduce); } // No compression else { reduced_compressed_frame.setRawData( reinterpret_cast<const char*>(frame.bits()), frame.sizeInBytes()); } for (std::uint8_t i = 0; i < configuration_.max_packet_number(); ++i) { if (configuration_.max_packet_number() == 1) { reduced_compressed_frame.insert(0, configuration_.protocol_type()) .insert(1, packet_number); datagram_.setData(reduced_compressed_frame); } else { QByteArray data; data.append(configuration_.protocol_type()) .append(packet_number++) .append(reduced_compressed_frame.sliced( i * configuration_.packet_payload_size(), configuration_.packet_payload_size())); datagram_.setData(data); } socket_.writeDatagram(datagram_); } } Configuration configuration_; QUdpSocket socket_; QNetworkDatagram datagram_; MuebTransmitter* q_ptr; }; MuebTransmitter::MuebTransmitter(QObject* parent) : QObject(parent), d_ptr_(new MuebTransmitterPrivate(this)) {} MuebTransmitter::~MuebTransmitter() { delete d_ptr_; } void MuebTransmitter::SendFrame(libmueb::Frame frame) { if (frame.isNull() || frame.format() == QImage::Format_Invalid || frame.width() != d_ptr_->configuration_.width() || frame.height() != d_ptr_->configuration_.height()) { qWarning() << "[MuebTransmitter] Frame is invalid"; return; } frame.convertTo(QImage::Format_RGB888); d_ptr_->SendFrame(frame); } MuebTransmitter& MuebTransmitter::Instance() { static MuebTransmitter instance; return instance; } int32_t MuebTransmitter::width() const { return d_ptr_->configuration_.width(); } int32_t MuebTransmitter::height() const { return d_ptr_->configuration_.height(); } libmueb::Frame MuebTransmitter::frame() const { return d_ptr_->configuration_.frame(); }