Skip to content
Snippets Groups Projects
muebtransmitter.cc 3.96 KiB
Newer Older
  • Learn to ignore specific revisions
  • #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();
    }