lms-video/src/libremediaserver.cpp
2013-10-17 18:56:45 +02:00

638 lines
19 KiB
C++

/*
Pure Media Server - A Media Server Sotfware for stage and performing
Copyright (C) 2012-2013 Santi Noreña libremediaserver@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "libremediaserver.h"
#define SOCKET "/tmp/pmspipe" // Pipe wich PD Video sends the files for preview in the GUI
#define SOCKET2PD "/tmp/socket"
#define CONF_FILE "lms.conf" // File when saving/restoring the configuration on exit/open
///////////////////////////////////////////////////////////////////
// Struct for the configuration files
///////////////////////////////////////////////////////////////////
struct conf
{
// Video configuration
bool window;
quint16 winpositionx;
quint16 winpositiony;
quint16 winsizex;
quint16 winsizey;
};
///////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////
libreMediaServer::libreMediaServer(QStringList args, QWidget *parent)
: QMainWindow(parent),
m_startvideo(0),
m_pd_video(NULL),
m_gui(FALSE)
{
qDebug() << "********************************************************************************";
qDebug() << QDate::currentDate() << QTime::currentTime();
// Iniciamos el User Interface
ui.setupUi(this);
// Parse the command line options
if (args.contains("-gui"))
{
qDebug()<< "libremediaserver Constructor option GUI detected";
m_gui = true;
ui.textEdit->appendPlainText("Pure Data GUI's will be shown");
}
if (args.contains("-log"))
{
ui.textEdit->appendPlainText("Log to file");
}
//Print the version
QString ver;
ver = VERSION;
ui.textEdit->appendPlainText(ver);
ver = COPYRIGHT;
ui.textEdit->appendPlainText(ver);
ver = LICENSE;
ui.textEdit->appendPlainText(ver);
qDebug() << VERSION;
qDebug() << COPYRIGHT;
qDebug() << LICENSE;
// Init Unix Local Sockets
QFile socket(SOCKET);
if (socket.exists())
{
socket.remove();
}
m_write_video = new QLocalSocket(this);
Q_CHECK_PTR(m_write_video);
m_server_vid = new QLocalServer(this);
Q_CHECK_PTR(m_server_vid);
if (!m_server_vid->listen(SOCKET))
{
qErrnoWarning("libremediaserver::constructor: Can not listen on unix local server");
}
connect(m_server_vid, SIGNAL(newConnection()),this, SLOT(newPeer()));
// Iniciamos olad
m_ola = new QProcess(this);
Q_CHECK_PTR(m_ola);
olastart();
// Conectamos los menus
connect(ui.actionOpen_conf, SIGNAL(triggered()), this, SLOT(openFile()));
connect(ui.actionSave_conf, SIGNAL(triggered()), this, SLOT(saveFile()));
connect(ui.actionChange_Media_Path, SIGNAL(triggered()), this, SLOT(ChangeMediaPath()));
connect(ui.actionLaunch_OLA_Setup,SIGNAL(triggered()),this, SLOT(olasetup()));
// Start PD
pdstart();
// Load the configuration
open_start();
m_olaInterface = new olaInterface();
Q_CHECK_PTR(m_olaInterface);
connect(m_olaInterface->m_msex->m_timer, SIGNAL(timeout()), this, SLOT(sendFrame()));
connect(m_olaInterface, SIGNAL(sendDmx(int,int,int)), this, SLOT(receiveDMX(int,int,int)));
connect(m_olaInterface, SIGNAL(newFile(QString)), this, SLOT(newFile(QString)));
}
///////////////////////////////////////////////////////////////////
// Destructor
///////////////////////////////////////////////////////////////////
libreMediaServer::~libreMediaServer()
{
save_finish();
QFile socket(SOCKET);
socket.remove();
if (m_server_vid->isListening()) {
m_server_vid->close();
delete m_server_vid;
}
if (m_pd_video != NULL)
{
disconnect(m_pd_video, SIGNAL(finished(int)), this, SLOT(pdrestart()));
m_pd_video->terminate();
if (m_pd_video->waitForFinished(30000))
{
delete m_pd_video;
}
}
m_ola->close();
delete m_ola;
qDebug() << "PD Video restarts: " << m_startvideo;
qDebug() << QDate::currentDate() << QTime::currentTime();
qDebug() << "********************************************************************************";
return;
}
///////////////////////////////////////////////////////////////////
// open/save the last configuration known
///////////////////////////////////////////////////////////////////
// Load the last configuration from the configuration file
void libreMediaServer::open_start()
{
QFile file(CONF_FILE);
open(&file);
}
// Save the last configuration to the conf file
void libreMediaServer::save_finish()
{
QFile file(CONF_FILE);
save(&file);
}
///////////////////////////////////////////////////////////////////
// Menu File
///////////////////////////////////////////////////////////////////
// Open a configuration File
void libreMediaServer::openFile()
{
QFileDialog dialog(this);
if (!dialog.exec())
return;
QStringList fileNames;
fileNames = dialog.selectedFiles();
QFile file(fileNames.at(0));
open(&file);
}
// Save configuration File
void libreMediaServer::saveFile()
{
QFileDialog dialog(this);
if (!dialog.exec())
return;
QStringList fileNames;
fileNames = dialog.selectedFiles();
QFile file(fileNames.at(0));
save(&file);
}
// Change Media path
void libreMediaServer::ChangeMediaPath()
{
QFileDialog dialog(this);
dialog.setFileMode(QFileDialog::Directory);
QStringList fileNames;
if (!dialog.exec())
return;
fileNames = dialog.selectedFiles();
QString file = fileNames.at(0);
m_olaInterface->setPath(file);
}
///////////////////////////////////////////////////////////////////
// Open/save Subroutines. The real work.
///////////////////////////////////////////////////////////////////
void libreMediaServer::open(QFile *file)
{
if (!file->open(QIODevice::ReadOnly | QIODevice::Text))
{
qDebug()<<("Can not find the configuration file");
return;
}
int size = file->size();
unsigned char * fileconf = new unsigned char[size];
memset(fileconf, 0, size);
fileconf = file->map(0x00, size);
if (fileconf == 0){
qDebug()<<("Cannot map the file");
return;
}
conf *packet = (conf *)fileconf;
// Video configuration
ui.window->setChecked(packet->window);
ui.winpositionx->setValue(packet->winpositionx);
ui.winpositiony->setValue(packet->winpositiony);
ui.winsizex->setValue(packet->winsizex);
ui.winsizey->setValue(packet->winsizey);
file->close();
}
void libreMediaServer::save(QFile *file)
{
if (!file->open(QIODevice::WriteOnly | QIODevice::Text))
{
qDebug()<<("Can not open file pms.conf");
return;
}
int bufferLen = sizeof(struct conf) + m_pathmedia.size();
unsigned char *buffer = new unsigned char[bufferLen];
memset(buffer, 0, bufferLen);
conf *packet = (conf *)buffer;
// Video Configuration
packet->window = ui.window->checkState();
packet->winpositionx = ui.winpositionx->value();
packet->winpositiony = ui.winpositiony->value();
packet->winsizex = ui.winsizex->value();
packet->winsizey = ui.winsizey->value();
int error = file->write((const char *)buffer, bufferLen);
QString errorstring = tr("Bytes Write to file %1").arg(error);
qDebug()<<"Saved file complete:"<< errorstring;
file->close();
delete buffer;
}
///////////////////////////////////////////////////////////////////
// OLA Stuff
///////////////////////////////////////////////////////////////////
void libreMediaServer::olastart()
{
qDebug()<<("Starting OLA");
m_ola->start("olad", QStringList()<< "-l 3");
// connect(ola, SIGNAL(finished(int)), this, SLOT(olastart()));
}
void libreMediaServer::olasetup()
{
QWebView *view = new QWebView();
view->load(QUrl("http://localhost:9090/ola.html"));
view->show();
}
///////////////////////////////////////////////////////////////////
// Video Controls
///////////////////////////////////////////////////////////////////
// Window Configuration
void libreMediaServer::on_window_stateChanged(int state)
{
if ((state == 2)) {
QString desc("101 0001;");
if (!sendPacket(desc.toAscii().constData(),desc.size()))
{
errorsending();
}
}
if ((state == 0)) {
QString desc("101 0000;");
if (!sendPacket(desc.toAscii().constData(),desc.size()))
{
errorsending();
}
}
}
void libreMediaServer::on_winpositionx_valueChanged()
{
int x = ui.winpositionx->value();
QString desc = tr("102 %1;").arg(x);
if (!sendPacket(desc.toAscii().constData(),desc.size()))
{
errorsending();
}
}
void libreMediaServer::on_winpositiony_valueChanged()
{
int x = ui.winpositiony->value();
QString desc = tr("103 %1;").arg(x);
if (!sendPacket(desc.toAscii().constData(),desc.size()))
{
errorsending();
}
}
void libreMediaServer::on_winsizex_valueChanged()
{
int x = ui.winsizex->value();
QString desc = tr("104 %1;").arg(x);
if (!sendPacket(desc.toAscii().constData(),desc.size()))
{
errorsending();
}
}
void libreMediaServer::on_winsizey_valueChanged()
{
int x = ui.winsizey->value();
QString desc = tr("105 %1;").arg(x);
if (!sendPacket(desc.toAscii().constData(),desc.size()))
{
errorsending();
}
}
// Change the Frame Rate
void libreMediaServer::on_fpsRate_valueChanged()
{
QString desc = tr("123 %1;").arg(ui.fpsRate->value());
if (!sendPacket(desc.toAscii().constData(),desc.size()))
{
errorsending();
}
}
///////////////////////////////////////////////////////////////////
// Pure Data Video
///////////////////////////////////////////////////////////////////
// Start the PD Process, open the ports and connects stdout de Pure Data.
void libreMediaServer::pdstart()
{
m_pd_video = new QProcess();
Q_CHECK_PTR(m_pd_video);
// Arrancamos el proceso Pure Data
QString program = "./pd";
program.append(" -noaudio -lib Gem -stderr -nostdpath -open lms-video.pd");
if (!m_gui)
program.append(" -nogui");
qDebug() << "PD Video starts with arguments: " << program;
m_pd_video->setWorkingDirectory("./puredata");
m_pd_video->start(program);
if (m_pd_video->waitForStarted(3000)){
ui.textEdit->appendPlainText("Video Engine started.");
}
else
{
qWarning("PD Video can not start") ;
ui.textEdit->appendPlainText("Video Engine can not start!");
return;
}
// Connect the output fropm PD Video to stdout slot to process it
connect(m_pd_video, SIGNAL(readyReadStandardError()), this, SLOT(stdout()));
// Restart PD Video if crash
connect(m_pd_video, SIGNAL(finished(int)), this, SLOT(pdrestart()));
}
// Restart the Pure Data process if crash. Connected with signal finished of QProcess
void libreMediaServer::pdrestart()
{
if (m_pd_video->state())
{
return;
}
save_finish();
qDebug()<<"********************************************************************************";
qDebug()<<"PD Video Restarts:" << ++m_startvideo;
ui.textEdit->appendPlainText("PD Video Restarting.");
disconnect(m_pd_video, SIGNAL(finished(int)), this, SLOT(pdrestart()));
pdstart();
}
// New conexion on TCP Server
void libreMediaServer::newPeer()
{
m_read_vid = m_server_vid->nextPendingConnection();
connect(m_read_vid, SIGNAL(readyRead()),
this, SLOT(newmessage()));
}
// New message in a socket stablished connection
void libreMediaServer::newmessage()
{
if (m_read_vid == NULL)
{
qDebug()<<("Local Socket not created");
newPeer();
return;
}
QByteArray byteArray;
byteArray.resize(m_read_vid->bytesAvailable());
byteArray = m_read_vid->readAll();
if (byteArray.isEmpty())
{
return;
}
QPixmap frame;
switch (byteArray.at(0)) {
case 11:
byteArray.remove(0,2);
if (!frame.loadFromData((byteArray))) {
qDebug()<<"Layer 1 Convert byte Array to frame failed ";
}
ui.layer1Preview->setPixmap(frame);
break;
case 12:
byteArray.remove(0,2);
if (!frame.loadFromData((byteArray))) {
qDebug()<<"Layer 2 Convert byte Array to frame failed ";
}
ui.layer2Preview->setPixmap(frame);
break;
case 13:
byteArray.remove(0,2);
if (!frame.loadFromData((byteArray))) {
qDebug()<<"Layer 3 Convert byte Array to frame failed ";
break;
}
ui.layer3Preview->setPixmap(frame);
break;
case 14:
byteArray.remove(0,2);
if (!frame.loadFromData(byteArray)) {
qDebug()<<"Layer 4 Convert byte Array to frame failed ";
break;
}
ui.layer4Preview->setPixmap(frame);
break;
case 15:
byteArray.remove(0,2);
if (!frame.loadFromData(byteArray)) {
qDebug()<<"Layer 5 Convert byte Array to frame failed ";
break;
}
ui.layer5Preview->setPixmap(frame);
break;
case 16:
byteArray.remove(0,2);
if (!frame.loadFromData(byteArray)) {
qDebug()<<"Layer 6 Convert byte Array to frame failed ";
break;
}
ui.layer6Preview->setPixmap(frame);
break;
case 17:
byteArray.remove(0,2);
if (!frame.loadFromData(byteArray)) {
qDebug()<<"Layer 7 Convert byte Array to frame failed ";
break;
}
ui.layer7Preview->setPixmap(frame);
break;
case 18:
byteArray.remove(0,2);
if (!frame.loadFromData(byteArray)) {
qDebug()<<"Layer 8 Convert byte Array to frame failed ";
break;
}
ui.layer8Preview->setPixmap(frame);
break;
default:
qDebug()<<"newmessage: Message received but can not identify the cookie";
break;
}
}
// Send the configuration to PD
void libreMediaServer::newconexion()
{
// Init the Unix Socket
m_write_video->connectToServer(SOCKET2PD);
m_write_video->waitForConnected(3000);
if(!(m_write_video->isOpen())) {
qErrnoWarning("newconexion: pd write Unix socket not open!");
}
// send the windows info
on_winpositionx_valueChanged();
on_winpositiony_valueChanged();
on_winsizex_valueChanged();
on_winsizey_valueChanged();
on_window_stateChanged(ui.window->checkState());
}
// Send packet to Pure Data video
bool libreMediaServer::sendPacket(const char *buffer, int bufferLen)
{
if (QLocalSocket::ConnectedState != m_write_video->state())
{
// qErrnoWarning("sendPacket:Socket not conected: ");
return false;
}
if (bufferLen != m_write_video->write((const char*)buffer, bufferLen))
{
qErrnoWarning("sendPacket:Can not write to socket: ");
return false;
}
return true;
}
// Send DMX Channel to Pure Data
void libreMediaServer::receiveDMX(int layer, int channel, int value)
{
QString buffer = tr("%1 %2 %3;").arg(layer).arg(channel).arg(value);
if (!sendPacket(buffer.toAscii().constData(), buffer.size()))
{
errorsending();
}
}
// Request PD to open a new media File
bool libreMediaServer::newFile(QString file)
{
if (QLocalSocket::ConnectedState != m_write_video->state())
{
qErrnoWarning("sendPacket:Socket not conected: ");
return false;
}
if (file.size() != m_write_video->write(file.toAscii().constData(), file.size()))
{
qErrnoWarning("sendPacket:Can not write to socket: ");
return false;
}
return true;
}
// Function error sending packets to PD video
void libreMediaServer::errorsending() {
qErrnoWarning("errorsending: Can not talk to Pure Data!");
ui.textEdit->appendPlainText("errorsending: Can not send packets to PD");
}
///////////////////////////////////////////////////////////////////
// Previews
///////////////////////////////////////////////////////////////////
// Master prevuew for CITP/MSEx 1.0 and GUI
void libreMediaServer::sendFrame()
{
QPixmap frame = QPixmap::grabWindow(QApplication::desktop()->winId(), ui.winpositionx->value() , ui.winpositiony->value(),ui.winsizex->value(),ui.winsizey->value());
if (!frame) {
qDebug()<<"sendFrame: Can not take frame";
return;
}
ui.masterPreview->setPixmap(frame);
QImage image = QImage(frame.toImage());
if (!image.byteCount()) {
qDebug()<<"sendFrame: Can not convert screen capture to image";
return;
}
image = image.scaledToWidth(88);
image = image.convertToFormat(QImage::Format_RGB888);
m_olaInterface->m_msex->sendFrame(image);
}
// Enable/Disable the GUI Master preview
void libreMediaServer::on_previewMaster_stateChanged (int state)
{
if ((state == 0)) {
m_olaInterface->m_msex->m_timer->stop();
}
if ((state == 2))
{
m_olaInterface->m_msex->m_timer->start();
}
}
// Enable/Disable the GUI Layers preview
void libreMediaServer::on_previewLayer_stateChanged (int state)
{
if ((state == 0)) {
QString desc = tr("122 0;");
if (!sendPacket(desc.toAscii().constData(),desc.size()))
{
errorsending();
}
}
if ((state == 2))
{
QString desc = tr("122 1;");
if (!sendPacket(desc.toAscii().constData(),desc.size()))
{
errorsending();
}
}
}
///////////////////////////////////////////////////////////////////
// Terminal and Log
////////////////////////////////////////////////////////////////////
// Sacamos la salida de Pure Data Video en la terminal. Filtra mensajes para el GUI.
void libreMediaServer::stdout() {
QByteArray out = m_pd_video->readAllStandardError();
if (out.size() < 7) {return;}
out.chop(1);
int j = out.indexOf("togui: ",0);
if ((j >= 0) && (out.size() > (j+7)))
{
// int i = m_pathmedia.size() + 16;
out.remove(0,j);
switch (out.at(7)) {
case '0':
qDebug()<<"Loadbang Video";
ui.textEdit->appendPlainText("LoadBang Video received.");
newconexion();
break;
case '9':
out.remove(0,9);
ui.fpsCounter->display(out.toInt());
break;
default:
qDebug()<<"stdout:Invalid cookie received"<<out;
break;
}
return;
}
qDebug() << "PD debug| " << out;
}