diff options
author | phintuka <phintuka> | 2006-06-03 10:04:49 +0000 |
---|---|---|
committer | phintuka <phintuka> | 2006-06-03 10:04:49 +0000 |
commit | 0e345486181ef82b681dd6047f3b6ccb44c77146 (patch) | |
tree | a5401c7f97ab047a0afa890e6806d8537564102a /device.c | |
parent | 321eb114c9fe9abd954ce4270595d53df6cccbae (diff) | |
download | xineliboutput-0e345486181ef82b681dd6047f3b6ccb44c77146.tar.gz xineliboutput-0e345486181ef82b681dd6047f3b6ccb44c77146.tar.bz2 |
Initial importxineliboutput-0_99rc4
Diffstat (limited to 'device.c')
-rw-r--r-- | device.c | 1364 |
1 files changed, 1364 insertions, 0 deletions
diff --git a/device.c b/device.c new file mode 100644 index 00000000..01a25f6e --- /dev/null +++ b/device.c @@ -0,0 +1,1364 @@ +/* + * device.c: xine-lib output device for the Video Disk Recorder + * + * See the main source file 'xineliboutput.c' for copyright information and + * how to reach the author. + * + * $Id: device.c,v 1.1 2006-06-03 10:01:17 phintuka Exp $ + * + */ + +#include <vdr/config.h> +#include <vdr/thread.h> +#include <vdr/dvbspu.h> +#include <vdr/channels.h> +#include <vdr/skins.h> +#include <vdr/status.h> +#include <vdr/remote.h> + +//#define XINELIBOUTPUT_DEBUG +//#define XINELIBOUTPUT_DEBUG_STDOUT +//#define XINELIBOUTPUT_DEBUG_STDERR + +#include "logdefs.h" +#include "config.h" +#include "osd.h" + +#ifdef ENABLE_SUSPEND +# include "tools/timer.h" +# include "tools/timer.c" +# ifdef SUSPEND_BY_PLAYER +# include "dummy_player.h" +# include "dummy_player.c" +# endif +# define ACTIVITY m_inactivityTimer = 0; +#else +# define ACTIVITY +#endif + +#include "tools/listiter.h" +#include "tools/pes.h" + +#include "frontend_local.h" +#include "frontend_svr.h" + +#include "device.h" + +#define STILLPICTURE_REPEAT_COUNT 3 + +//---------------------------- status monitor ------------------------------- + +#define DEBUG_SWITCHING_TIME + +#ifdef DEBUG_SWITCHING_TIME +int64_t switchtimeOff = 0LL; +int64_t switchtimeOn = 0LL; +bool switchingIframe; +#endif + +class cXinelibStatusMonitor : public cStatus +{ + private: + cXinelibStatusMonitor(); + cXinelibStatusMonitor(cXinelibStatusMonitor&); + + public: + cXinelibStatusMonitor(cXinelibDevice& device, int cardIndex) : + m_Device(device), m_cardIndex(cardIndex) {}; + + protected: + virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber); +#if VDRVERSNUM < 10338 + virtual void Replaying(const cControl *Control, const char *Name); +#else + virtual void Replaying(const cControl *Control, const char *Name, + const char *FileName, bool On); +#endif + + cXinelibDevice& m_Device; + int m_cardIndex; +}; + +void cXinelibStatusMonitor::ChannelSwitch(const cDevice *Device, + int ChannelNumber) +{ + TRACEF("cXinelibStatusMonitor::ChannelSwitch"); + + if (ChannelNumber) { + if (Device->CardIndex() == m_cardIndex) { +#ifdef DEBUG_SWITCHING_TIME + switchtimeOn = cTimeMs::Now(); +#endif + m_Device.SetTvMode(Channels.GetByNumber(ChannelNumber)); + TRACE("cXinelibStatusMonitor: Set to TvMode"); + } + } else { + if (Device->CardIndex() == m_cardIndex) { +#ifdef DEBUG_SWITCHING_TIME + switchtimeOff = cTimeMs::Now(); +#endif + m_Device.StopOutput(); + TRACE("cXinelibStatusMonitor: received stop"); + } + } +} + +#if VDRVERSNUM < 10338 +void cXinelibStatusMonitor::Replaying(const cControl *Control, + const char *Name) +{ + TRACEF("cXinelibStatusMonitor::Replaying"); + + if (Name != NULL) { + TRACE("cXinelibStatusMonitor: Replaying " << Name); + m_Device.SetReplayMode(); + } +} +#else +void cXinelibStatusMonitor::Replaying(const cControl *Control, + const char *Name, + const char *FileName, bool On) +{ + TRACEF("cXinelibStatusMonitor::Replaying"); + + if (On /*&& Name != NULL*/) { + TRACE("cXinelibStatusMonitor: Replaying " << Name << "("<<FileName")"); + m_Device.SetReplayMode(); + } +} +#endif + +//----------------------------- device ---------------------------------------- + +#ifdef USENOPPLAYER +class cNopControl : public cControl { +public: + cNopControl() : cControl(new cPlayer()) + { LOGMSG("cNopControl created"); } + ~cNopControl() + { LOGMSG("cNopControl destroyed"); } + virtual void Hide(void) {}; + + static int NopPlayerActivated; +}; + +int cNopControl::NopPlayerActivated = 0; +#endif + +// +// Singleton +// + +cXinelibDevice* cXinelibDevice::m_pInstance = NULL; + +cXinelibDevice& cXinelibDevice::Instance(void) +{ + TRACEF("cXinelibDevice::Instance"); + if (!m_pInstance) { + m_pInstance = new cXinelibDevice(); + TRACE("cXinelibDevice::Instance(): create, cardindex = " + << m_pInstance->CardIndex()); + } + + return *m_pInstance; +} + +void cXinelibDevice::Dispose(void) +{ + TRACEF("cXinelibDevice::Dispose"); + delete m_pInstance; + m_pInstance = NULL; +} + +// +// init and shutdown +// + +cXinelibDevice::cXinelibDevice() +{ + TRACEF("cXinelibDevice::cXinelibDevice"); + + m_statusMonitor = NULL; + m_spuDecoder = NULL; + + m_local = NULL; + m_server = NULL; + + if(*xc.local_frontend && strncmp(xc.local_frontend, "none", 4)) + m_clients.Add(m_local = new cXinelibLocal(xc.local_frontend)); + if(xc.remote_mode && xc.listen_port>0) + m_clients.Add(m_server = new cXinelibServer(xc.listen_port)); + + m_ac3Present = false; + m_spuPresent = false; +#ifdef ENABLE_SUSPEND + m_suspended = false; + ACTIVITY +#endif + m_liveMode = false; + m_TrickSpeed = -1; + m_SkipAudio = false; + m_PlayingFile = false; + m_StreamStart = true; + m_RadioStream = false; + m_AudioCount = 0; +} + +cXinelibDevice::~cXinelibDevice() +{ + TRACEF("cXinelibDevice::~cXinelibDevice"); + + StopDevice(); + + m_pInstance = NULL; +} + +bool cXinelibDevice::StartDevice() +{ + TRACEF("cXinelibDevice::StartDevice"); + + // if(dynamic_cast<cXinelibLocal*>(it)) + if(m_local) { + m_local->Start(); + while(!m_local->IsReady()) { + cCondWait::SleepMs(100); + if(m_local->IsFinished()) { + LOGMSG("cXinelibDevice::Start(): Local frontend init failed"); + return false; + } + } + if(xc.force_primary_device) + ForcePrimaryDevice(true); + } + + if(m_server) { + m_server->Start(); + while(!m_server->IsReady()) { + cCondWait::SleepMs(100); + if(m_server->IsFinished()) { + LOGMSG("cXinelibDevice::Start(): Server init failed"); + return false; + } + } + } + +#ifdef ENABLE_SUSPEND + m_suspended = false; + ACTIVITY +#endif + + m_statusMonitor = new cXinelibStatusMonitor(*this, CardIndex()); + +#ifdef ENABLE_SUSPEND + CreateTimerEvent(this, &cXinelibDevice::CheckInactivityTimer, 60*1000, false); +#endif + +#ifdef USENOPPLAYER + if(!m_local) { + LOGMSG("No clients, activating cNopControl"); + cControl::Launch(new cNopControl); + cNopControl::NopPlayerActivated = true; + } +#endif + + LOGDBG("cXinelibDevice::StartDevice(): Device started"); + return true; +} + +void cXinelibDevice::StopDevice(void) +{ + TRACEF("cXinelibDevice::StopDevice"); + LOGDBG("cXinelibDevice::StopDevice(): Stopping device ..."); +#ifdef ENABLE_SUSPEND + CancelTimerEvents(this); +#endif + if(m_statusMonitor) { + delete m_statusMonitor; + m_statusMonitor = NULL; + } + if (m_spuDecoder) { + delete m_spuDecoder; + m_spuDecoder = NULL; + } + + ForEach(m_clients, &cXinelibThread::SetLiveMode, false); + TrickSpeed(-1); + ForEach(m_clients, &cXinelibThread::Stop); + + m_local = m_server = NULL; + m_clients.Clear(); +} + +void cXinelibDevice::MakePrimaryDevice(bool On) +{ + TRACEF("cXinelibDevice::MakePrimaryDevice"); + if(On) + new cXinelibOsdProvider(this); +} + +void cXinelibDevice::ForcePrimaryDevice(bool On) +{ + static int Original = 0; + static int Counter = 0; + + TRACEF("cXinelibDevice::ForcePrimaryDevice"); + + if(On) { + Counter++; +#ifdef USENOPPLAYER + if(cNopControl::NopPlayerActivated) { + LOGMSG("First client, stopping cNopControl"); + cControl::Shutdown(); + cNopControl::NopPlayerActivated = false; + } +#endif + if(xc.force_primary_device) { + if(cDevice::PrimaryDevice() && this != cDevice::PrimaryDevice()) { + /* TODO: may need to use vdr main thread for this */ + Original = cDevice::PrimaryDevice()->DeviceNumber() + 1; + cControl::Shutdown(); + LOGMSG("Forcing primary device, original index = %d", Original); + if(cOsd::IsOpen()) { + LOGMSG("Forcing primary device, old OSD still open !"); +#if VDRVERSNUM >= 10400 + xc.main_menu_mode = CloseOsd; + cRemote::CallPlugin("xineliboutput"); +#endif + } + SetPrimaryDevice(DeviceNumber() + 1); + } + } + + } else /* Off */ { + Counter--; + if(Counter<0) + LOGMSG("Internal error (ForcePrimaryDevice < 0)"); + if(!Counter) { + if(Original) { + LOGMSG("Restoring original primary device %d", Original); + cControl::Shutdown(); + if(cOsd::IsOpen()) { + LOGMSG("Restoring primary device, xineliboutput OSD still open !"); +#if VDRVERSNUM >= 10400 + xc.main_menu_mode = CloseOsd; + cRemote::CallPlugin("xineliboutput"); +#endif + } + cDevice::SetPrimaryDevice(Original); + Original = 0; + } +#ifdef USENOPPLAYER + if(!m_local && !cControl::Control()) { + LOGMSG("No clients, activating cNopControl"); + cControl::Launch(new cNopControl); + cNopControl::NopPlayerActivated = true; + } +#endif + } + } +} + + +// +// Configuration +// + +void cXinelibDevice::ConfigureOSD(bool prescale_osd, bool unscaled_osd) +{ + TRACEF("cXinelibDevice::ConfigureOSD"); + + ACTIVITY + if(m_local) + m_local->ConfigureOSD(prescale_osd, unscaled_osd); + if(m_server) + m_server->ConfigureOSD(prescale_osd, unscaled_osd); +} + +void cXinelibDevice::ConfigurePostprocessing(char *deinterlace_method, int audio_delay, int audio_compression, int *audio_equalizer, int audio_surround) +{ + TRACEF("cXinelibDevice::ConfigurePostprocessing"); + + ACTIVITY + if(m_local) + m_local->ConfigurePostprocessing(deinterlace_method, audio_delay, + audio_compression, audio_equalizer, + audio_surround); + if(m_server) + m_server->ConfigurePostprocessing(deinterlace_method, audio_delay, + audio_compression, audio_equalizer, + audio_surround); +} + +void cXinelibDevice::ConfigurePostprocessing(char *name, bool on, char *args) +{ + TRACEF("cXinelibDevice::ConfigurePostprocessing"); + + ACTIVITY + if(m_local) + m_local->ConfigurePostprocessing(name, on, args); + if(m_server) + m_server->ConfigurePostprocessing(name, on, args); +} + +void cXinelibDevice::ConfigureVideo(int hue, int saturation, int brightness, int contrast) +{ + TRACEF("cXinelibDevice::ConfigureVideo"); + + ACTIVITY + if(m_local) + m_local->ConfigureVideo(hue, saturation, brightness, contrast); + if(m_server) + m_server->ConfigureVideo(hue, saturation, brightness, contrast); +} + +void cXinelibDevice::ConfigureDecoder(int pes_buffers, int priority) +{ + TRACEF("cXinelibDevice::ConfigureDecoder"); + + ACTIVITY + if(m_local) + m_local->ConfigureDecoder(pes_buffers, priority); + //if(m_server) + // m_server->ConfigureDecoder(pes_buffers, priority); + + cXinelibOsdProvider::RefreshOsd(); +} + +void cXinelibDevice::ConfigureWindow(int fullscreen, int width, int height, + int modeswitch, char *modeline, + int aspect, int scale_video, int field_order) +{ + TRACEF("cXinelibDevice::ConfigureWindow"); + + ACTIVITY + if((!*xc.local_frontend || !strncmp(xc.local_frontend, "none", 4)) && m_local) { + cXinelibThread *tmp = m_local; + m_clients.Del(tmp, false); + m_local = NULL; + cCondWait::SleepMs(5); + tmp->Stop(); + cCondWait::SleepMs(5); + delete tmp; + if(xc.force_primary_device) + ForcePrimaryDevice(false); + } + if(m_local) + m_local->ConfigureWindow(fullscreen, width, height, modeswitch, modeline, + aspect, scale_video, field_order); + else if(*xc.local_frontend && strncmp(xc.local_frontend, "none", 4)) { + cXinelibThread *tmp = new cXinelibLocal(xc.local_frontend); + tmp->Start(); + m_clients.Add(m_local = tmp); + + cCondWait::SleepMs(25); + while(!m_local->IsReady() && !m_local->IsFinished()) + cCondWait::SleepMs(25); + + if(m_local->IsFinished()) { + m_local = NULL; + m_clients.Del(tmp, true); + Skins.QueueMessage(mtError, tr("Frontend initialization failed"), 10); + } else { + if(xc.force_primary_device) + ForcePrimaryDevice(true); + + m_local->ConfigureWindow(fullscreen, width, height, modeswitch, modeline, + aspect, scale_video, field_order); + } + } +} + +void cXinelibDevice::Listen(bool activate, int port) +{ + TRACEF("cXinelibDevice::Listen"); + + ACTIVITY + if(activate && port>0) { + if(!m_server) { + cXinelibThread *tmp = new cXinelibServer(port); + tmp->Start(); + m_clients.Add(m_server = tmp); + + cCondWait::SleepMs(10); + while(!m_server->IsReady() && !m_server->IsFinished()) + cCondWait::SleepMs(10); + + if(m_server->IsFinished()) { + Skins.QueueMessage(mtError, tr("Server initialization failed"), 10); + m_server = NULL; + m_clients.Del(tmp, true); + } + + } else { + if(! m_server->Listen(port)) + Skins.QueueMessage(mtError, tr("Server initialization failed"), 10); + } + } else if( /*((!activate) || port<=0) && */ m_server) { + cXinelibThread *tmp = m_server; + m_clients.Del(tmp, false); + m_server = NULL; + cCondWait::SleepMs(5); + tmp->Stop(); + cCondWait::SleepMs(5); + delete tmp; + } +} + +// +// OSD +// + +void cXinelibDevice::OsdCmd(void *cmd) +{ + TRACEF("cXinelibDevice::OsdCmd"); + + ACTIVITY + if(m_server) // call first server, local frontend modifies contents of the message ... + m_server->OsdCmd(cmd); + if(m_local) + m_local->OsdCmd(cmd); +} + +// +// Play mode control +// + +void cXinelibDevice::StopOutput(void) +{ + TRACEF("cXinelibDevice::StopOutput"); + + ACTIVITY + m_RadioStream = false; + m_AudioCount = 0; + + ForEach(m_clients, &cXinelibThread::SetLiveMode, false); + Clear(); + ForEach(m_clients, &cXinelibThread::QueueBlankDisplay); + ForEach(m_clients, &cXinelibThread::SetNoVideo, false); +} + +void cXinelibDevice::SetTvMode(cChannel *Channel) +{ + TRACEF("cXinelibDevice::SetTvMode"); + + m_RadioStream = false; + if (Channel && !Channel->Vpid() && (Channel->Apid(0) || Channel->Apid(1))) + m_RadioStream = true; + if(/*playMode==pmAudioOnly||*/playMode==pmAudioOnlyBlack) + m_RadioStream = true; + TRACE("cXinelibDevice::SetTvMode - isRadio = "<<m_RadioStream); + + m_StreamStart = true; + m_liveMode = true; + ACTIVITY + m_TrickSpeed = -1; + m_SkipAudio = false; + m_AudioCount = 0; + + Clear(); + ForEach(m_clients, &cXinelibThread::SetNoVideo, m_RadioStream); + ForEach(m_clients, &cXinelibThread::SetLiveMode, true); + ForEach(m_clients, &cXinelibThread::QueueBlankDisplay); + ForEach(m_clients, &cXinelibThread::ResumeOutput); +} + +void cXinelibDevice::SetReplayMode(void) +{ + TRACEF("cXinelibDevice::SetReplayMode"); + + //m_RadioStream = false; +#if 1 + //m_RadioStream = (playMode==pmAudioOnly || playMode==pmAudioOnlyBlack); + //TRACE("cXinelibDevice::SetReplayMode - isRadio = "<<m_RadioStream); + m_RadioStream = true; // first seen replayed video packet resets this + m_AudioCount = 15; +#endif + + ForEach(m_clients, &cXinelibThread::SetLiveMode, false); + TrickSpeed(-1); + ForEach(m_clients, &cXinelibThread::Clear); + ForEach(m_clients, &cXinelibThread::SetNoVideo, false /*m_RadioStream*/); + if(m_RadioStream && !m_liveMode) + ForEach(m_clients, &cXinelibThread::BlankDisplay); + ForEach(m_clients, &cXinelibThread::ResumeOutput); + + m_liveMode = false; + ACTIVITY +} + +bool cXinelibDevice::SetPlayMode(ePlayMode PlayMode) +{ + TRACEF("cXinelibDevice::SetPlayMode"); + + ACTIVITY + +#ifdef XINELIBOUTPUT_DEBUG + switch (PlayMode) { + case pmNone: + TRACE("cXinelibDevice::SetPlayMode audio/video from decoder"); break; + case pmAudioVideo: + TRACE("cXinelibDevice::SetPlayMode audio/video from player"); break; + case pmVideoOnly: + TRACE("cXinelibDevice::SetPlayMode video from player, audio from decoder"); break; + case pmAudioOnly: + TRACE("cXinelibDevice::SetPlayMode audio from player, video from decoder"); break; + case pmAudioOnlyBlack: + TRACE("cXinelibDevice::SetPlayMode audio only from player, no video (black screen)"); break; + case pmExtern_THIS_SHOULD_BE_AVOIDED: + TRACE("cXinelibDevice::SetPlayMode this should be avoided"); break; + } +#endif + + m_ac3Present = false; + m_spuPresent = false; + playMode = PlayMode; + + TrickSpeed(-1); + if (playMode == pmAudioOnlyBlack /*|| playMode == pmNone*/) { + TRACE("pmAudioOnlyBlack --> BlankDisplay, NoVideo"); + ForEach(m_clients, &cXinelibThread::BlankDisplay); + ForEach(m_clients, &cXinelibThread::SetNoVideo, true); + } + + return true; +} + +// +// Playback control +// + +void cXinelibDevice::TrickSpeed(int Speed) +{ + TRACEF("cXinelibDevice::TrickSpeed"); + + int RealSpeed = abs(Speed); + ACTIVITY + m_TrickSpeed = Speed; + m_TrickSpeedPts = 0; + + ForEach(m_clients, &cXinelibThread::TrickSpeed, RealSpeed); +} + +void cXinelibDevice::Clear(void) +{ + TRACEF("cXinelibDevice::Clear"); + m_StreamStart = 1; + TrickSpeed(-1); + ForEach(m_clients, &cXinelibThread::Clear); +} + +void cXinelibDevice::Play(void) +{ + TRACEF("cXinelibDevice::Play"); + + ACTIVITY + m_SkipAudio = false; + + ForEach(m_clients, &cXinelibThread::SetLiveMode, false); + TrickSpeed(-1); +} + +void cXinelibDevice::Freeze(void) +{ + TRACEF("cXinelibDevice::Freeze"); + + ACTIVITY + + TrickSpeed(0); +} + +// +// Suspend device, inactivity timer +// +#ifdef ENABLE_SUSPEND +void cXinelibDevice::CheckInactivityTimer() +{ + TRACEF("cXinelibDevice::CheckInactivityTimer"); + + Lock(); + + if(xc.inactivity_timer>0) { + int old_Timer = m_inactivityTimer++; + TRACE("cXinelibDevice::CheckInactivityTimer @" << time(NULL)); + TRACE("cXinelibDevice::CheckInactivityTimer: m_inactivityTimer = " << m_inactivityTimer); + + if(old_Timer<=xc.inactivity_timer && m_inactivityTimer>xc.inactivity_timer) { + SuspendedAction(); + Unlock(); +# ifndef SUSPEND_BY_PLAYER + CreateTimerEvent(this, &cXinelibDevice::SuspendedAction, 5000); +# endif + return; + } + } + Unlock(); +} + +bool cXinelibDevice::SuspendedAction(void) +{ + TRACEF("cXinelibDevice::SuspendedAction"); + + LOCK_THREAD; + if(m_suspended || (xc.inactivity_timer>0 && m_inactivityTimer>xc.inactivity_timer)) { + if(m_liveMode) { +# ifndef SUSPEND_BY_PLAYER + TRACE("cXinelibDevice::SuspendedAction - DECODER SUSPENDED"); + ForEach(m_clients, &cXinelibThread::SetLiveMode, false); + ForEach(m_clients, &cXinelibThread::LogoDisplay); +# else + if(!cDummyPlayerControl::IsOpen()) + cControl::Launch(new cDummyPlayerControl); +# endif + } + return true; + } + +# ifndef SUSPEND_BY_PLAYER + ForEach(m_clients, &cXinelibThread::SetLiveMode, m_liveMode); +# else + if(cDummyPlayerControl::IsOpen()) + cDummyPlayerControl::Close(); +# endif + + return false; +} + +void cXinelibDevice::Suspend(bool onoff) +{ + TRACEF("cXinelibDevice::Suspend"); + TRACE("cXinelibDevice::Suspend = " << onoff); + + Lock(); + ACTIVITY + + if(!m_suspended && onoff) { + m_suspended = onoff; + SuspendedAction(); + Unlock(); +#ifndef SUSPEND_BY_PLAYER + CreateTimerEvent(this, &cXinelibDevice::SuspendedAction, 5000); +#endif + return; + } + m_suspended = onoff; + Unlock(); +} +#endif // ENABLE_SUSPEND + +// +// Playback of files and images +// + +int cXinelibDevice::PlayFileCtrl(const char *Cmd) +{ + TRACEF("cXinelibDevice::PlayFile"); + int result = -1; + + if(m_PlayingFile) { + if(m_server) + result = m_server->PlayFileCtrl(Cmd); + if(m_local) + result = m_local->PlayFileCtrl(Cmd); + } + return result; +} + +bool cXinelibDevice::EndOfStreamReached(void) +{ + return (((!m_server) || m_server->EndOfStreamReached()) && + ((!m_local) || m_local->EndOfStreamReached())); +} + +bool cXinelibDevice::PlayFile(const char *FileName, int Position, bool LoopPlay) +{ + TRACEF("cXinelibDevice::PlayFile"); + TRACE("cXinelibDevice::PlayFile(\"" << FileName << "\")"); + + bool result = true; + + if(FileName) { + if(!m_PlayingFile) { + m_PlayingFile = true; + StopOutput(); + } + result = (((!m_server) || + m_server->PlayFile(FileName, Position, LoopPlay)) && + ((!m_local) || + m_local->PlayFile(FileName, Position, LoopPlay))); + } else if(/*!FileName &&*/m_PlayingFile) { + result = (((!m_server) || m_server->PlayFile(NULL, 0)) && + ((!m_local) || m_local->PlayFile(NULL, 0))); + if(!m_liveMode) + SetReplayMode(); + else + SetTvMode(Channels.GetByNumber(cDevice::CurrentChannel())); + m_PlayingFile = false; + } + + return result; +} + +// +// Data stream handling +// + +int cXinelibDevice::PlayAny(const uchar *buf, int length) +{ + TRACEF("cXinelibDevice::PlayAny"); + + if(m_PlayingFile) + return length; + +#ifdef ENABLE_SUSPEND + if(m_suspended || (xc.inactivity_timer > 0 && + m_inactivityTimer > xc.inactivity_timer)) { + if(m_liveMode) { + return length; + } + } +#endif + + bool isMpeg1 = false; + + int len = pes_packet_len(buf, length, isMpeg1); + if(len>0 && len != length) + LOGMSG("cXinelibDevice::PlayAny: invalid data !"); + + // strip timestamps in trick speed modes + if(m_SkipAudio || m_TrickSpeed > 0) { + if(!m_SkipAudio) { + +#ifdef TEST_TRICKSPEEDS +#warning Experimental trickspeed mode handling included ! + // TODO: re-gen pts or signal pts+trickspeed for udp scheduler + bool Video = false, Audio = false; + uchar PictureType = NO_PICTURE; + int64_t pts = pes_extract_pts(buf, length, Audio, Video); + if(m_TrickSpeedPts <= 0LL) { + if(pts>0 && Video) { + m_TrickSpeedPts = pts; + if(ScanVideoPacket(buf, length, PictureType) > 0) + ; + LOGMSG("TrickSpeed: VIDEO PTS %lld (%s)", pts, + PictureTypeStr(PictureType)); + } + } else if(Audio) { + LOGMSG("TrickSpeed: AUDIO PTS %lld", pts); + } else if(pts > 0LL) { + if(ScanVideoPacket(buf, length, PictureType) > 0) + ; + LOGMSG("TrickSpeed: VIDEO PTS DIFF %lld (%s)", pts - m_TrickSpeedPts, + PictureTypeStr(PictureType)); + //m_TrickSpeedPts += (int64_t)(40*90 * m_TrickSpeed); /* 40ms * 90kHz */ + //pes_change_pts((uchar *)buf, length); + } + pes_strip_pts((uchar*)buf, length); + +#else + pes_strip_pts((uchar*)buf, length); +#endif + } else { + pes_strip_pts((uchar*)buf, length); + } + } + + if(m_local) { + length = (isMpeg1 ? m_local->Play_Mpeg1_PES(buf,length) : + m_local->Play_PES(buf,length)); + } + if(m_server && length > 0) { + int length2 = isMpeg1 ? m_server->Play_Mpeg1_PES(buf, length) : + m_server->Play_PES(buf, length); + if(!m_local) + return length2; + } + + return length; +} + +int cXinelibDevice::PlayVideo(const uchar *buf, int length) +{ + TRACEF("cXinelibDevice::PlayVideo"); + + if(m_RadioStream) { + m_RadioStream = false; + m_AudioCount = 0; + ForEach(m_clients, &cXinelibThread::SetNoVideo, m_RadioStream); + } + +#ifdef START_IFRAME + // Start with I-frame if stream has video + if(m_StreamStart) { + // wait for first I-frame + uchar pictureType; + if( ScanVideoPacket(buf, length, /*0,*/pictureType) > 0 && + pictureType == I_FRAME) { + m_StreamStart = false; + } else { + return length; + } + } +#else + m_StreamStart = false; +#endif + +#ifdef DEBUG_SWITCHING_TIME + if(switchtimeOff && switchtimeOn) { + uchar pictureType; + if( ScanVideoPacket(buf, length, /*0,*/pictureType) > 0 && + pictureType == I_FRAME) { + if(!switchingIframe) { + int64_t now = cTimeMs::Now(); + switchingIframe = true; + LOGMSG("Channel switch: off -> on %lld ms, on -> 1. I-frame %lld ms", + switchtimeOn-switchtimeOff, now-switchtimeOn); + } else { + int64_t now = cTimeMs::Now(); + LOGMSG("Channel switch: on -> 2. I-frame %lld ms, Total %lld ms", + now-switchtimeOn, now-switchtimeOff); + switchtimeOff = 0LL; + switchtimeOn = 0LL; + switchingIframe = false; + } + } + } +#endif + + return PlayAny(buf, length); +} + +void cXinelibDevice::StillPicture(const uchar *Data, int Length) +{ + TRACEF("cXinelibDevice::StillPicture"); + + bool isPes = (!Data[0] && !Data[1] && Data[2] == 0x01 && + (Data[3] & 0xF0) == 0xE0); + bool isMpeg1 = isPes && ((Data[6] & 0xC0) != 0x80); + int i; + + if(m_PlayingFile) + return; + + TRACE("cXinelibDevice::StillPicture: isPes = "<<isPes + <<", isMpeg1 = "<<isMpeg1); + + ForEach(m_clients, &cXinelibThread::Clear); + ForEach(m_clients, &cXinelibThread::SetNoVideo, false); + ForEach(m_clients, &cXinelibThread::SetLiveMode, false); + ForEach(m_clients, &cXinelibThread::SetStillMode, true); + ForEach(m_clients, &cXinelibThread::TrickSpeed, 1); + + m_TrickSpeed = -1; // to make Poll work ... + m_SkipAudio = 1; // enables audio and pts stripping + + for(i=0; i<STILLPICTURE_REPEAT_COUNT; i++) + if(isMpeg1) { + ForEach(m_clients, &cXinelibThread::Play_Mpeg1_PES, Data, Length, + &mmin<int>, Length); + } else if(isPes) { + /*cDevice::*/PlayPes(Data, Length, m_SkipAudio); + } else { + ForEach(m_clients, &cXinelibThread::Play_Mpeg2_ES, + Data, Length, VIDEO_STREAM, + &mand<bool>, true); + } + + ForEach(m_clients, &cXinelibThread::Play_Mpeg2_ES, + Data, 0, VIDEO_STREAM, + &mand<bool>, true); + +#if 0 + if(m_server) + for(i=0; i<5; i++) + if(m_server->Flush(50)) + break; + else + LOGMSG("cXinelibDevice::StillPicture: retry server flush (%d)", i+1); +#endif + m_TrickSpeed = 0; + m_SkipAudio = 0; +} + +int cXinelibDevice::PlayAudio(const uchar *buf, int length, uchar Id) +{ + TRACEF("cXinelibDevice::PlayAudio"); + +#ifdef SKIP_AC3_AUDIO + // skip AC3 audio + if(((unsigned char *)buf)[3] == PRIVATE_STREAM1) { + TRACE("cXinelibDevice::PlayVideo: PRIVATE_STREAM1 discarded"); + return length; + } +#endif + + // strip audio in trick speed modes + if(m_SkipAudio || m_TrickSpeed > 0) + return length; + + if(m_RadioStream) { + if(m_AudioCount) { + m_AudioCount--; + if(!m_AudioCount) + ForEach(m_clients, &cXinelibThread::SetNoVideo, m_RadioStream); + } + } + + return PlayAny(buf, length); +} + +int cXinelibDevice::PlaySpu(const uchar *buf, int length, uchar Id) +{ + TRACEF("cXinelibDevice::PlaySpu"); + +#ifdef SKIP_DVDSPU + return length; +#else + if(((unsigned char *)buf)[3] == PRIVATE_STREAM1) { + + if(!m_spuPresent) { + TRACE("cXinelibDevice::PlaySpu first DVD SPU frame"); + Skins.QueueMessage(mtInfo,"DVD SPU"); + m_spuPresent = true; + } + } + + // + // TODO: channel must be selectable + // + // use: cXinelibThread::SpuStreamChanged(int StreamId); + // + + return PlayAny(buf, length); +#endif +} + +void cXinelibDevice::SetVolumeDevice(int Volume) +{ + TRACEF("cXinelibDevice::SetVolumeDevice"); + + ACTIVITY + ForEach(m_clients, &cXinelibThread::SetVolume, Volume); +} + +void cXinelibDevice::SetAudioTrackDevice(eTrackType Type) +{ + TRACEF("cXinelibDevice::SetAudioTrackDevice"); + + LOGDBG("SetAudioTrackDevice(%d)", (int)Type); + if(IS_DOLBY_TRACK(Type)) + ForEach(m_clients, &cXinelibThread::AudioStreamChanged, + true, (int)(Type - ttDolbyFirst)); + if(IS_AUDIO_TRACK(Type)) + ForEach(m_clients, &cXinelibThread::AudioStreamChanged, + false, AUDIO_STREAM + (int)(Type - ttAudioFirst)); +} + +void cXinelibDevice::SetAudioChannelDevice(int AudioChannel) +{ + TRACEF("cXinelibDevice::SetAudioChannelDevice"); + + LOGDBG("SetAudioChannelDevice(%d)", (int)AudioChannel); + m_AudioChannel = AudioChannel; + // + // TODO + // + // - stereo, left only, right only + // +} + +void cXinelibDevice::SetDigitalAudioDevice(bool On) +{ + TRACEF("cXinelibDevice::SetDigitalAudioDevice"); + + LOGDBG("SeDigitalAudioDevice(%s)", On ? "on" : "off"); + // + // should we do something here ??? + // +} + +void cXinelibDevice::SetVideoFormat(bool VideoFormat16_9) +{ + TRACEF("cXinelibDevice::SetVideoFormat"); + + LOGDBG("SetVideoFormat(%s)", VideoFormat16_9 ? "16:9" : "4:3"); + ACTIVITY + cDevice::SetVideoFormat(VideoFormat16_9); + + // + // TODO + // +#if 0 + if(xc.aspect != ASPECT_AUTO && + xc.aspect != ASPECT_DEFAULT) { + if(VideoFormat16_9) + xc.aspect = ASPECT_16_9; + else if(xc.aspect == ASPECT_16_9) + xc.aspect = ASPECT_4_3; + ConfigureDecoder(,,,xc.aspect,,,); + } +#endif +} + +void cXinelibDevice::SetVideoDisplayFormat(eVideoDisplayFormat VideoDisplayFormat) +{ + TRACEF("cXinelibDevice::SetVideoDisplayFormat"); + + LOGDBG("SetVideoDisplayFormat(%d)", VideoDisplayFormat); + cDevice::SetVideoDisplayFormat(VideoDisplayFormat); + // + // TODO + // + // - set normal, pan&scan, letterbox (only for 4:3?) + // +#if 0 + if(xc.aspect != ASPECT_AUTO && + xc.aspect != ASPECT_DEFAULT) { + switch(VideoDisplayFormat) { + case vdfPanAndScan: + xc.aspect = ASPECT_PAN_SCAN; + break; + case vdfLetterBox: + xc.aspect = ASPECT_4_3; /* borders are added automatically if needed */ + break; + case vdfCenterCutOut: + xc.aspect = ASPECT_CENTER_CUT_OUT; + break; + } + ConfigureDecoder(,,,xc.aspect,,,); + } +#endif +} + +eVideoSystem cXinelibDevice::GetVideoSystem(void) +{ + TRACEF("cXinelibDevice::GetVideoSystem"); + return cDevice::GetVideoSystem(); +} + +bool cXinelibDevice::Poll(cPoller &Poller, int TimeoutMs) +{ + TRACEF("cXinelibDevice::Poll"); + + if(m_PlayingFile) + return true; + + if(m_TrickSpeed == 0) { + cCondWait::SleepMs(TimeoutMs); + return Poller.Poll(0); + } + + if(!m_local && !m_server) { + /* nothing to do... why do I exist ... ? */ + cCondWait::SleepMs(TimeoutMs); + return Poller.Poll(0); + } + + bool result = true; + + if(m_local) + result = result && m_local->Poll(Poller, TimeoutMs); + if(m_server) + result = result && m_server->Poll(Poller, TimeoutMs); + + return result /*|| Poller.Poll(0)*/; +} + +bool cXinelibDevice::Flush(int TimeoutMs) +{ + TRACEF("cXinelibDevice::Flush"); + + if(m_TrickSpeed == 0) { + ForEach(m_clients, &cXinelibThread::SetLiveMode, false); + TrickSpeed(-1); + } + + bool r = ForEach(m_clients, &cXinelibThread::Flush, TimeoutMs, + &mand<bool>, true); + + return r; +} + +#if 0 +// +// TODO +// - forward spu's directly to Xine +// +class cXineSpuDecoder : public cDvbSpuDecoder +{ + private: + cSpuDecoder::eScaleMode scaleMode; + cXinelibDevice *m_Device; + + public: + cXineSpuDecoder(cXinelibDevice *dev) { + scaleMode = eSpuNormal; + m_Device = dev; + } + virtual ~cXineSpuDecoder() {}; + + virtual int setTime(uint32_t pts) { return 1; } + + cSpuDecoder::eScaleMode getScaleMode(void) { return scaleMode; } + virtual void setScaleMode(cSpuDecoder::eScaleMode ScaleMode) + { scaleMode = ScaleMode; } + virtual void setPalette(uint32_t * pal) {}; + virtual void setHighlight(uint16_t sx, uint16_t sy, + uint16_t ex, uint16_t ey, + uint32_t palette) {}; + virtual void clearHighlight(void) {}; + virtual void Empty(void) {}; + virtual void Hide(void) {}; + virtual void Draw(void) {}; + virtual bool IsVisible(void) { return true; } + virtual void processSPU(uint32_t pts, uint8_t * buf, + bool AllowedShow = true); +}; + +#define CMD_SPU_MENU 0x00 +#define CMD_SPU_SHOW 0x01 +#define CMD_SPU_HIDE 0x02 +#define CMD_SPU_SET_PALETTE 0x03 +#define CMD_SPU_SET_ALPHA 0x04 +#define CMD_SPU_SET_SIZE 0x05 +#define CMD_SPU_SET_PXD_OFFSET 0x06 +#define CMD_SPU_CHG_COLCON 0x07 +#define CMD_SPU_EOF 0xff + +#define spuU32(i) ((spu[i] << 8) + spu[i+1]) + +void cXineSpuDecoder::processSPU(uint32_t pts, uint8_t * buf, bool AllowedShow) +{ + uchar buf2[65536+8] = {0, 0, 1, PRIVATE_STREAM1, 0, 0, 0x80, 0x80, 5}; + int len = ((buf[0] << 8) | buf[1]); + + if(len+8 < 0xffff) { + buf2[4] = ((len+8)<<8) & 0xFF; + buf2[5] = ((len+8)) & 0xFF; + } else { + // should be able to handle this (but only internally ...) + LOGMSG("cXineSpuDecoder: SPU bigger than PES packet !"); + buf2[4] = 0xff; + buf2[5] = 0xff; + } + buf2[9] = ((pts>>29) & 0x0E) | 0x21; + buf2[10] = (pts>>22) & 0xFF; + buf2[11] = (pts>>14) & 0xFE; + buf2[12] = (pts>>7) & 0xFF; + buf2[13] = (pts<<1) & 0xFE; + + memcpy(buf2+14, buf, len); + + m_Device->PlaySpu(buf, len+14, 0); +} +#endif + +cSpuDecoder *cXinelibDevice::GetSpuDecoder(void) +{ + TRACEF("cXinelibDevice::GetSpuDecoder"); + if (!m_spuDecoder && IsPrimaryDevice()) + // + // TODO + // + // - use own derived SpuDecoder with special cXinelibOsd + // -> always visible + // + +#if 1 + m_spuDecoder = new cDvbSpuDecoder(); +#else +#warning NON-FUNCTIONAL SPU DECODER SELECTED !!! + m_spuDecoder = new cXineSpuDecoder(this); +#endif + return m_spuDecoder; +} + +int64_t cXinelibDevice::GetSTC(void) +{ + TRACEF("cXinelibDevice::GetSTC"); + + if(m_local) + return m_local->GetSTC(); + if(m_server) + return m_server->GetSTC(); + return cDevice::GetSTC(); +} + + +#if VDRVERSNUM < 10338 + +bool cXinelibDevice::GrabImage(const char *FileName, bool Jpeg, + int Quality, int SizeX, int SizeY) +{ + uchar *Data = NULL; + int Size = 0; + TRACEF("cXinelibDevice::GrabImage"); + + ACTIVITY + if(m_local) + Data = m_local->GrabImage(Size, Jpeg, Quality, SizeX, SizeY); + if(!Data && m_server) + Data = m_local->GrabImage(Size, Jpeg, Quality, SizeX, SizeY); + + if(Data) { + FILE *fp = fopen(FileName, "wb"); + if(fp) { + fwrite(Data, Size, 1, fp); + fclose(fp); + free(Data); + return true; + } + LOGERR("Grab: Can't open %s", FileName); + free(Data); + } else { + LOGMSG("Grab to %s failed", FileName); + } + return false; +} + +#else + +uchar *cXinelibDevice::GrabImage(int &Size, bool Jpeg, + int Quality, int SizeX, int SizeY) +{ + TRACEF("cXinelibDevice::GrabImage"); + + ACTIVITY + if(m_local) + return m_local->GrabImage(Size, Jpeg, Quality, SizeX, SizeY); + if(m_server) + return m_local->GrabImage(Size, Jpeg, Quality, SizeX, SizeY); + + return NULL; +} + +#endif + + +#if 0 +// override cDevice to get DVD SPUs +int cXinelibDevice::PlayPesPacket(const uchar *Data, int Length, + bool VideoOnly) +{ +#ifndef SKIP_DVDSPU + switch (Data[3]) { + case 0xBD: { // private stream 1 + int PayloadOffset = Data[8] + 9; + uchar SubStreamId = Data[PayloadOffset]; + uchar SubStreamType = SubStreamId & 0xF0; + uchar SubStreamIndex = SubStreamId & 0x1F; + switch (SubStreamType) { + case 0x20: // SPU + case 0x30: // SPU + return PlaySpu(Data, Length, SubStreamIndex); + break; + default: + ; + } + } + default: + ; + } +#endif + return cDevice::PlayPesPacket(Data, Length, VideoOnly); +} +#endif |