Skip to content
Snippets Groups Projects
muebreceiver.cc 2.80 KiB
#include "muebreceiver.h"

#include <QNetworkDatagram>
#include <QUdpSocket>

#include "configuration.h"

class MuebReceiverPrivate {
  Q_DECLARE_PUBLIC(MuebReceiver)
  Q_DISABLE_COPY(MuebReceiverPrivate)

 public:
  explicit MuebReceiverPrivate(MuebReceiver *q)
      : frame(configuration.frame()), q_ptr(q) {
    socket.bind(configuration.broadcast_animation_port());

    QObject::connect(&socket, &QUdpSocket::readyRead, q,
                     &MuebReceiver::readPendingDatagrams);

    qInfo() << "[MuebReceiver] UDP Socket will receive packets on port"
            << configuration.broadcast_animation_port();
  }

  Configuration configuration;
  libmueb::Frame frame;
  QUdpSocket socket;
  MuebReceiver *q_ptr;
};

MuebReceiver::MuebReceiver(QObject *parent)
    : QObject(parent), d_ptr_(new MuebReceiverPrivate(this)) {}

MuebReceiver::~MuebReceiver() { delete d_ptr_; }

MuebReceiver &MuebReceiver::Instance() {
  static MuebReceiver instance;

  return instance;
}

libmueb::Frame MuebReceiver::frame() { return d_ptr_->frame; }

inline void datagram_uncompress_error() {
  qWarning() << "[MuebReceiver] Processed packet is invalid! Check the header "
                "or packet contents(size)";
}

void MuebReceiver::readPendingDatagrams() {
  while (d_ptr_->socket.hasPendingDatagrams()) {
    if (d_ptr_->socket.pendingDatagramSize() ==
        d_ptr_->configuration.packet_size()) {
      QNetworkDatagram datagram = d_ptr_->socket.receiveDatagram();
      QByteArray data = datagram.data();

      // Process datagram
      // Packet header check
      // Check protocol
      if (data[0] != d_ptr_->configuration.protocol_type()) {
        datagram_uncompress_error();
        return;
      }

      auto packet_number = data[1];
      if (packet_number >= d_ptr_->configuration.max_packet_number() ||
          packet_number < 0) {
        datagram_uncompress_error();
        return;
      }

      data.remove(0, d_ptr_->configuration.packet_header_size());
      auto frame_begin =
          d_ptr_->frame.bits() +
          packet_number * d_ptr_->configuration.max_pixel_per_datagram();

      // Uncompress 1 byte into 2 color components
      if (d_ptr_->configuration.color_depth() < 5) {
        for (auto i : data) {
          *frame_begin = i & 0xf0;
          frame_begin++;
          *frame_begin = (i & 0x0f) << d_ptr_->configuration.factor();
          frame_begin++;
        }
        // No compression
      } else {
        // FIXME use better copy method
        for (auto i : data) {
          *frame_begin = i;
          frame_begin++;
        }
      }

      emit(FrameChanged(d_ptr_->frame));
    }
    // Drop invalid packet
    else {
      qWarning() << "[MuebReceiver] Packet has invalid size!"
                 << d_ptr_->socket.pendingDatagramSize() << "bytes";

      d_ptr_->socket.receiveDatagram(0);
    }
  }
}