You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
amarok/amarok/src/engine/helix/hxplayercontrol.cpp

1299 lines
42 KiB

/***************************************************************************
* Copyright (C) 2006 Paul Cifarelli <paul@cifarelli.net> *
* *
* 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 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
/***************************************************************************
basically this implements a rudamentary rpc mechanism so the we can have
each player in a separate process. this is solely done to implement a
reliable crossfade (or more precicely, put the crossfade in the hands of
the alsa guys
***************************************************************************/
#include <cstdlib>
#include <cstring>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <iostream>
#include <cstring>
#include <sys/mman.h>
using namespace std;
#include "hxplayercontrol.h"
class HSPPlayerControlled : public HelixSimplePlayer
{
public:
HSPPlayerControlled(PlayerControl *pcntl, int index) : m_pcntl(pcntl), m_index(index) {}
virtual ~HSPPlayerControlled() {}
virtual void notifyUser(unsigned long code, const char *moreinfo, const char *moreinfourl);
virtual void interruptUser(unsigned long code, const char *moreinfo, const char *moreinfourl);
virtual void onContacting(const char *host);
virtual void onBuffering(int percentage);
private:
PlayerControl *m_pcntl;
int m_index;
};
void HSPPlayerControlled::notifyUser(unsigned long code, const char *moreinfo, const char *moreinfourl)
{
m_pcntl->sendnotifyuser(code, moreinfo, moreinfourl);
}
void HSPPlayerControlled::interruptUser(unsigned long code, const char *moreinfo, const char *moreinfourl)
{
m_pcntl->sendinterruptuser(code, moreinfo, moreinfourl);
}
void HSPPlayerControlled::onContacting(const char *host)
{
m_pcntl->sendcontacting(host);
}
void HSPPlayerControlled::onBuffering(int percentage)
{
m_pcntl->sendbuffering(percentage);
}
PlayerControl::PlayerControl() : m_eq_enabled(false), m_preamp(0), m_err(0), iamparent(0), m_index(0), nNumPlayers(0),
m_inited(false), m_api( HelixSimplePlayer::OSS ), m_device(0), mimehead(0), mimelistlen(0),
m_numPlugins(0), m_pluginInfo(0)
{
memset(m_children, 0, sizeof(m_children));
}
PlayerControl::~PlayerControl()
{
tearDown();
print2stderr("In PlayerControl::~PlayerControl(), m_index=%d\n", m_index);
delete m_device;
if (pmapped)
munmap(pmapped, sizeof(stateStuff) * 2);
}
// init functions
void PlayerControl::init(const char *corelibpath, const char *pluginslibpath, const char *codecspath, int numPlayers)
{
int err;
iamparent = 0;
nNumPlayers = numPlayers;
m_err = 0;
print2stderr("In PlayerControl::init(), m_api=%d, m_device=%s\n", m_api, m_device ? m_device : "DEVICE NOT SET");
if (numPlayers > 2) // it's impossible
{
m_err = -1;
return;
}
memset(&m_children, 0, numPlayers * sizeof(struct playerChildren));
m_inited = false;
// create a shared memory region for state like stuff
if ( MAP_FAILED == (pmapped = (stateStuff *) mmap( (void *) statestuff,
sizeof(stateStuff) * 2,
PROT_READ | PROT_WRITE,
#ifdef __linux__
MAP_SHARED | MAP_ANONYMOUS,
#else
MAP_SHARED | MAP_ANON,
#endif
-1, 0)) )
pmapped = 0;
// we do this the old fashioned way, so that we don't have to include an executable with our plugin...
for (int i = 0; i < numPlayers; i++)
{
if (pmapped)
{
m_children[i].current_time = &(pmapped[i].current_time);
m_children[i].duration = &(pmapped[i].duration);
m_children[i].md = &(pmapped[i].md);
m_children[i].m_current = &(pmapped[i].m_current);
m_children[i].m_consumed = &(pmapped[i].m_consumed);
//m_children[i].q = pmapped[i].q;
//for (int j=0; j<NUM_SCOPEBUFS; j++)
//{
// m_children[i].q[j].allocd = false;
// m_children[i].q[j].buf = pmapped[i].b[j];
//}
}
err = pipe(m_children[i].m_pipeA);
err |= pipe(m_children[i].m_pipeB);
if ( !err && (iamparent = fork()) )
{
// parent
print2stderr("%%%%%% parent initializes player %d\n", i);
// parent's m_pid remains 0
m_children[i].m_pid = iamparent;
close(m_children[i].m_pipeA[1]); // parent uses A for reading
close(m_children[i].m_pipeB[0]); // and B for writing
}
else if (!err)
{
// child
cerr << "%%%%%% child initializes as player " << i << endl;;
m_index = i; // child's index is saved
close(m_children[i].m_pipeA[0]); // child uses A for writing
close(m_children[i].m_pipeB[1]); // and B for reading
break;
}
}
if (!iamparent) // children stay here, parents return
{
int rfd = m_children[m_index].m_pipeB[0];
int wfd = m_children[m_index].m_pipeA[1];
int n;
struct timeval timeout;
HSPPlayerControlled *player = 0;
bool playing = false;
player = new HSPPlayerControlled(this, m_index);
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
fd_set rdset, wrset;
FD_ZERO(&rdset);
FD_ZERO(&wrset);
FD_SET(rfd, &rdset);
FD_SET(wfd, &wrset); // really should check to see if we can write, but not gonna
while ( 1 )
{
FD_SET(rfd, &rdset);
n = select(rfd + 1, &rdset, 0, 0, &timeout);
if ( FD_ISSET(rfd, &rdset) )
{
msgid m;
unsigned char buf[65536];
int sz = 0;
if (getmessage(rfd, m, buf, sz) && player)
{
switch (m)
{
case INIT:
cerr << "INIT\n";
if (!sz)
{
player->setOutputSink(m_api);
if (m_device)
player->setDevice(m_device);
player->init(corelibpath, pluginslibpath, codecspath, 1);
m_inited = true;
if (!m_index) // only player 0 need send the mime and plugin stuff
{
sendplugins(wfd, player);
sendmimetypes(wfd, player);
}
cerr << "%%%%%% player " << m_index << " child sends ready\n";
sendready(wfd);
}
else
cerr << "CHILD " << m_index << " sz not right in INIT, sz=" << sz << endl;
break;
case SETURL:
{
bool islocal = (bool) buf[0];
int len = strlen((const char *)&buf[1]); // not that this would prevent a crash...
if (m_inited)
{
cerr << "CHILD " << m_index << " setURL for " << (const char *)&buf[1] <<
",islocal=" << islocal << ",len=" << len << endl;
if (sz == len + 2)
player->setURL((const char *)&buf[1], 0, islocal); // remember, we sent the null...
else
cerr << "CHILD " << m_index << " sz not right in SETURL, sz=" << sz << endl;
}
}
break;
case START:
{
bool fadein = (bool) buf[0];
unsigned long fadetime;
if (m_inited)
{
if (pmapped)
*m_children[m_index].current_time = 0;
if (sz == sizeof(unsigned long) + 1)
{
cerr << "CHILD " << m_index << " gets START\n";
memcpy((void *)&fadetime, (void *)&buf[1], sizeof(unsigned long));
playing = true;
player->start(0, fadein, fadetime);
}
else
cerr << "CHILD " << m_index << " sz not right in START, sz=" << sz << endl;
}
}
break;
case STOP:
if (m_inited)
if (!sz)
{
player->stop();
playing = false;
cerr << "CHILD " << m_index << " gets STOP\n";
}
else
cerr << "CHILD " << m_index << " sz not right in STOP, sz=" << sz << endl;
break;
case PAUSE:
if (m_inited)
if (!sz)
player->pause();
else
cerr << "CHILD " << m_index << " sz not right in PAUSE, sz=" << sz << endl;
break;
case RESUME:
if (m_inited)
if (!sz)
player->resume();
else
cerr << "CHILD " << m_index << " sz not right in RESUME, sz=" << sz << endl;
break;
case SEEK:
if (m_inited)
if (sz == sizeof(unsigned long))
{
unsigned long pos;
memcpy( (void *) &pos, (void *) buf, sizeof(unsigned long) );
if (pos < player->duration(0))
player->seek(pos, 0);
}
else
cerr << "CHILD " << m_index << " sz not right in SEEK, sz=" << sz << endl;
break;
case SETVOLUME:
{
if (m_inited)
if (sz == sizeof(unsigned long))
{
memcpy( (void *) &m_volume, (void *) buf, sizeof(unsigned long));
cerr << "CHILD: received setvolume request " << m_volume <<endl;;
player->setVolume(m_volume);
}
else
cerr << "CHILD " << m_index << " sz not right in SETVOLUME, sz=" << sz << endl;
}
break;
case OUTPUTSINK:
if (m_inited)
if (sz == 1)
{
m_api = (HelixSimplePlayer::AUDIOAPI) buf[0];
cerr << "CHILD: received OUTPUTSINK: " << m_api <<endl;;
}
else
cerr << "CHILD " << m_index << " sz not right in OUTPUTSINK, sz=" << sz << endl;
break;
case DEVICE:
{
char* dev = strdup((const char*) buf);
if (m_inited)
if ((unsigned)sz == strlen(dev) + 1)
{
cerr << "CHILD " << m_index << " gets device " << dev << endl;
setDevice( dev );
}
else
cerr << "CHILD " << m_index << " sz not right in DEVICE, sz=" << sz << endl;
free(dev);
}
break;
case SETFADE:
{
if (m_inited)
if (sz == sizeof(unsigned long) + 1)
{
bool fadeout;
unsigned long fadelength;
fadeout = (bool) buf[0];
memcpy((void *) &fadelength, (void *) &buf[1], sizeof(unsigned long));
player->setFadeout(fadeout, fadelength, 0);
}
else
cerr << "CHILD " << m_index << " sz not right in SETFADE, sz=" << sz << endl;
}
break;
case ENABLEEQ:
{
if (m_inited)
if (sz == 1)
{
m_eq_enabled = (bool) buf[0];
player->enableEQ(m_eq_enabled);
cerr << "CHILD " << m_index << " enables EQ\n";
}
else
cerr << "CHILD " << m_index << " sz not right in ENABLEEQ, sz=" << sz << endl;
}
break;
case UPDATEETQGAINS:
{
int i, n, k;
cerr << "UPDATEGAINS\n";
if (m_inited)
{
if (sz >= 2)
{
memcpy( (void *) &m_preamp, (void *) buf, sizeof(m_preamp) );
memcpy( (void *) &n, (void *) &buf[ sizeof(m_preamp) ], sizeof(int) );
player->m_preamp = m_preamp;
}
else
cerr << "CHILD " << m_index << " sz not right in UPDATEETQGAINS, sz=" << sz << endl;
if ((unsigned)sz == sizeof(m_preamp) + sizeof(int) + n * sizeof(int))
{
if (n > 0)
{
m_equalizerGains.resize(n);
cerr << "CHILD " << m_index << " receives " << n << " equalizer gains\n";
for (i=0; i<n; i++)
{
memcpy( (void *) &k, (void *) &buf[ sizeof(m_preamp) + (i+1)*sizeof(int) ], sizeof(int) );
m_equalizerGains[i] = k;
}
player->m_equalizerGains = m_equalizerGains;
if (!m_eq_enabled)
player->enableEQ(true);
player->updateEQgains();
player->enableEQ(m_eq_enabled);
}
}
else
cerr << "CHILD " << m_index << " sz not right in UPDATEETQGAINS, sz=" << sz << endl;
}
}
break;
case SCOPECLEAR:
if (m_inited)
{
player->clearScopeQ(0);
if (pmapped)
*m_children[m_index].m_consumed = *m_children[m_index].m_current = 0;
}
break;
case TEARDOWN:
if (!sz)
{
cerr << "CHILD: " << m_index << " received shutdown request\n";
player->stop(0);
delete player;
raise(15);
exit(0);
}
else
cerr << "CHILD " << m_index << " sz not right in TEARDOWN, sz=" << sz << endl;
break;
default: // send an error to the parent
cerr << "CHILD " << m_index << " received unhandled message, sz=" << sz << endl;
break;
}
}
else
{
cerr << "CHILD " << m_index << " gets EOD\n";
break;
}
}
if (m_inited)
{
player->dispatch();
if (playing && player->done(0))
{
player->stop(0);
player->clearScopeQ(0);
senddone(wfd);
playing = false;
if (pmapped)
{
*m_children[m_index].current_time = 0;
*m_children[m_index].duration = 0;
}
}
if (pmapped)
{
*m_children[m_index].current_time = player->where(0);
*m_children[m_index].duration = player->duration(0);
HelixSimplePlayer::metaData *md = player->getMetaData(0);
if (md)
memcpy((void *) m_children[m_index].md, (void *) md, sizeof(HelixSimplePlayer::metaData));
struct DelayQueue *item;
//int j;
while ((item = player->getScopeBuf(0)))
{
//j = (*m_children[m_index].m_current + 1) % NUM_SCOPEBUFS;
//cerr << "player:" << m_index << " j=" << j << " time=" << item->time << " etime=" << item->etime << " len=" << item->len << endl;
//m_children[m_index].q[j].len = item->len;
//m_children[m_index].q[j].time = item->time;
//m_children[m_index].q[j].etime = item->etime;
//m_children[m_index].q[j].nchan = item->nchan;
//m_children[m_index].q[j].bps = item->bps;
//m_children[m_index].q[j].tps = item->tps;
//m_children[m_index].q[j].spb = item->spb;
//memcpy((void *)m_children[m_index].q[j].buf, (void *) item->buf, item->len );
//*m_children[m_index].m_current = j;
//cerr << "player:" << m_index << " time=" << item->time << " etime=" << item->etime << endl;
sendscopebuf(wfd, item);
delete item;
}
}
}
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
}
cerr << "CHILD " << m_index << " will exit!\n";
}
else
{
int i;
bool done = false, dead = false;
sendsetoutputsink();
sendsetdevice();
sendinit();
// wait for ready from children
while (!done && !dead)
{
dispatch();
done = true;
for (i=0; i<numPlayers; i++)
{
done &= m_children[i].isready;
dead |= m_children[i].isdead;
}
}
if (dead)
{
m_err = -1;
return;
}
}
m_inited = true;
}
void PlayerControl::setOutputSink( HelixSimplePlayer::AUDIOAPI out )
{
print2stderr("%%%% In PlayerControl::setOutputSink:%d\n", out);
m_api = out;
}
void PlayerControl::setDevice( const char *dev )
{
delete [] m_device;
int len = strlen(dev);
m_device = new char [len + 1];
strcpy(m_device, dev);
print2stderr("%%%% In PlayerControl::setDevice:%s\n", dev);
}
int PlayerControl::initDirectSS()
{
return 0;
}
void PlayerControl::tearDown()
{
int tmp;
if (iamparent)
{
for (int i = 0; i < nNumPlayers; i++)
{
if (m_inited)
{
sendteardown(m_children[i].m_pipeB[1]);
close(m_children[i].m_pipeB[1]);
close(m_children[i].m_pipeA[0]);
cerr << "About to waitpid for pid " << m_children[i].m_pid << endl;
kill(m_children[i].m_pid, SIGTERM);
waitpid(m_children[i].m_pid, &tmp, 0);
}
}
}
}
void PlayerControl::start(int playerIndex, bool fadein, unsigned long fadetime)
{
m_children[playerIndex].isplaying = true;
if (pmapped)
*m_children[playerIndex].m_consumed = *m_children[playerIndex].m_current = 0;
sendstart(m_children[playerIndex].m_pipeB[1], fadein, fadetime);
}
int PlayerControl::setURL(const char *url, int playerIndex, bool islocal)
{
m_children[playerIndex].islocal = islocal;
if (sendsetURL(m_children[playerIndex].m_pipeB[1], url, islocal))
return 0;
return -1;
}
bool PlayerControl::done(int playerIndex)
{
return (!m_children[playerIndex].isplaying);
}
void PlayerControl::stop(int playerIndex)
{
if (playerIndex == HelixSimplePlayer::ALL_PLAYERS)
{
for (int i=0; i<nNumPlayers; i++)
stop(i);
}
else
{
m_children[playerIndex].isplaying = false;
sendstop(m_children[playerIndex].m_pipeB[1]);
}
}
void PlayerControl::pause(int playerIndex)
{
sendpause(m_children[playerIndex].m_pipeB[1]);
}
void PlayerControl::resume(int playerIndex)
{
sendresume(m_children[playerIndex].m_pipeB[1]);
}
void PlayerControl::seek(unsigned long pos, int playerIndex)
{
sendmessage(m_children[playerIndex].m_pipeB[1], SEEK, (unsigned char *) &pos, sizeof(unsigned long));
}
unsigned long PlayerControl::where(int playerIndex) const
{
if (pmapped)
return *m_children[playerIndex].current_time;
else
return 0;
}
unsigned long PlayerControl::duration(int playerIndex) const
{
if (pmapped)
return *m_children[playerIndex].duration;
else
return 0;
}
unsigned long PlayerControl::getVolume()
{
return m_volume;
}
void PlayerControl::setVolume(unsigned long vol)
{
m_volume = vol;
for (int i = 0; i < nNumPlayers; i++)
sendsetvolume(m_children[i].m_pipeB[1], vol);
}
void PlayerControl::dispatch()
{
int i;
struct timeval timeout;
int n = -1, ntot;
int rfd;
int wfd;
timeout.tv_sec = 0;
timeout.tv_usec = 0;
fd_set rdset, wrset;
FD_ZERO(&rdset);
FD_ZERO(&wrset);
for (i=0; i<nNumPlayers; i++)
{
rfd = m_children[i].m_pipeA[0];
wfd = m_children[i].m_pipeB[1];
FD_SET(rfd, &rdset);
FD_SET(wfd, &wrset); // really should check to see if we can write, but not gonna
if (rfd > n)
n = rfd;
}
if (n < 0)
return;
ntot = select(n + 1, &rdset, 0, 0, &timeout);
for (i=0; ntot && i < nNumPlayers; i++)
{
rfd = m_children[i].m_pipeA[0];
wfd = m_children[i].m_pipeB[1];
if ( FD_ISSET(rfd, &rdset) )
{
msgid m;
unsigned char buf[65536];
int sz = 0;
if (getmessage(rfd, m, buf, sz))
{
switch (m)
{
case READY:
print2stderr("CHILD %d is READY\n", i);
m_children[i].isready = true;;
break;
case DONE:
print2stderr("CHILD %d is DONE\n", i);
if (!sz)
{
m_children[i].isplaying = false;
clearScopeQ(i);
play_finished(i);
}
else
print2stderr("PARENT: sz does not agree in DONE\n");
break;
case MIMETYPES:
{
int len, slen;
MimeList *entry;
char *tmp;
char tmpbuf[65536];
mimehead = 0;
memcpy( (void *) &mimelistlen, (void *) buf, sizeof(mimelistlen) );
len = sizeof(mimelistlen);
print2stderr("%%%%%%% Received %d mimetypes\n", mimelistlen);
for (int j = 0; j < mimelistlen; j++)
{
tmp = (char *) &buf[len];
slen = strlen(tmp);
strcpy(tmpbuf, tmp);
tmp += slen + 1;
len += slen + 1;
entry = new MimeList(tmpbuf, tmp);
slen = strlen(tmp);
len += slen + 1;
entry->fwd = mimehead;
mimehead = entry;
}
if (sz != len) // sanity check
cerr << "PARENT: sz not = len in MIMETYPES " << sz << " " << len << endl;
}
break;
case PLUGINS:
{
int len, slen;
int nplugins;
char *tmp;
memcpy( (void *) &nplugins, (void *) buf, sizeof(nplugins) );
len = sizeof(nplugins);
m_pluginInfo = new pluginInfo* [nplugins];
m_numPlugins = nplugins;
print2stderr("%%%%%%% Received %d plugins\n", nplugins);
for (int j = 0; j < nplugins; j++)
{
m_pluginInfo[j] = new pluginInfo;
tmp = (char *) &buf[len];
slen = strlen(tmp);
m_pluginInfo[j]->description = new char[ slen + 1 ];
strcpy(m_pluginInfo[j]->description, tmp);
len += slen + 1;
tmp = (char *) &buf[len];
slen = strlen(tmp);
m_pluginInfo[j]->copyright = new char[ slen + 1 ];
strcpy(m_pluginInfo[j]->copyright, tmp);
len += slen + 1;
tmp = (char *) &buf[len];
slen = strlen(tmp);
m_pluginInfo[j]->moreinfourl = new char[ slen + 1 ];
strcpy(m_pluginInfo[j]->moreinfourl, tmp);
len += slen + 1;
}
if (sz != len) // sanity check
cerr << "PARENT: sz not = len in PLUGINS " << sz << " " << len << endl;
}
break;
case CONTACTING:
{
int len = strlen((const char *)buf);
if (sz == len + 1)
onContacting((const char *)buf);
else
cerr << "PARENT: sz not right in CONTACTING sz=" << sz << endl;
}
break;
case BUFFERING:
{
unsigned long percent;
if (sz == sizeof(unsigned long))
{
memcpy((void *)&percent, (void *) buf, sizeof(unsigned long));
onBuffering(percent);
}
else
cerr << "PARENT: sz not right in BUFFERING sz=" << sz << endl;
}
break;
case NOTIFYUSER:
{
int len1, len2;
unsigned long code;
const char *moreinfo, *moreinfourl;
memcpy((void *) &code, (void *) buf, sizeof(unsigned long));
moreinfo = (const char *) &buf[sizeof(unsigned long)];
len1 = strlen(moreinfo);
moreinfourl = (const char *) &moreinfo[ len1 + 1];
len2 = strlen(moreinfourl);
// sanity check
if ((unsigned) sz != sizeof(unsigned long) + len1 + len2 + 2)
cerr << "PARENT: sz not right in NOTIFYUSER sz=" << sz << endl;
notifyUser(code, moreinfo, moreinfourl);
}
break;
case INTERRUPTUSER:
{
int len1, len2;
unsigned long code;
const char *moreinfo, *moreinfourl;
memcpy((void *) &code, (void *) buf, sizeof(unsigned long));
moreinfo = (const char *) &buf[sizeof(unsigned long)];
len1 = strlen(moreinfo);
moreinfourl = (const char *) &moreinfo[ len1 + 1];
len2 = strlen(moreinfourl);
// sanity check
if ((unsigned) sz != sizeof(unsigned long) + len1 + len2 + 2)
cerr << "PARENT: sz not right in INTERRUPTUSER sz=" << sz << endl;
interruptUser(code, moreinfo, moreinfourl);
}
break;
case SCOPEBUF:
{
DelayQueue *item;
int bufsz, len = 0;
memcpy( (void *) &bufsz, (void *) buf, sizeof(int) ); len += sizeof(int);
if ((int) (bufsz + 2 * sizeof(unsigned long) + 4 * sizeof(int) + sizeof(double)) == sz)
{
item = new DelayQueue(bufsz);
memcpy( (void *) &item->time, (void *) &buf[len], sizeof(unsigned long) );
len += sizeof(unsigned long);
memcpy( (void *) &item->etime, (void *) &buf[len], sizeof(unsigned long) );
len += sizeof(unsigned long);
memcpy( (void *) &item->nchan, (void *) &buf[len], sizeof(int) ); len += sizeof(int);
memcpy( (void *) &item->bps, (void *) &buf[len], sizeof(int) ); len += sizeof(int);
memcpy( (void *) &item->tps, (void *) &buf[len], sizeof(double) ); len += sizeof(double);
memcpy( (void *) &item->spb, (void *) &buf[len], sizeof(int) ); len += sizeof(int);
memcpy( (void *) item->buf, (void *) &buf[len], item->len ); len += item->len;
addScopeBuf(item, i);
}
else
cerr << "PARENT: sz not right in SCOPEBUF sz=" << sz << endl;
}
break;
default:
print2stderr("PARENT recvd unhandled message %d\n", m);
break;
}
}
else
{
m_children[i].isdead = true;
return; // should never happen
}
}
}
for (i=0; pmapped && i<nNumPlayers; i++)
{
while (*m_children[i].m_consumed != *m_children[i].m_current)
{
addScopeBuf(&m_children[i].q[*m_children[i].m_consumed], i);
//cerr << "j=" << *m_children[i].m_consumed <<
// " time=" << m_children[i].q[*m_children[i].m_consumed].time <<
// " etime=" << m_children[i].q[*m_children[i].m_consumed].etime <<
// " len=" << m_children[i].q[*m_children[i].m_consumed].len << endl;
*m_children[i].m_consumed = (*m_children[i].m_consumed + 1) % NUM_SCOPEBUFS;
}
}
}
void PlayerControl::cleanUpStream(int playerIndex)
{
stop(playerIndex);
}
bool PlayerControl::isPlaying(int playerIndex) const
{
return m_children[playerIndex].isplaying;
}
bool PlayerControl::isLocal(int playerIndex) const
{
return m_children[playerIndex].islocal;
}
void PlayerControl::setFadeout(bool fadeout, unsigned long fadelength, int playerIndex)
{
sendsetfade(m_children[playerIndex].m_pipeB[1], fadeout, fadelength);
}
void PlayerControl::addScopeBuf(struct DelayQueue *item, int playerIndex)
{
if (playerIndex >=0 && playerIndex < nNumPlayers)
{
if (m_children[playerIndex].scopebuftail)
{
item->fwd = 0;
m_children[playerIndex].scopebuftail->fwd = item;
m_children[playerIndex].scopebuftail = item;
m_children[playerIndex].scopecount++;
}
else
{
item->fwd = 0;
m_children[playerIndex].scopebufhead = item;
m_children[playerIndex].scopebuftail = item;
m_children[playerIndex].scopecount = 1;
}
}
}
DelayQueue *PlayerControl::getScopeBuf(int playerIndex)
{
if (playerIndex >=0 && playerIndex < nNumPlayers)
{
struct DelayQueue *item = m_children[playerIndex].scopebufhead;
if (item)
{
m_children[playerIndex].scopebufhead = item->fwd;
m_children[playerIndex].scopecount--;
if (!m_children[playerIndex].scopebufhead)
m_children[playerIndex].scopebuftail = 0;
}
return item;
}
else
return 0;
}
int PlayerControl::getScopeCount(int playerIndex)
{
return (playerIndex >= 0 && playerIndex < nNumPlayers ? m_children[playerIndex].scopecount : 0);
}
int PlayerControl::peekScopeTime(unsigned long &t, int playerIndex)
{
if (playerIndex >=0 && playerIndex < nNumPlayers)
{
if (m_children[playerIndex].scopebufhead)
t = m_children[playerIndex].scopebufhead->time;
else
return -1;
return 0;
}
return -1;
}
void PlayerControl::clearScopeQ(int playerIndex)
{
if (playerIndex < 0)
{
for (int i=0; i<nNumPlayers; i++)
clearScopeQ(i);
}
else
{
sendscopeclear(m_children[playerIndex].m_pipeB[1]);
struct DelayQueue *item;
while ((item = getScopeBuf(playerIndex)))
if (item->allocd)
delete item;
}
}
void PlayerControl::enableEQ(bool enabled)
{
int i;
unsigned char c = (char) enabled;
for (i=0; i<nNumPlayers; i++)
sendmessage(m_children[i].m_pipeB[1], ENABLEEQ, (unsigned char *) &c, 1);
m_eq_enabled = enabled;
}
bool PlayerControl::isEQenabled()
{
return m_eq_enabled;
}
void PlayerControl::updateEQgains()
{
sendupdateeqgains();
}
int PlayerControl::numPlugins() const
{
return m_numPlugins;
}
int PlayerControl::getPluginInfo(int index, const char *&description, const char *&copyright, const char *&moreinfourl) const
{
if (m_pluginInfo && index < m_numPlugins)
{
description = m_pluginInfo[index]->description;
copyright = m_pluginInfo[index]->copyright;
moreinfourl = m_pluginInfo[index]->moreinfourl;
return 0;
}
return -1;
}
const MimeList *PlayerControl::getMimeList() const
{
return mimehead;
}
int PlayerControl::getMimeListLen() const
{
return mimelistlen;
}
HelixSimplePlayer::metaData *PlayerControl::getMetaData(int playerIndex )
{
return m_children[playerIndex].md;
}
bool PlayerControl::sendsetoutputsink()
{
int i;
char c = (char) m_api;
bool ok = false;
for (i=0; i<nNumPlayers; i++)
ok |= sendmessage(m_children[i].m_pipeB[1], OUTPUTSINK, (unsigned char *) &c, 1);
return ok;
}
bool PlayerControl::sendsetdevice()
{
if (!m_device)
return false;
int i, len = strlen( m_device );
bool ok = false;
for (i=0; i<nNumPlayers; i++)
ok |= sendmessage(m_children[i].m_pipeB[1], DEVICE, (unsigned char *) m_device, len + 1);
return ok;
}
bool PlayerControl::sendinit()
{
int i;
bool ok = false;
for (i=0; i<nNumPlayers; i++)
ok |= sendrequest(m_children[i].m_pipeB[1], INIT);
return ok;
}
bool PlayerControl::sendupdateeqgains()
{
unsigned char buf[ 65535 ];
int bandGain;
uint i;
bool ok = false;
memcpy((void *) buf, (void *) &m_preamp, sizeof(m_preamp));
i = m_equalizerGains.size();
memcpy( (void *) &buf[ sizeof(m_preamp) ], (void *) &i, sizeof(int) );
for ( i = 0; i < m_equalizerGains.size(); i++ )
{
bandGain = m_equalizerGains[i];
memcpy((void *)&buf[ sizeof(m_preamp) + (i+1) * sizeof(int) ], (void *) &bandGain, sizeof(int));
}
for ( i = 0; i < (uint) nNumPlayers; i++ )
ok |= sendmessage(m_children[i].m_pipeB[1], UPDATEETQGAINS, buf, sizeof(m_preamp) + (m_equalizerGains.size()+1) * sizeof(int));
return ok;
}
// children send this!
bool PlayerControl::sendnotifyuser(unsigned long code, const char *moreinfo, const char *moreinfourl)
{
int len1 = strlen(moreinfo), len2 = strlen(moreinfourl), len;
unsigned char buf[65536];
memcpy( (void *) buf, (void *) &code, sizeof(unsigned long) );
len = sizeof(unsigned long);
memcpy( (void *) &buf[ len ], (void *) moreinfo, len1 + 1);
len += len1 + 1;
memcpy( (void *) &buf[ len ], (void *) moreinfourl, len2 + 1);
len += len2 + 1;
return (sendmessage(m_children[m_index].m_pipeA[1], NOTIFYUSER, buf, len));
}
// children send this!
bool PlayerControl::sendinterruptuser(unsigned long code, const char *moreinfo, const char *moreinfourl)
{
int len1 = strlen(moreinfo), len2 = strlen(moreinfourl), len;
unsigned char buf[65536];
memcpy( (void *) buf, (void *) &code, sizeof(unsigned long) );
len = sizeof(unsigned long);
memcpy( (void *) &buf[ len ], (void *) moreinfo, len1 + 1);
len += len1 + 1;
memcpy( (void *) &buf[ len ], (void *) moreinfourl, len2 + 1);
len += len2 + 1;
return (sendmessage(m_children[m_index].m_pipeA[1], INTERRUPTUSER, buf, len));
}
// children send this!
bool PlayerControl::sendcontacting(const char *host)
{
int len = strlen(host);
return (sendmessage(m_children[m_index].m_pipeA[1], CONTACTING, (unsigned char *) host, len + 1));
}
// children send this!
bool PlayerControl::sendbuffering(int percentage)
{
return (sendmessage(m_children[m_index].m_pipeA[1], BUFFERING, (unsigned char *) &percentage, sizeof(unsigned long)));
}
///////////// statics //////////////
bool PlayerControl::getmessage(int fd, msgid &m, unsigned char *buf, int &sz)
{
int nbytes = 0, bytes = 0;
unsigned char mm;
bytes = read(fd, (void *) &mm, 1);
if (bytes <= 0)
return false;
m = (msgid) mm;
nbytes = bytes;
nbytes = 0;
char *tmp = (char *) &sz;
while ( bytes > 0 && 4 != nbytes )
{
bytes = read(fd, (void *) &tmp[ nbytes ], 4 - nbytes);
nbytes += bytes;
}
if (sz)
{
nbytes = 0;
while ( bytes > 0 && sz != nbytes )
{
bytes = read(fd, (void *) &buf[ nbytes ], sz - nbytes);
nbytes += bytes;
}
}
return (nbytes > 0);
}
bool PlayerControl::sendmessage(int fd, msgid m, unsigned char *buf, int sz)
{
unsigned char hdr[5];
hdr[0] = (unsigned char) m;
int ret = 0;
memcpy(&hdr[1], (void *) &sz, 4);
ret = write(fd, (void *) hdr, 5);
if (sz)
ret += write(fd, (void *) buf, sz);
return (ret == (sz + 5));
}
bool PlayerControl::sendsetURL(int fd, const char *url, bool islocal)
{
int len = strlen(url);
unsigned char* buf = new unsigned char[ len + 2 ];
buf[0] = (unsigned char) islocal;
memcpy((void *) &buf[1], (void *) url, len + 1); // go ahead and send the null, what the hell
bool r = sendmessage(fd, SETURL, (unsigned char *)buf, len + 2);
delete [] buf;
return r;
}
bool PlayerControl::sendstart(int fd, bool fadin, unsigned long fadetime)
{
unsigned char buf[32];
buf[0] = (unsigned char) fadin;
memcpy( (void *) &buf[1], (void *) &fadetime, sizeof(unsigned long) );
return sendmessage(fd, START, buf, sizeof(unsigned long) + 1);
}
bool PlayerControl::sendsetvolume(int fd, unsigned long volume)
{
return sendmessage(fd, SETVOLUME, (unsigned char *) &volume, sizeof(unsigned long));
}
bool PlayerControl::sendvolume(int fd, unsigned long volume)
{
return sendmessage(fd, VOLUME, (unsigned char *) &volume, sizeof(unsigned long));
}
bool PlayerControl::sendsetfade(int fd, bool fadeout, unsigned long fadelength)
{
unsigned char buf[ sizeof(bool) + sizeof(unsigned long) ];
buf[0] = (char )fadeout;
memcpy((void *) &buf[1], (void *) &fadelength, sizeof(unsigned long));
return sendmessage(fd, SETFADE, buf, 1 + sizeof(unsigned long));
}
bool PlayerControl::sendplugins(int fd, HelixSimplePlayer *player)
{
unsigned char buf[65536];
int sz, slen;
int nplugins = player->numPlugins();
const char *description, *copyright, *moreinfourl;
memcpy( (void *) buf, (void *) &nplugins, sizeof(nplugins) );
sz = sizeof(nplugins);
for (int i = 0; i < nplugins; i++)
{
player->getPluginInfo(i, description, copyright, moreinfourl);
slen = strlen(description);
memcpy( (void *) &buf[sz], (void *) description, slen + 1 ); // expecting the null
sz += slen + 1;
slen = strlen(copyright);
memcpy( (void *) &buf[sz], (void *) copyright, slen + 1 ); // expecting the null
sz += slen + 1;
slen = strlen(moreinfourl);
memcpy( (void *) &buf[sz], (void *) moreinfourl, slen + 1 ); // expecting the null
sz += slen + 1;
}
cerr << "CHILD: nplugins " << nplugins << " sz " << sz << endl;
return sendmessage(fd, PLUGINS, buf, sz);
}
bool PlayerControl::sendmimetypes(int fd, HelixSimplePlayer *player)
{
unsigned char buf[65536];
int sz, slen;
int mimelistlen = player->getMimeListLen();
const MimeList *mimelisthead = player->getMimeList();
memcpy( (void *) buf, (void *) &mimelistlen, sizeof(mimelistlen) );
sz = sizeof(mimelistlen);
while (mimelisthead)
{
slen = strlen(mimelisthead->mimetypes);
memcpy( (void *) &buf[sz], (void *) mimelisthead->mimetypes, slen + 1 ); // expecting the null
sz += slen + 1;
slen = strlen(mimelisthead->mimeexts);
memcpy( (void *) &buf[sz], (void *) mimelisthead->mimeexts, slen + 1 ); // expecting the null
sz += slen + 1;
mimelisthead = mimelisthead->fwd;
}
return sendmessage(fd, MIMETYPES, buf, sz);
}
bool PlayerControl::sendscopebuf(int fd, DelayQueue *item)
{
unsigned char buf[65536];
int len = 0;
if (len + 2 * sizeof(unsigned long) + 4 * sizeof(int) + sizeof(double) < 65536)
{
memcpy( (void *) buf, (void *) &item->len, sizeof(int) ); len += sizeof(int);
memcpy( (void *) &buf[len], (void *) &item->time, sizeof(unsigned long) ); len += sizeof(unsigned long);
memcpy( (void *) &buf[len], (void *) &item->etime, sizeof(unsigned long) ); len += sizeof(unsigned long);
memcpy( (void *) &buf[len], (void *) &item->nchan, sizeof(int) ); len += sizeof(int);
memcpy( (void *) &buf[len], (void *) &item->bps, sizeof(int) ); len += sizeof(int);
memcpy( (void *) &buf[len], (void *) &item->tps, sizeof(double) ); len += sizeof(double);
memcpy( (void *) &buf[len], (void *) &item->spb, sizeof(int) ); len += sizeof(int);
memcpy( (void *) &buf[len], (void *) item->buf, item->len ); len += item->len;
return sendmessage(fd, SCOPEBUF, buf, len);
}
return false;
}