From cd147d1e93840af816a95f0f27dfce52c1370910 Mon Sep 17 00:00:00 2001 From: Chris Tallon Date: Wed, 2 Apr 2008 20:23:07 +0000 Subject: [PATCH] Live radio half way there --- GNUmakefile | 4 +- afeed.cc | 1 + objects.mk | 2 +- playerlive.h | 57 +++++++ playerliveradio.cc | 402 +++++++++++++++++++++++++++++++++++++++++++++ playerliveradio.h | 121 ++++++++++++++ playerlivetv.cc | 10 +- playerlivetv.h | 36 ++-- vchannellist.cc | 7 +- vvideolivetv.cc | 12 +- vvideolivetv.h | 4 +- 11 files changed, 619 insertions(+), 37 deletions(-) create mode 100644 playerlive.h create mode 100644 playerliveradio.cc create mode 100644 playerliveradio.h diff --git a/GNUmakefile b/GNUmakefile index 11c6f94..ebef069 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -38,8 +38,8 @@ strip: $(STRIP) vompclient install: - rm -f /diskless/nfs/mvp/vompclient - cp vompclient /diskless/nfs/mvp + rm -f /mnt/auto/defiant/diskless/nfs/mvp/vompclient + cp vompclient /mnt/auto/defiant/diskless/nfs/mvp install-wmp: rm -f /diskless/nfs/wmvp/vompclient diff --git a/afeed.cc b/afeed.cc index f650201..9250c03 100644 --- a/afeed.cc +++ b/afeed.cc @@ -77,6 +77,7 @@ void AFeed::threadMethod() if (alen) { cb.call(this); + Log::getInstance()->log("Afeed", Log::DEBUG, "written"); } else { diff --git a/objects.mk b/objects.mk index bd8ee20..0478427 100644 --- a/objects.mk +++ b/objects.mk @@ -18,4 +18,4 @@ OBJECTS1 = command.o log.o tcp.o dsock.o thread.o timers.o i18n.o mutex.o \ vaudioplayer.o audioplayer.o demuxeraudio.o abstractoption.o \ eventdispatcher.o vdrrequestpacket.o vdrresponsepacket.o \ vvideolivetv.o \ - vvideolive.o vlivebanner.o playerlivetv.o + vvideolive.o vlivebanner.o playerlivetv.o playerliveradio.o diff --git a/playerlive.h b/playerlive.h new file mode 100644 index 0000000..4321c77 --- /dev/null +++ b/playerlive.h @@ -0,0 +1,57 @@ +/* + Copyright 2004-2005 Chris Tallon + + This file is part of VOMP. + + VOMP 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. + + VOMP 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 VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef PLAYERLIVE_H +#define PLAYERLIVE_H + +#include "defines.h" + +struct StreamChunk +{ + void* data; + ULONG len; +}; + +struct PLInstruction +{ + UCHAR instruction; + ULONG channelIndex; +}; + +class PlayerLive +{ + public: + virtual ~PlayerLive() {} + + virtual int init()=0; + virtual int shutdown()=0; + + virtual void go(ULONG index)=0; + virtual void setChannel(ULONG index)=0; + virtual void stop()=0; + virtual void setAudioChannel(int newChannel)=0; + + virtual bool* getDemuxerMpegAudioChannels()=0; + virtual bool* getDemuxerAc3AudioChannels()=0; + virtual int getCurrentAudioChannel()=0; +}; + +#endif + diff --git a/playerliveradio.cc b/playerliveradio.cc new file mode 100644 index 0000000..6ea19d6 --- /dev/null +++ b/playerliveradio.cc @@ -0,0 +1,402 @@ +/* + Copyright 2008 Chris Tallon + + This file is part of VOMP. + + VOMP 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. + + VOMP 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 VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "playerliveradio.h" + +#include "log.h" +#include "audio.h" +#include "demuxerts.h" +#include "vdr.h" +#include "messagequeue.h" +#include "remote.h" +#include "message.h" +#include "channel.h" + +// ----------------------------------- Called from outside, one offs or info funcs + +PlayerLiveRadio::PlayerLiveRadio(MessageQueue* tmessageQueue, void* tmessageReceiver, ChannelList* tchanList) +: afeed(this) +{ + messageQueue = tmessageQueue; + messageReceiver = tmessageReceiver; + chanList = tchanList; + + audio = Audio::getInstance(); + logger = Log::getInstance(); + vdr = VDR::getInstance(); + initted = false; + + stopNow = false; + state = S_STOP; +} + +PlayerLiveRadio::~PlayerLiveRadio() +{ + if (initted) shutdown(); +} + +int PlayerLiveRadio::init() +{ + if (initted) return 0; + + demuxer = new DemuxerTS(); + if (!demuxer) return 0; + + if (!demuxer->init(this, audio, NULL, 0, 40000)) + { + logger->log("PlayerLiveRadio", Log::ERR, "Demuxer failed to init"); + shutdown(); + return 0; + } + + afeed.init(); + audio->stop(); + + initted = true; + return 1; +} + +int PlayerLiveRadio::shutdown() +{ + if (!initted) return 0; + stop(); + initted = false; + + delete demuxer; + +#ifdef WIN32 + CloseHandle(mutex); +#endif + + return 1; +} + +bool* PlayerLiveRadio::getDemuxerMpegAudioChannels() +{ + return demuxer->getmpAudioChannels(); +} + +bool* PlayerLiveRadio::getDemuxerAc3AudioChannels() +{ + return demuxer->getac3AudioChannels(); +} + +int PlayerLiveRadio::getCurrentAudioChannel() +{ + return demuxer->getAID(); +} + +void PlayerLiveRadio::setAudioChannel(int newChannel) +{ + return demuxer->setAID(newChannel); +} + +// ----------------------------------- Externally called events + +void PlayerLiveRadio::go(ULONG index) +{ + struct PLInstruction i; + i.instruction = I_SETCHANNEL; + i.channelIndex = index; + instructions.push(i); + threadStart(); +} + +void PlayerLiveRadio::setChannel(ULONG index) +{ + logger->log("PlayerLiveRadio", Log::DEBUG, "setChannel"); + struct PLInstruction i; + i.instruction = I_SETCHANNEL; + i.channelIndex = index; + instructions.push(i); + threadSignalNoLock(); +} + +void PlayerLiveRadio::stop() +{ + logger->log("PlayerLiveRadio", Log::DEBUG, "stop"); + struct PLInstruction i; + i.instruction = I_STOP; + instructions.push(i); + threadSignal(); + threadStop(); +} + +// ----------------------------------- Callback + +void PlayerLiveRadio::call(void* caller) +{ +} + +// ----------------------------------- + +void PlayerLiveRadio::streamReceive(void* data, ULONG len) +{ + if (streamChunks.size() < 11) + { + StreamChunk s; + s.data = data; + s.len = len; + streamChunks.push(s); + threadSignalNoLock(); + } + else + { + // Too many chunks in streamChunks, drop this chunk + free(data); + logger->log("PlayerLiveRadio", Log::WARN, "Dropped chunk"); + } +} + +void PlayerLiveRadio::clearStreamChunks() +{ + while(streamChunks.size()) + { + logger->log("PlayerLiveRadio", Log::DEBUG, "Dropping chunk from old stream"); + struct StreamChunk s = streamChunks.front(); + streamChunks.pop(); + free(s.data); + } +} + +void PlayerLiveRadio::chunkToDemuxer() +{ + StreamChunk s = streamChunks.front(); + streamChunks.pop(); + logger->log("PlayerLiveRadio", Log::DEBUG, "About to call demuxer with %p %lu", s.data, s.len); + int a = demuxer->put((UCHAR*)s.data, s.len); + logger->log("PlayerLiveRadio", Log::DEBUG, "put %i to demuxer", a); + free(s.data); +} + +void PlayerLiveRadio::switchState(UCHAR newState) +{ + logger->log("PlayerLiveRadio", Log::DEBUG, "Switch from state %u to state %u", state, newState); + + switch(state) + { + case S_STOP: // FROM S_STOP + { + switch(newState) + { + case S_PREBUFFERING: + { + audio->stop(); + audio->unPause(); + audio->reset(); + audio->setStreamType(Audio::MPEG2_PES); + audio->systemMuteOff(); + audio->doMuting(); +// audio->sync(); + audio->play(); + audio->pause(); + + demuxer->reset(); +// demuxer->seek(); + + afeed.start(); + + state = newState; + preBufferCount = 0; + return; + } + default: + { + logger->log("PlayerLiveRadio", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState); + abort(); + break; + } + } + } + + case S_PREBUFFERING: // FROM S_PREBUFFERING + { + switch(newState) + { + case S_PLAY: + { + audio->unPause(); + state = newState; + return; + } + case S_STOP: + { + vdr->stopStreaming(); + clearStreamChunks(); + afeed.stop(); + audio->stop(); + audio->reset(); + state = newState; + return; + } + default: + { + logger->log("PlayerLiveRadio", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState); + abort(); + break; + } + } + } + + case S_PLAY: // FROM S_PLAY + { + switch(newState) + { + case S_STOP: + { + vdr->stopStreaming(); + clearStreamChunks(); + afeed.stop(); + audio->stop(); + audio->reset(); + state = newState; + return; + } + case S_PREBUFFERING: // IS THIS HOW IT WORKS? + { + /* + vdr->stopStreaming(); + clearStreamChunks(); + vfeed.stop(); + afeed.stop(); + video->stop(); + video->blank(); + audio->stop(); + audio->unPause(); + audio->reset(); + + video->reset(); + video->sync(); + video->play(); + video->pause(); + + audio->setStreamType(Audio::MPEG2_PES); + audio->sync(); + audio->play(); + audio->pause(); + + demuxer->reset(); + demuxer->seek(); + + afeed.start(); + vfeed.start(); + + state = newState; + preBufferCount = 0; + return; + */ + } + default: + { + logger->log("PlayerLiveRadio", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState); + abort(); + break; + } + } + } + } +} + +void PlayerLiveRadio::optimizeInstructionQueue() +{ + // Walk the list + + // Currently there are only 2 instruction types, so this is a bit overkill... + + struct PLInstruction i; + while(instructions.size() > 1) + { + i = instructions.front(); + if (i.instruction == I_SETCHANNEL) + { + instructions.pop(); // if this is the first of more than 1 command, currently it cannot possibly be relevant + } + else if (i.instruction == I_STOP) + { + return; // return here and ensure the next instruction will be stop + } + } +} + +void PlayerLiveRadio::threadMethod() +{ + while(1) + { + while(!instructions.empty()) + { + if (instructions.size() > 1) optimizeInstructionQueue(); + + struct PLInstruction i = instructions.front(); + instructions.pop(); + + if (i.instruction == I_SETCHANNEL) + { + logger->log("PlayerLiveRadio", Log::DEBUG, "start new stream"); + + switchState(S_PREBUFFERING); + + Channel* chan = (*chanList)[i.channelIndex]; + chan->loadPids(); + + if (chan->numAPids > 0) + { + demuxer->setAID(chan->apids[0].pid); + logger->log("PlayerLiveRadio", Log::DEBUG, "Demuxer pids: %u %u", chan->vpid, chan->apids[0].pid); + } + else + { + logger->log("PlayerLiveRadio", Log::WARN, "Demuxer no pids!"); + } + + vdr->streamChannel(chan->number, this); + } + else if (i.instruction == I_STOP) + { + logger->log("PlayerLiveRadio", Log::DEBUG, "Stopping"); + switchState(S_STOP); + + stopNow = true; + break; + } + } + + if (stopNow) break; + + while(streamChunks.size()) + { + chunkToDemuxer(); + + if (state == S_PREBUFFERING) + { + if (++preBufferCount == preBufferAmount) + { + switchState(S_PLAY); + } + } + } + + threadLock(); + threadWaitForSignal(); // unlocks and waits for signal + threadUnlock(); + } + + logger->log("PlayerLiveRadio", Log::DEBUG, "End of thread"); +} + diff --git a/playerliveradio.h b/playerliveradio.h new file mode 100644 index 0000000..794d502 --- /dev/null +++ b/playerliveradio.h @@ -0,0 +1,121 @@ +/* + Copyright 2008 Chris Tallon + + This file is part of VOMP. + + VOMP 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. + + VOMP 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 VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef PLAYERLIVERADIO_H +#define PLAYERLIVERADIO_H + +#include +#include +#ifndef WIN32 +#include +#endif +#include + +#include + +#include "playerlive.h" + +#ifdef WIN32 +#include "threadwin.h" +#else +#include "threadp.h" +#endif + +#include "callback.h" +#include "defines.h" +#include "afeed.h" +#include "vdr.h" + +class MessageQueue; +class Audio; +class Log; +class DemuxerTS; + +class PlayerLiveRadio : public PlayerLive, public Thread_TYPE, public Callback, public StreamReceiver +{ + public: + PlayerLiveRadio(MessageQueue* messageQueue, void* messageReceiver, ChannelList* chanList); + virtual ~PlayerLiveRadio(); + + virtual int init(); + virtual int shutdown(); + + virtual void go(ULONG index); + virtual void setChannel(ULONG index); + virtual void stop(); + virtual void setAudioChannel(int newChannel); + + virtual bool* getDemuxerMpegAudioChannels(); + virtual bool* getDemuxerAc3AudioChannels(); + virtual int getCurrentAudioChannel(); + + void call(void*); // for callback interface + + virtual void streamReceive(void*, ULONG); // stream receiver interface + + // Player events + + // FIXME so far this just duplicates the old system + the wss + + const static UCHAR CONNECTION_LOST = 1; + const static UCHAR STOP_PLAYBACK = 2; + const static UCHAR STREAM_END = 3; + const static UCHAR ASPECT43 = 4; + const static UCHAR ASPECT169 = 5; + + protected: + void threadMethod(); + void threadPostStopCleanup() {}; + + private: + MessageQueue* messageQueue; + void* messageReceiver; + Log* logger; + Audio* audio; + DemuxerTS* demuxer; + VDR* vdr; + AFeed afeed; + ChannelList* chanList; + + queue instructions; + const static UCHAR I_SETCHANNEL = 1; + const static UCHAR I_STOP = 2; + + queue streamChunks; + + bool initted; + + UCHAR state; + const static UCHAR S_STOP = 1; + const static UCHAR S_PREBUFFERING = 2; + const static UCHAR S_PLAY = 3; + void switchState(UCHAR newState); + + bool stopNow; + int preBufferCount; + const static int preBufferAmount = 3; + + void clearStreamChunks(); + void chunkToDemuxer(); + void optimizeInstructionQueue(); +}; + +#endif + diff --git a/playerlivetv.cc b/playerlivetv.cc index 9584a33..f32bf58 100644 --- a/playerlivetv.cc +++ b/playerlivetv.cc @@ -122,7 +122,7 @@ void PlayerLiveTV::setAudioChannel(int newChannel) void PlayerLiveTV::go(ULONG index) { - struct PLTVInstruction i; + struct PLInstruction i; i.instruction = I_SETCHANNEL; i.channelIndex = index; instructions.push(i); @@ -132,7 +132,7 @@ void PlayerLiveTV::go(ULONG index) void PlayerLiveTV::setChannel(ULONG index) { logger->log("PlayerLiveTV", Log::DEBUG, "setChannel"); - struct PLTVInstruction i; + struct PLInstruction i; i.instruction = I_SETCHANNEL; i.channelIndex = index; instructions.push(i); @@ -142,7 +142,7 @@ void PlayerLiveTV::setChannel(ULONG index) void PlayerLiveTV::stop() { logger->log("PlayerLiveTV", Log::DEBUG, "stop"); - struct PLTVInstruction i; + struct PLInstruction i; i.instruction = I_STOP; instructions.push(i); threadSignal(); @@ -484,7 +484,7 @@ void PlayerLiveTV::optimizeInstructionQueue() // Currently there are only 2 instruction types, so this is a bit overkill... - struct PLTVInstruction i; + struct PLInstruction i; while(instructions.size() > 1) { i = instructions.front(); @@ -514,7 +514,7 @@ void PlayerLiveTV::threadMethod() { if (instructions.size() > 1) optimizeInstructionQueue(); - struct PLTVInstruction i = instructions.front(); + struct PLInstruction i = instructions.front(); instructions.pop(); if (i.instruction == I_SETCHANNEL) diff --git a/playerlivetv.h b/playerlivetv.h index f4de887..a1321a8 100644 --- a/playerlivetv.h +++ b/playerlivetv.h @@ -30,6 +30,8 @@ #include +#include "playerlive.h" + #ifdef WIN32 #include "threadwin.h" #else @@ -48,35 +50,23 @@ class Video; class Log; class DemuxerTS; -struct PLTVInstruction -{ - UCHAR instruction; - ULONG channelIndex; -}; - -struct StreamChunk -{ - void* data; - ULONG len; -}; - -class PlayerLiveTV : public Thread_TYPE, public Callback, public StreamReceiver +class PlayerLiveTV : public PlayerLive, public Thread_TYPE, public Callback, public StreamReceiver { public: PlayerLiveTV(MessageQueue* messageQueue, void* messageReceiver, ChannelList* chanList); virtual ~PlayerLiveTV(); - int init(); - int shutdown(); + virtual int init(); + virtual int shutdown(); - void go(ULONG index); - void setChannel(ULONG index); - void stop(); - void setAudioChannel(int newChannel); + virtual void go(ULONG index); + virtual void setChannel(ULONG index); + virtual void stop(); + virtual void setAudioChannel(int newChannel); - bool* getDemuxerMpegAudioChannels(); - bool* getDemuxerAc3AudioChannels(); - int getCurrentAudioChannel(); + virtual bool* getDemuxerMpegAudioChannels(); + virtual bool* getDemuxerAc3AudioChannels(); + virtual int getCurrentAudioChannel(); void call(void*); // for callback interface @@ -108,7 +98,7 @@ class PlayerLiveTV : public Thread_TYPE, public Callback, public StreamReceiver AFeed afeed; ChannelList* chanList; - queue instructions; + queue instructions; const static UCHAR I_SETCHANNEL = 1; const static UCHAR I_STOP = 2; diff --git a/vchannellist.cc b/vchannellist.cc index d9e2a49..d624b62 100644 --- a/vchannellist.cc +++ b/vchannellist.cc @@ -220,11 +220,12 @@ int VChannelList::handleCommand(int command) if (chanList) chan = (Channel*)sl.getCurrentOptionData(); if (chan == NULL) return 2; - if (chan->type == VDR::VIDEO) - { +// if (chan->type == VDR::VIDEO) +// { VVideoLiveTV* v = new VVideoLiveTV(chanList, chan->number, this); boxstack->add(v); v->go(); +/* } else { @@ -234,7 +235,7 @@ int VChannelList::handleCommand(int command) boxstack->update(v); v->channelChange(VVideoLive::NUMBER, chan->number); } - +*/ return 2; } case Remote::BACK: diff --git a/vvideolivetv.cc b/vvideolivetv.cc index 8f808cc..99ea2e1 100644 --- a/vvideolivetv.cc +++ b/vvideolivetv.cc @@ -22,7 +22,9 @@ #include "vchannellist.h" #include "video.h" +#include "playerlive.h" #include "playerlivetv.h" +#include "playerliveradio.h" #include "channel.h" #include "boxstack.h" #include "colour.h" @@ -69,7 +71,15 @@ VVideoLiveTV::VVideoLiveTV(ChannelList* tchanList, ULONG initialChannelNumber, V eventList = NULL; videoMode = video->getMode(); - player = new PlayerLiveTV(Command::getInstance(), this, chanList); + + if ((*chanList)[currentChannelIndex]->type == VDR::VIDEO) + { + player = new PlayerLiveTV(Command::getInstance(), this, chanList); + } + else + { + player = new PlayerLiveRadio(Command::getInstance(), this, chanList); + } player->init(); setSize(video->getScreenWidth(), video->getScreenHeight()); diff --git a/vvideolivetv.h b/vvideolivetv.h index 6acf8e6..bc048d1 100644 --- a/vvideolivetv.h +++ b/vvideolivetv.h @@ -38,7 +38,7 @@ class Video; class VChannelList; class BoxStack; class WTextbox; -class PlayerLiveTV; +class PlayerLive; class VVideoLiveTV : public Boxx, public TimerReceiver { @@ -68,7 +68,7 @@ class VVideoLiveTV : public Boxx, public TimerReceiver BoxStack* boxstack; VDR* vdr; Video* video; - PlayerLiveTV* player; + PlayerLive* player; bool playing; ChannelList* chanList; VChannelList* vchannelList; -- 2.39.2