From 6e258aa540d276ebb63acc677bc9eedb529b5a28 Mon Sep 17 00:00:00 2001 From: Marten Richter Date: Thu, 17 May 2012 09:08:41 +0200 Subject: [PATCH] Initial import of patches for ARM/Android/Raspeberry pi --- GNUmakefile | 184 +- Makefile.nmake | 2 +- abstractoption.cc | 3 +- abstractoption.h | 11 +- afeed.cc | 195 +- afeed.h | 8 +- audio.cc | 5 + audio.h | 183 +- audiowin.cc | 1454 +++++++------- bitmap.h | 2 + boxstack.cc | 1233 ++++++------ boxx.cc | 851 ++++---- boxx.h | 375 ++-- command.cc | 1922 +++++++++--------- defines.h | 224 ++- demuxer.cc | 1940 +++++++++--------- demuxer.h | 470 ++--- demuxeraudio.cc | 2496 +++++++++++------------ demuxerts.cc | 1754 ++++++++-------- demuxerts.h | 192 +- demuxervdr.cc | 801 ++++---- directory.cc | 2 +- directory.h | 2 +- draintarget.cc | 28 - draintarget.h | 219 +- dvbsubtitles.h | 7 +- i18n.cc | 6 +- imagereader.h | 236 ++- ledmvp.h | 2 +- log.cc | 404 ++-- log.h | 218 +- main.cc | 1152 +++++------ message.h | 161 +- objects.mk | 56 +- osdmvp.cc | 7 +- osdmvp.h | 4 +- osdwin.cc | 1202 +++++------ osdwin.h | 246 +-- player.cc | 2701 +++++++++++++------------ player.h | 476 +++-- playerliveradio.cc | 950 ++++----- playerlivetv.cc | 1551 +++++++------- playerradio.cc | 1175 +++++------ playerradio.h | 316 ++- recman.h | 4 + remote.cc | 80 +- remote.h | 4 + stream.cc | 408 ++-- surface.cc | 421 ++-- surface.h | 188 +- surfacedirectfb.cc | 608 +++--- surfacedirectfb.h | 146 +- surfacewin.cc | 805 ++++---- surfacewin.h | 130 +- tbboxx.cc | 2 +- teletextdecodervbiebu.cc | 1671 +++++++-------- tfeed.cc | 89 +- tfeed.h | 58 +- threadp.cc | 3 + threadp.h | 1 + timerreceiver.h | 0 timers.cc | 0 timers.h | 6 +- udp.cc | 4 +- udp.h | 6 +- vchannellist.cc | 11 +- vcolourtuner.cc | 572 +++--- vconnect.cc | 5 +- vdr.cc | 2939 ++++++++++++++------------- vdr.h | 584 +++--- vdrcommand.h | 327 +-- vdrrequestpacket.cc | 81 +- vdrresponsepacket.cc | 290 +-- vepg.cc | 1649 +++++++-------- vepgsettimer.cc | 10 +- vfeed.cc | 166 +- vfeed.h | 8 +- video.cc | 191 +- video.h | 317 +-- videomvp.cc | 811 ++++---- videomvp.h | 310 +-- videowin.cc | 4129 +++++++++++++++++++------------------- videowin.h | 446 ++-- vmediaview.cc | 2914 +++++++++++++-------------- vopts.cc | 745 +++---- vradiorec.cc | 1088 +++++----- vrecmove.cc | 2 +- vrecmove.h | 2 +- vrecording.cc | 776 +++---- vrecordinglist.cc | 1106 +++++----- vscreensaver.h | 8 +- vsleeptimer.h | 6 +- vteletextview.cc | 556 ++--- vteletextview.h | 128 +- vtimeredit.cc | 49 +- vtimerlist.cc | 781 +++---- vvideolivetv.cc | 2389 +++++++++++----------- vvideomedia.cc | 1568 +++++++-------- vvideorec.cc | 2179 ++++++++++---------- vwelcome.cc | 708 +++---- vwelcome.h | 3 +- wbutton.cc | 222 +- wbutton.h | 2 +- wjpeg.cc | 937 +-------- wjpeg.h | 290 +-- wol.cc | 12 +- woptionbox.cc | 6 +- woptionpane.cc | 11 +- wselectlist.cc | 590 +++--- wselectlist.h | 179 +- wsymbol.cc | 4 +- wtabbar.cc | 2 +- 112 files changed, 29936 insertions(+), 30233 deletions(-) delete mode 100644 draintarget.cc mode change 100755 => 100644 timerreceiver.h mode change 100755 => 100644 timers.cc mode change 100755 => 100644 timers.h mode change 100755 => 100644 udp.cc mode change 100755 => 100644 udp.h diff --git a/GNUmakefile b/GNUmakefile index 88ad464..112207a 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -1,71 +1,113 @@ -include ../crosstool/cross-var - -CC=$(CROSS)gcc -STRIP=$(CROSS)strip -CXX=$(CROSS)g++ -LD=$(CROSS)g++ - -INCLUDES = -I../jpeg/jpeg-6b -CXXFLAGS_DEV = -g -O0 -Wall -Wshadow -DDEV -D_GNU_SOURCE $(INCLUDES) -CXXFLAGS_REL = -O3 -Wall -Wshadow -D_GNU_SOURCE $(INCLUDES) -LDFLAGS = -Wall -static - -LIBPATHS = -LIBS = -lpthread -lrt -CROSSLIBS = ../jpeg/jpeg-6b/libjpeg.a - -# This is the only thing windows and linux share -include objects.mk - -OBJECTSMVP = main.o threadp.o remotemvp.o ledmvp.o mtdmvp.o videomvp.o audiomvp.o osdmvp.o surfacemvp.o -OBJECTSWIN = threadwin.o remotewin.o ledwin.o mtdwin.o videowin.o audiowin.o osdwin.o surfacewin.o - -OBJECTS = $(OBJECTS1) $(OBJECTSMVP) - -.PHONY: clean fresh all install strip - -default: dev -fresh: clean default - -vompclient: $(OBJECTS) ticonfig.o $(CROSSLIBS) - $(LD) $(LDFLAGS) $(LIBPATHS) $(RELEASE) -o vompclient ticonfig.o $(OBJECTS) $(CROSSLIBS) $(LIBS) - -# A slight hash up -ticonfig.o: - $(CC) $(CXXFLAGS_REL) -c -o ticonfig.o ticonfig.c - -strip: - $(STRIP) vompclient - -install: - 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 - cp vompclient /diskless/nfs/wmvp - -install-dev: - rm -f /mnt/auto/defiant/diskless/nfs/mvp-dev/vompclient - cp vompclient /mnt/auto/defiant/diskless/nfs/mvp-dev - -debug: - ../../gdb/gdb-6.7/gdb/gdb /mnt/auto/defiant/diskless/nfs/mvp/vompclient /mnt/auto/defiant/diskless/nfs/mvp/core.* - -debug2: - ../../gdb/gdb-6.7/gdb/gdb /mnt/auto/defiant/diskless/nfs/mvp-dev/vompclient /mnt/auto/defiant/diskless/nfs/mvp-dev/core.* - -dev: CXXFLAGS := $(CXXFLAGS_DEV) -dev: vompclient - -release: CXXFLAGS := $(CXXFLAGS_REL) -release: clean vompclient strip - -deps: GNUmakefile - $(CC) -MM $(INCLUDES) $(OBJECTS:%.o=%.cc) > deps - --include deps - -clean: - rm -f *.o deps vompclient *~ fonts/*.o fonts/*~ - +vomp_platform =raspberry +# valid platforms are raspberry and mvp + +ifeq ($(vomp_platform),mvp) + +$(info MVP crosscompiler) +include ../crosstool/cross-var +CC=$(CROSS)gcc +STRIP=$(CROSS)strip +CXX=$(CROSS)g++ +LD=$(CROSS)g++ + +endif + +ifeq ($(vomp_platform),raspberry) + +$(info raspberry normal compiler) +CC=gcc +STRIP=strip +CXX=g++ +LD=g++ + +endif + + + +CXXFLAGS_DEV = -g -O0 -Wall -Wshadow -DDEV -D_GNU_SOURCE $(INCLUDES) +CXXFLAGS_REL = -O3 -Wall -Wshadow -D_GNU_SOURCE $(INCLUDES) + + +LIBPATHS = + + +$(info Setting up objects) +# This is the only thing windows and linux share +include objects.mk + +OBJECTSWIN = threadwin.o remotewin.o ledwin.o mtdwin.o videowin.o audiowin.o osdwin.o surfacewin.o + +OBJECTS = $(OBJECTS1) + + +ifeq ($(vomp_platform),mvp) +$(info MVP flags) +LDFLAGS = -Wall -static +LIBS = -lpthread -lrt + +OBJECTS += main.o threadp.o remotemvp.o ledmvp.o mtdmvp.o videomvp.o audiomvp.o osdmvp.o surfacemvp.o vmedialist.o vcolourtuner.o vmediaview.o vvideomedia.o +TIOBJECT = ticonfig.o +CROSSLIBS = ../jpeg/jpeg-6b/libjpeg.a +INCLUDES = -I../jpeg/jpeg-6b -DVOMP_PLATTFORM_MVP + +endif + +ifeq ($(vomp_platform),raspberry) +$(info Raspberry pi flags) +LDFLAGS = -Wall +LIBS = -L/opt/vc/lib -lpthread -lrt -lEGL -lGLESv2 -lbcm_host + +OBJECTS += main.o threadp.o osdopengl.o surfaceopengl.o ledraspberry.o mtdraspberry.o videovpeogl.o audiovpe.o wjpegsimple.o remotelinux.o +LIBS+= -ljpeg +CROSSLIBS = +INCLUDES = -DVOMP_PLATTFORM_RASPBERRY -I/opt/vc/include + +endif + +.PHONY: clean fresh all install strip + +default: dev +fresh: clean default + +vompclient: $(OBJECTS) $(TIOBJECT) $(CROSSLIBS) + $(LD) $(LDFLAGS) $(LIBPATHS) $(RELEASE) -o vompclient $(TIOBJECT) $(OBJECTS) $(CROSSLIBS) $(LIBS) + +# A slight hash up +ticonfig.o: + $(CC) $(CXXFLAGS_REL) -c -o ticonfig.o ticonfig.c + +strip: + $(STRIP) vompclient + +install: + 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 + cp vompclient /diskless/nfs/wmvp + +install-dev: + rm -f /mnt/auto/defiant/diskless/nfs/mvp-dev/vompclient + cp vompclient /mnt/auto/defiant/diskless/nfs/mvp-dev + +debug: + ../../gdb/gdb-6.7/gdb/gdb /mnt/auto/defiant/diskless/nfs/mvp/vompclient /mnt/auto/defiant/diskless/nfs/mvp/core.* + +debug2: + ../../gdb/gdb-6.7/gdb/gdb /mnt/auto/defiant/diskless/nfs/mvp-dev/vompclient /mnt/auto/defiant/diskless/nfs/mvp-dev/core.* + +dev: CXXFLAGS := $(CXXFLAGS_DEV) +dev: vompclient + +release: CXXFLAGS := $(CXXFLAGS_REL) +release: clean vompclient strip + +deps: GNUmakefile + $(CC) -MM $(INCLUDES) $(OBJECTS:%.o=%.cc) > deps + +-include deps + +clean: + rm -f *.o deps vompclient *~ fonts/*.o fonts/*~ + diff --git a/Makefile.nmake b/Makefile.nmake index cf2d5ea..dbb2fb8 100644 --- a/Makefile.nmake +++ b/Makefile.nmake @@ -17,7 +17,7 @@ CROSSLIBS = !include "objects.mk" -OBJECTSWIN = winmain.o threadwin.o remotewin.o ledwin.o mtdwin.o videowin.o audiowin.o osdwin.o surfacewin.o dsallocator.o dssourcefilter.o dssourcepin.o wwinvideofilter.o wwinaudiofilter.o wwinmp3audiofilter.o +OBJECTSWIN = winmain.o threadwin.o remotewin.o ledwin.o mtdwin.o videowin.o audiowin.o osdwin.o surfacewin.o dsallocator.o dssourcefilter.o dssourcepin.o wwinvideofilter.o wwinaudiofilter.o wwinmp3audiofilter.o wjpegsimple.o OBJECTS = $(OBJECTS1) $(OBJECTSWIN) diff --git a/abstractoption.cc b/abstractoption.cc index 6c7408c..586a8f7 100644 --- a/abstractoption.cc +++ b/abstractoption.cc @@ -21,6 +21,7 @@ #include "vdr.h" #include "option.h" +/* bool AbstractOption::loadOptionsfromServer(VDR* vdr) { return true; @@ -44,5 +45,5 @@ bool AbstractOption::addOptionsToPanes(int panenumber,Options *options,WOptionPa bool AbstractOption::handleOptionChanges(Option* option) { return false; -} +}*/ diff --git a/abstractoption.h b/abstractoption.h index ac6c5ba..a08497c 100644 --- a/abstractoption.h +++ b/abstractoption.h @@ -31,11 +31,12 @@ class WOptionPane; class AbstractOption { public: - virtual bool loadOptionsfromServer(VDR* vdr); - virtual bool saveOptionstoServer(); - virtual bool addOptionPagesToWTB(WTabBar *wtb); - virtual bool addOptionsToPanes(int panenumber,Options *options,WOptionPane* pane); - virtual bool handleOptionChanges(Option* option); + // inline definition allows plugins architecture for audio video + virtual bool loadOptionsfromServer(VDR* vdr) {return true;}; + virtual bool saveOptionstoServer() {return true;}; + virtual bool addOptionPagesToWTB(WTabBar *wtb) {return true;}; + virtual bool addOptionsToPanes(int panenumber,Options *options,WOptionPane* pane) {return true;}; + virtual bool handleOptionChanges(Option* option){return false;}; }; #endif diff --git a/afeed.cc b/afeed.cc index 43e8c86..4da46c7 100644 --- a/afeed.cc +++ b/afeed.cc @@ -1,97 +1,98 @@ -/* - 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. -*/ - -#include "afeed.h" - -#include "log.h" -#include "demuxer.h" -#include "callback.h" - -AFeed::AFeed(Callback* tcb) -: cb(*tcb) -{ - audioEnabled = 1; -} - -int AFeed::init() -{ - return 1; -} - -int AFeed::shutdown() -{ - // FIXME - return 1; -} - -void AFeed::disable() -{ - audioEnabled = 0; -} - -void AFeed::enable() -{ - audioEnabled = 1; -} - -int AFeed::start() -{ - audioEnabled = 1; - return threadStart(); -} - -void AFeed::stop() -{ - threadCancel(); -} - -void AFeed::threadMethod() -{ - bool alen; - - while(1) - { - threadCheckExit(); - - if (audioEnabled) - { - alen = Demuxer::getInstance()->writeAudio(); - - if (alen) - { - cb.call(this); -// Log::getInstance()->log("Afeed", Log::DEBUG, "written"); - } - else - { - //MILLISLEEP(100); - MILLISLEEP(5); //Performance Issue Marten - } - } - else - { - Demuxer::getInstance()->flushAudio(); - //Log::getInstance()->log("AFeed", Log::DEBUG, "No data delay"); - //MILLISLEEP(100); - MILLISLEEP(5); //Performance Issue - } - } -} - +/* + 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. +*/ + +#include "afeed.h" + +#include "log.h" +#include "demuxer.h" +#include "callback.h" + + +AFeed::AFeed(Callback* tcb) +: cb(*tcb) +{ + audioEnabled = 1; +} + +int AFeed::init() +{ + return 1; +} + +int AFeed::shutdown() +{ + // FIXME + return 1; +} + +void AFeed::disable() +{ + audioEnabled = 0; +} + +void AFeed::enable() +{ + audioEnabled = 1; +} + +int AFeed::start() +{ + audioEnabled = 1; + return threadStart(); +} + +void AFeed::stop() +{ + threadCancel(); +} + +void AFeed::threadMethod() +{ + bool alen; + + while(1) + { + threadCheckExit(); + + if (audioEnabled) + { + alen = Demuxer::getInstance()->writeAudio(); + + if (alen) + { + cb.call(this); +// Log::getInstance()->log("Afeed", Log::DEBUG, "written"); + } + else + { + //MILLISLEEP(100); + MILLISLEEP(5); //Performance Issue Marten + } + } + else + { + Demuxer::getInstance()->flushAudio(); + //Log::getInstance()->log("AFeed", Log::DEBUG, "No data delay"); + //MILLISLEEP(100); + MILLISLEEP(5); //Performance Issue + } + } +} + diff --git a/afeed.h b/afeed.h index 599d9cb..69ca133 100644 --- a/afeed.h +++ b/afeed.h @@ -24,11 +24,8 @@ #include #include -#ifdef WIN32 -#include "threadwin.h" -#else -#include "threadp.h" -#endif + +#include "threadsystem.h" class Callback; @@ -49,6 +46,7 @@ class AFeed : public Thread_TYPE void threadMethod(); void threadPostStopCleanup() {}; int audioEnabled; + bool callbacksend; Callback& cb; }; diff --git a/audio.cc b/audio.cc index ab2d4df..aa02ec4 100644 --- a/audio.cc +++ b/audio.cc @@ -42,6 +42,11 @@ Audio* Audio::getInstance() { return instance; } +/* +void Audio::setInstance(Audio* inst) +{ + instance=inst; +}*/ int Audio::volumeUp() { diff --git a/audio.h b/audio.h index 082bc27..ab0a213 100644 --- a/audio.h +++ b/audio.h @@ -1,91 +1,92 @@ -/* - 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 AUDIO_H -#define AUDIO_H - -#include -#include "defines.h" -#include "draintarget.h" -#include "abstractoption.h" - -typedef struct -{ - unsigned char frontleft; - unsigned char frontright; - unsigned char rearleft; - unsigned char rearright; - unsigned char center; - unsigned char lfe; -} audio_volume; - -class Audio : public DrainTarget, public AbstractOption -{ - public: - Audio(); - virtual ~Audio(); - static Audio* getInstance(); - - virtual int init(UCHAR streamType)=0; - virtual int shutdown()=0; - virtual int setStreamType(UCHAR streamType)=0; - virtual int setChannel()=0; - virtual int setSource()=0; - virtual int sync()=0; - virtual int play()=0; - virtual int stop()=0; - virtual int pause()=0; - virtual int unPause()=0; - virtual int reset()=0; - virtual int setVolume(int volume)=0; - virtual int mute()=0; - virtual int unMute()=0; - virtual bool supportsAc3()=0; - virtual bool maysupportAc3(){return false;} - - int volumeUp(); - int volumeDown(); - int getVolume(); - int toggleUserMute(); - int systemMuteOn(); - int systemMuteOff(); - int doMuting(); - - // Audio stream type // FIXME these are MVP specific (probably!) - const static UCHAR MPEG2_PES = 2; - const static UCHAR MPEG1_PES = 3; // unused - const static UCHAR MP3 = 0; //media player - -#ifdef DEV - virtual int test()=0; -#endif - - protected: - static Audio* instance; - int initted; - UCHAR userMute; - UCHAR systemMute; - UCHAR muted; - int volume; - - audio_volume Aoffset; -}; - -#endif +/* + 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 AUDIO_H +#define AUDIO_H + +#include +#include "defines.h" +#include "draintarget.h" +#include "abstractoption.h" + +typedef struct +{ + unsigned char frontleft; + unsigned char frontright; + unsigned char rearleft; + unsigned char rearright; + unsigned char center; + unsigned char lfe; +} audio_volume; + +class Audio : public DrainTarget, public AbstractOption +{ + public: + Audio(); + virtual ~Audio(); + static Audio* getInstance(); + // static void setInstance(Audio* ); + + virtual int init(UCHAR streamType)=0; + virtual int shutdown()=0; + virtual int setStreamType(UCHAR streamType)=0; + virtual int setChannel()=0; + virtual int setSource()=0; + virtual int sync()=0; + virtual int play()=0; + virtual int stop()=0; + virtual int pause()=0; + virtual int unPause()=0; + virtual int reset()=0; + virtual int setVolume(int volume)=0; + virtual int mute()=0; + virtual int unMute()=0; + virtual bool supportsAc3()=0; + virtual bool maysupportAc3(){return false;} + + int volumeUp(); + int volumeDown(); + int getVolume(); + int toggleUserMute(); + int systemMuteOn(); + int systemMuteOff(); + int doMuting(); + + // Audio stream type // FIXME these are MVP specific (probably!) + const static UCHAR MPEG2_PES = 2; + const static UCHAR MPEG1_PES = 3; // unused + const static UCHAR MP3 = 0; //media player + +#ifdef DEV + virtual int test()=0; +#endif + + protected: + static Audio* instance; + int initted; + UCHAR userMute; + UCHAR systemMute; + UCHAR muted; + int volume; + + audio_volume Aoffset; +}; + +#endif diff --git a/audiowin.cc b/audiowin.cc index aeb8347..2079b1d 100644 --- a/audiowin.cc +++ b/audiowin.cc @@ -1,727 +1,727 @@ -/* - 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. -*/ - -#include "audiowin.h" -#include "videowin.h" -#include "vdr.h" -#include "wtabbar.h" -#include "wwinaudiofilter.h" -#include "wwinmp3audiofilter.h" -#include "i18n.h" - - - - -AudioWin::AudioWin() -{ - initted = 0; - firstsynched=false; - winvolume=0; - volume=20; -audiofilterselected=-1; - mp3audiofilterselected=-1; - aud_type=Audio::MPEG2_PES; - -} - -AudioWin::~AudioWin() -{ - - int i; - for (i=0;isetAudioStreamType(type); - aud_type=type; - if (!initted) return 0; - return 1; -} - -int AudioWin::setChannel() -{ - if (!initted) return 0; - return 1; -} - -int AudioWin::setSource() -{ - if (!initted) return 0; - return 1; -} - -int AudioWin::sync() -{ - if (!initted) return 0; - return 1; -} - -int AudioWin::play() -{ - if (!initted) return 0; - firstsynched=false; - return ((VideoWin*)Video::getInstance())->dsplay(); - -} - -int AudioWin::stop() -{ - if (!initted) return 0; - return ((VideoWin*)Video::getInstance())->dsstop(); -} - -int AudioWin::pause() -{ - if (!initted) return 0; - return ((VideoWin*)Video::getInstance())->dspause(); -} - -int AudioWin::unPause() -{ - if (!initted) return 0; - return ((VideoWin*)Video::getInstance())->dsunPause(); -} - -int AudioWin::reset() -{ - - if (!initted){return 0;} - return ((VideoWin*)Video::getInstance())->dsreset(); -} - -int AudioWin::setVolume(int tvolume) -{ - // parameter: 0 for silence, 20 for full - if ((tvolume < 0) || (tvolume > 20)) return 0; - winvolume=((tvolume-20)*100*30)/20; - if (tvolume==0) winvolume=-10000; - ((VideoWin*)Video::getInstance())->SetAudioVolume(winvolume); - - - return 1; -} - -int AudioWin::mute() -{ - if (!initted) return 0; - ((VideoWin*)Video::getInstance())->SetAudioState(false); - ((VideoWin*)Video::getInstance())->SetAudioVolume(-10000); - return 1; -} - -int AudioWin::unMute() -{ - if (!initted) return 0; - ((VideoWin*)Video::getInstance())->SetAudioState(true); - ((VideoWin*)Video::getInstance())->SetAudioVolume(winvolume); - return 1; -} - -void AudioWin::PrepareMediaSample(const MediaPacketList& mplist,UINT samplepos) -{ - mediapacket = mplist.front(); -} - -void AudioWin::initFilterDatabase() -{ - /* This method should determine all availiable DirectShow Filters */ - IFilterMapper2* filtmap=NULL; - HRESULT result; - result = CoCreateInstance(CLSID_FilterMapper2,NULL,CLSCTX_INPROC, - IID_IFilterMapper2,(void**)&filtmap); - if (result != S_OK) - { - Log::getInstance()->log("AudioWin", Log::ERR , "Unable to create FilterMapper!"); - return; - } - /* Wishlist, what Mediatypes do we want */ - GUID mtypesin[]={MEDIATYPE_Audio,MEDIASUBTYPE_MPEG2_AUDIO, - /*MEDIATYPE_Audio,MEDIASUBTYPE_MPEG1Payload,*/ - MEDIATYPE_Audio,MEDIASUBTYPE_DOLBY_AC3, - MEDIATYPE_Audio, MEDIASUBTYPE_DOLBY_AC3_SPDIF}; - IEnumMoniker *myenum; - result = filtmap->EnumMatchingFilters(&myenum,0,TRUE,MERIT_DO_NOT_USE+1, - TRUE,3,mtypesin,NULL,NULL,FALSE,TRUE,0,NULL,NULL,NULL); - if (result != S_OK) - { - filtmap->Release(); - Log::getInstance()->log("AudioWin", Log::ERR , "Unable to enum Filters!"); - return; - } - ULONG gethowmany; - IMoniker * moni; - while(myenum->Next(1,&moni,&gethowmany)==S_OK) - { - AudioFilterDesc desc; - ZeroMemory(&desc,sizeof(desc)); - - LPOLESTR string; - moni->GetDisplayName(0,0,&string); - desc.displayname=new char[wcslen(string)+1]; - wcstombs(desc.displayname,string,wcslen(string)+1); - CoTaskMemFree(string); - IPropertyBag *bag; - if (moni->BindToStorage(0,0,IID_IPropertyBag,(void**)&bag) == S_OK) - { - VARIANT vari; - VariantInit(&vari); - result = bag->Read(L"FriendlyName",&vari,NULL); - if (result == S_OK) - { - desc.friendlyname=new char[wcslen(vari.bstrVal)+1]; - wcstombs(desc.friendlyname,vari.bstrVal,wcslen(vari.bstrVal)+1); - } - VariantClear(&vari); - bag->Release(); - - } - - - audiofilterlist.push_back(desc); - - - - moni->Release(); - // bctx->Release(); - } - int i; - audiofilterselected=-1; - myenum->Release(); - filtmap->Release(); -} - -void AudioWin::initMp3FilterDatabase() -{ - /* This method should determine all availiable DirectShow Filters */ - IFilterMapper2* filtmap=NULL; - HRESULT result; - result = CoCreateInstance(CLSID_FilterMapper2,NULL,CLSCTX_INPROC, - IID_IFilterMapper2,(void**)&filtmap); - if (result != S_OK) - { - Log::getInstance()->log("AudioWin", Log::ERR , "Unable to create FilterMapper!"); - return; - } - /* Wishlist, what Mediatypes do we want */ - GUID mtypesin[]={MEDIATYPE_Audio,MEDIATYPE_WaveFmt_Mpeg1Layer3, - MEDIATYPE_Audio,MEDIASUBTYPE_MPEG2_AUDIO}; - IEnumMoniker *myenum; - result = filtmap->EnumMatchingFilters(&myenum,0,TRUE,MERIT_DO_NOT_USE+1, - TRUE,3,mtypesin,NULL,NULL,FALSE,TRUE,0,NULL,NULL,NULL); - if (result != S_OK) - { - filtmap->Release(); - Log::getInstance()->log("AudioWin", Log::ERR , "Unable to enum Filters!"); - return; - } - ULONG gethowmany; - IMoniker * moni; - while(myenum->Next(1,&moni,&gethowmany)==S_OK) - { - AudioFilterDesc desc; - ZeroMemory(&desc,sizeof(desc)); - - LPOLESTR string; - moni->GetDisplayName(0,0,&string); - desc.displayname=new char[wcslen(string)+1]; - wcstombs(desc.displayname,string,wcslen(string)+1); - CoTaskMemFree(string); - IPropertyBag *bag; - if (moni->BindToStorage(0,0,IID_IPropertyBag,(void**)&bag) == S_OK) - { - VARIANT vari; - VariantInit(&vari); - result = bag->Read(L"FriendlyName",&vari,NULL); - if (result == S_OK) - { - desc.friendlyname=new char[wcslen(vari.bstrVal)+1]; - wcstombs(desc.friendlyname,vari.bstrVal,wcslen(vari.bstrVal)+1); - } - VariantClear(&vari); - bag->Release(); - - } - - - mp3audiofilterlist.push_back(desc); - - - - moni->Release(); - // bctx->Release(); - } - int i; - mp3audiofilterselected=-1; - myenum->Release(); - filtmap->Release(); -} - -bool AudioWin::loadOptionsfromServer(VDR* vdr) -{ - char *name=vdr->configLoad("DirectShow","AudioFilter"); - - if (name != NULL) - { - for (int i = 0;i configLoad("DirectShow","Mp3AudioFilter"); - - if (name != NULL) - { - for (int i = 0;i configSave("DirectShow", - "AudioFilter",audiofilterlist[audiofilterselected].displayname); - } - if (mp3audiofilterselected!=-1) { - VDR::getInstance()->configSave("DirectShow", - "Mp3AudioFilter",mp3audiofilterlist[mp3audiofilterselected].displayname); - } - return true; -} - -UINT AudioWin::DeliverMediaSample(UCHAR* buffer, UINT *samplepos) -{ - DeliverMediaPacket(mediapacket, buffer, samplepos); - if (*samplepos == mediapacket.length) { - *samplepos = 0; - return 1; - } - else return 0; -} - -UINT AudioWin::DeliverMediaPacket(const MediaPacket packet, - UCHAR* buffer, - UINT *samplepos) -{ - - /*First Check, if we have an audio sample*/ - VideoWin *vw=(VideoWin*)Video::getInstance(); - if (!vw->isdsinited()) return 0; - if (vw->InIframemode()) { - samplepos=0; - MILLISLEEP(10); - return 0; //Not in iframe mode! - } - IMediaSample* ms=NULL; - REFERENCE_TIME reftime1=0; - REFERENCE_TIME reftime2=0; - - UINT headerstrip=0; - if (packet.disconti) { - firstsynched=false; - vw->DeliverAudioMediaSample(); - } - - if (packet.type!=vw->lastAType()){//Format Change //Push data out ! - firstsynched=false; - vw->DeliverAudioMediaSample(); - } - - - - /*Inspect PES-Header */ -/* UINT header_length=buffer[(packet.pos_buffer+8)%bufferlength]+8/*is this right*; -*/ - if (*samplepos==0 && packet.type!=MPTYPE_MPEG_AUDIO_LAYER3) {//stripheader - headerstrip=buffer[packet.pos_buffer+8]+9; - if (packet.type == MPTYPE_AC3) headerstrip+=4; //skip ac3 bytes - *samplepos+=headerstrip; - if ( packet.synched ) { - vw->DeliverAudioMediaSample();//write out old data - reftime1=packet.presentation_time; - reftime2=reftime1+1; - firstsynched=true; - } else { - if (!firstsynched) {// - *samplepos=packet.length;//if we have not processed at least one - return packet.length;//synched packet ignore it! - } - } - } - BYTE *ms_buf; - UINT ms_length; - UINT ms_pos; - UINT haveToCopy; - if (!vw->getCurrentAudioMediaSample(&ms) || ms==NULL) {// get the current sample - //samplepos=0; - //MILLISLEEP(10); - return *samplepos; - } - ms_pos=ms->GetActualDataLength(); - ms_length=ms->GetSize(); - haveToCopy=min(ms_length-ms_pos,packet.length-*samplepos); - if ((ms_length-ms_pos)<1) { - vw->DeliverAudioMediaSample(); //we are full! - if (!vw->getCurrentAudioMediaSample(&ms) || ms==NULL) {// get the current sample - //samplepos=0; - //MILLISLEEP(10); - return *samplepos; - } - ms_pos=ms->GetActualDataLength(); - ms_length=ms->GetSize(); - haveToCopy=min(ms_length-ms_pos,packet.length-*samplepos); - } - ms->GetPointer(&ms_buf); - - - if (ms_pos==0) {//will only be changed on first packet - if (packet.disconti) { - ms->SetDiscontinuity(TRUE); - } else { - ms->SetDiscontinuity(FALSE); - } - if (packet.synched) { - ms->SetSyncPoint(TRUE); - ms->SetTime(&reftime1,&reftime2); - - //ms->SetTime(NULL,NULL); - ms->SetMediaTime(NULL, NULL); - if (reftime1<0) ms->SetPreroll(TRUE); - else ms->SetPreroll(FALSE); - }else { - ms->SetSyncPoint(FALSE); - ms->SetTime(NULL,NULL); - ms->SetMediaTime(NULL, NULL); - ms->SetPreroll(FALSE); - // ms->SetSyncPoint(TRUE); - } - } - if (packet.type!=vw->lastAType()) { - vw->changeAType(packet.type,ms); - ms->SetDiscontinuity(TRUE); - } - - - memcpy(ms_buf+ms_pos,buffer+packet.pos_buffer+*samplepos,haveToCopy); - - ms->SetActualDataLength(haveToCopy+ms_pos); - - *samplepos+=haveToCopy; - - return haveToCopy+headerstrip; - -} - -int AudioWin::dsInitAudioFilter(IGraphBuilder* dsgraphbuilder) -{ - HRESULT hres; - IFilterGraph2*fg2=NULL; - VideoWin *vw=(VideoWin*)Video::getInstance(); - if (dsgraphbuilder->QueryInterface(IID_IFilterGraph2,(void**)&fg2)!= S_OK) - { - Log::getInstance()->log("AudiooWin", Log::WARN , "Failed querying for FilterGraph2 Interface!"); - return 0; - } - IBaseFilter*audiofilter; - if (aud_type!=Audio::MP3) { - audiofilter = getAudioFilter(); - } else { - audiofilter = getMp3AudioFilter(); - } - if (hres=dsgraphbuilder->AddFilter(audiofilter,NULL) != S_OK) - { - Log::getInstance()->log("AudioWin", Log::WARN , "Failed adding Video Filter!"); - return 0; - } - IEnumPins *pinenum=NULL; - bool error=false; - if (audiofilter->EnumPins(&pinenum) == S_OK) - { - IPin *current=NULL; - ULONG fetch=0; - bool firststep=false; - while (pinenum->Next(1,¤t,&fetch)==S_OK) - { - PIN_DIRECTION dir; - if (current->QueryDirection(&dir)==S_OK) - { - if (dir == PINDIR_INPUT) - { - if (vw->getSourceFilter()->GetAudioPin()->Connect(current,NULL)==S_OK) - { - current->Release(); - firststep=true; - break; - } - } - } - current->Release(); - } - if (firststep==false) - { - Log::getInstance()->log("AudioWin", Log::WARN , "Audio Filter has no suitable input!"); - audiofilter->Release(); - return 0; - } - bool secondstep=false; - pinenum->Reset(); - while (pinenum->Next(1,¤t,&fetch)==S_OK) - { - PIN_DIRECTION dir; - if (current->QueryDirection(&dir)==S_OK) - { - if (dir == PINDIR_OUTPUT) - { - - if (fg2->RenderEx((IPin*)current/*video*/, - 0,NULL) ==S_OK) - { - current->Release(); - secondstep=true; - break; - } - } - } - current->Release(); - } - if (secondstep==false) - { - Log::getInstance()->log("AudioWin", Log::WARN , "Audio Filter has no suitable output!"); - audiofilter->Release(); - - return 0; - } - - audiofilter->Release(); - pinenum->Release(); - - } - - - - fg2->Release(); - return 1; -} - - -IBaseFilter *AudioWin::getAudioFilter() -{ - IBaseFilter *curfilter= NULL; - bool notset=false; - if (audiofilterselected == -1) - { - int i; - for (i = 0;i BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) - { - if (curfilter != NULL && notset) - { - VDR *vdr=VDR::getInstance(); - if (vdr != NULL) - { - vdr->configSave("DirectShow","AudioFilter", - audiofilterlist[audiofilterselected].displayname); - } - } - - moni->Release(); - delete [] name; - bindctx->Release(); - return curfilter; - } - bindctx->Release(); - delete [] name; - return NULL; - } - return NULL; -} - -IBaseFilter *AudioWin::getMp3AudioFilter() -{ - IBaseFilter *curfilter= NULL; - bool notset=false; - if (mp3audiofilterselected == -1) - { - int i; - for (i = 0;i BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) - { - if (curfilter != NULL && notset) - { - VDR *vdr=VDR::getInstance(); - if (vdr != NULL) - { - vdr->configSave("DirectShow","Mp3AudioFilter", - mp3audiofilterlist[mp3audiofilterselected].displayname); - } - } - - moni->Release(); - delete [] name; - bindctx->Release(); - return curfilter; - } - bindctx->Release(); - delete [] name; - return NULL; - } - return NULL; -} - - -bool AudioWin::addOptionPagesToWTB(WTabBar *wtb) -{ - Boxx *box=new WWinAudioFilter(); - wtb->addTab(tr("Audio Filter"), box); - - - box=new WWinMp3AudioFilter(); - wtb->addTab(tr("Mp3 Audio Filter"), box); - - - return true; -} - -const AudioFilterDescList *AudioWin::getAudioFilterList(int &selected) -{ - selected=audiofilterselected; - return &audiofilterlist; -} - -const AudioFilterDescList *AudioWin::getMp3AudioFilterList(int &selected) -{ - selected=mp3audiofilterselected; - return &mp3audiofilterlist; -} -bool AudioWin::selectMp3AudioFilter(int filter) -{ - mp3audiofilterselected=filter; - return true; - -} - -bool AudioWin::selectAudioFilter(int filter) -{ - audiofilterselected=filter; - return true; - -} - -long long AudioWin::SetStartOffset(long long curreftime, bool *rsync){ - VideoWin *vw=(VideoWin*)Video::getInstance(); - return vw->SetStartAudioOffset(curreftime,rsync); -} - -void AudioWin::ResetTimeOffsets() { - VideoWin *vw=(VideoWin*)Video::getInstance(); - return vw->ResetTimeOffsets(); -} - -bool AudioWin::supportsAc3(){ - VideoWin *vw=(VideoWin*)Video::getInstance(); - return vw->supportsAc3(); -} - -#ifdef DEV -int AudioWin::test() -{ - return 0; -} -#endif - - - +/* + 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. +*/ + +#include "audiowin.h" +#include "videowin.h" +#include "vdr.h" +#include "wtabbar.h" +#include "wwinaudiofilter.h" +#include "wwinmp3audiofilter.h" +#include "i18n.h" + + + + +AudioWin::AudioWin() +{ + initted = 0; + firstsynched=false; + winvolume=0; + volume=20; +audiofilterselected=-1; + mp3audiofilterselected=-1; + aud_type=Audio::MPEG2_PES; + +} + +AudioWin::~AudioWin() +{ + + int i; + for (i=0;isetAudioStreamType(type); + aud_type=type; + if (!initted) return 0; + return 1; +} + +int AudioWin::setChannel() +{ + if (!initted) return 0; + return 1; +} + +int AudioWin::setSource() +{ + if (!initted) return 0; + return 1; +} + +int AudioWin::sync() +{ + if (!initted) return 0; + return 1; +} + +int AudioWin::play() +{ + if (!initted) return 0; + firstsynched=false; + return ((VideoWin*)Video::getInstance())->dsplay(); + +} + +int AudioWin::stop() +{ + if (!initted) return 0; + return ((VideoWin*)Video::getInstance())->dsstop(); +} + +int AudioWin::pause() +{ + if (!initted) return 0; + return ((VideoWin*)Video::getInstance())->dspause(); +} + +int AudioWin::unPause() +{ + if (!initted) return 0; + return ((VideoWin*)Video::getInstance())->dsunPause(); +} + +int AudioWin::reset() +{ + + if (!initted){return 0;} + return ((VideoWin*)Video::getInstance())->dsreset(); +} + +int AudioWin::setVolume(int tvolume) +{ + // parameter: 0 for silence, 20 for full + if ((tvolume < 0) || (tvolume > 20)) return 0; + winvolume=((tvolume-20)*100*30)/20; + if (tvolume==0) winvolume=-10000; + ((VideoWin*)Video::getInstance())->SetAudioVolume(winvolume); + + + return 1; +} + +int AudioWin::mute() +{ + if (!initted) return 0; + ((VideoWin*)Video::getInstance())->SetAudioState(false); + ((VideoWin*)Video::getInstance())->SetAudioVolume(-10000); + return 1; +} + +int AudioWin::unMute() +{ + if (!initted) return 0; + ((VideoWin*)Video::getInstance())->SetAudioState(true); + ((VideoWin*)Video::getInstance())->SetAudioVolume(winvolume); + return 1; +} + +void AudioWin::PrepareMediaSample(const MediaPacketList& mplist,UINT samplepos) +{ + mediapacket = mplist.front(); +} + +void AudioWin::initFilterDatabase() +{ + /* This method should determine all availiable DirectShow Filters */ + IFilterMapper2* filtmap=NULL; + HRESULT result; + result = CoCreateInstance(CLSID_FilterMapper2,NULL,CLSCTX_INPROC, + IID_IFilterMapper2,(void**)&filtmap); + if (result != S_OK) + { + Log::getInstance()->log("AudioWin", Log::ERR , "Unable to create FilterMapper!"); + return; + } + /* Wishlist, what Mediatypes do we want */ + GUID mtypesin[]={MEDIATYPE_Audio,MEDIASUBTYPE_MPEG2_AUDIO, + /*MEDIATYPE_Audio,MEDIASUBTYPE_MPEG1Payload,*/ + MEDIATYPE_Audio,MEDIASUBTYPE_DOLBY_AC3, + MEDIATYPE_Audio, MEDIASUBTYPE_DOLBY_AC3_SPDIF}; + IEnumMoniker *myenum; + result = filtmap->EnumMatchingFilters(&myenum,0,TRUE,MERIT_DO_NOT_USE+1, + TRUE,3,mtypesin,NULL,NULL,FALSE,TRUE,0,NULL,NULL,NULL); + if (result != S_OK) + { + filtmap->Release(); + Log::getInstance()->log("AudioWin", Log::ERR , "Unable to enum Filters!"); + return; + } + ULONG gethowmany; + IMoniker * moni; + while(myenum->Next(1,&moni,&gethowmany)==S_OK) + { + AudioFilterDesc desc; + ZeroMemory(&desc,sizeof(desc)); + + LPOLESTR string; + moni->GetDisplayName(0,0,&string); + desc.displayname=new char[wcslen(string)+1]; + wcstombs(desc.displayname,string,wcslen(string)+1); + CoTaskMemFree(string); + IPropertyBag *bag; + if (moni->BindToStorage(0,0,IID_IPropertyBag,(void**)&bag) == S_OK) + { + VARIANT vari; + VariantInit(&vari); + result = bag->Read(L"FriendlyName",&vari,NULL); + if (result == S_OK) + { + desc.friendlyname=new char[wcslen(vari.bstrVal)+1]; + wcstombs(desc.friendlyname,vari.bstrVal,wcslen(vari.bstrVal)+1); + } + VariantClear(&vari); + bag->Release(); + + } + + + audiofilterlist.push_back(desc); + + + + moni->Release(); + // bctx->Release(); + } + int i; + audiofilterselected=-1; + myenum->Release(); + filtmap->Release(); +} + +void AudioWin::initMp3FilterDatabase() +{ + /* This method should determine all availiable DirectShow Filters */ + IFilterMapper2* filtmap=NULL; + HRESULT result; + result = CoCreateInstance(CLSID_FilterMapper2,NULL,CLSCTX_INPROC, + IID_IFilterMapper2,(void**)&filtmap); + if (result != S_OK) + { + Log::getInstance()->log("AudioWin", Log::ERR , "Unable to create FilterMapper!"); + return; + } + /* Wishlist, what Mediatypes do we want */ + GUID mtypesin[]={MEDIATYPE_Audio,MEDIATYPE_WaveFmt_Mpeg1Layer3, + MEDIATYPE_Audio,MEDIASUBTYPE_MPEG2_AUDIO}; + IEnumMoniker *myenum; + result = filtmap->EnumMatchingFilters(&myenum,0,TRUE,MERIT_DO_NOT_USE+1, + TRUE,3,mtypesin,NULL,NULL,FALSE,TRUE,0,NULL,NULL,NULL); + if (result != S_OK) + { + filtmap->Release(); + Log::getInstance()->log("AudioWin", Log::ERR , "Unable to enum Filters!"); + return; + } + ULONG gethowmany; + IMoniker * moni; + while(myenum->Next(1,&moni,&gethowmany)==S_OK) + { + AudioFilterDesc desc; + ZeroMemory(&desc,sizeof(desc)); + + LPOLESTR string; + moni->GetDisplayName(0,0,&string); + desc.displayname=new char[wcslen(string)+1]; + wcstombs(desc.displayname,string,wcslen(string)+1); + CoTaskMemFree(string); + IPropertyBag *bag; + if (moni->BindToStorage(0,0,IID_IPropertyBag,(void**)&bag) == S_OK) + { + VARIANT vari; + VariantInit(&vari); + result = bag->Read(L"FriendlyName",&vari,NULL); + if (result == S_OK) + { + desc.friendlyname=new char[wcslen(vari.bstrVal)+1]; + wcstombs(desc.friendlyname,vari.bstrVal,wcslen(vari.bstrVal)+1); + } + VariantClear(&vari); + bag->Release(); + + } + + + mp3audiofilterlist.push_back(desc); + + + + moni->Release(); + // bctx->Release(); + } + int i; + mp3audiofilterselected=-1; + myenum->Release(); + filtmap->Release(); +} + +bool AudioWin::loadOptionsfromServer(VDR* vdr) +{ + char *name=vdr->configLoad("DirectShow","AudioFilter"); + + if (name != NULL) + { + for (int i = 0;i configLoad("DirectShow","Mp3AudioFilter"); + + if (name != NULL) + { + for (int i = 0;i configSave("DirectShow", + "AudioFilter",audiofilterlist[audiofilterselected].displayname); + } + if (mp3audiofilterselected!=-1) { + VDR::getInstance()->configSave("DirectShow", + "Mp3AudioFilter",mp3audiofilterlist[mp3audiofilterselected].displayname); + } + return true; +} + +UINT AudioWin::DeliverMediaSample(UCHAR* buffer, UINT *samplepos) +{ + DeliverMediaPacket(mediapacket, buffer, samplepos); + if (*samplepos == mediapacket.length) { + *samplepos = 0; + return 1; + } + else return 0; +} + +UINT AudioWin::DeliverMediaPacket(const MediaPacket packet, + UCHAR* buffer, + UINT *samplepos) +{ + + /*First Check, if we have an audio sample*/ + VideoWin *vw=(VideoWin*)Video::getInstance(); + if (!vw->isdsinited()) return 0; + if (vw->InIframemode()) { + samplepos=0; + MILLISLEEP(10); + return 0; //Not in iframe mode! + } + IMediaSample* ms=NULL; + REFERENCE_TIME reftime1=0; + REFERENCE_TIME reftime2=0; + + UINT headerstrip=0; + if (packet.disconti) { + firstsynched=false; + vw->DeliverAudioMediaSample(); + } + + if (packet.type!=vw->lastAType()){//Format Change //Push data out ! + firstsynched=false; + vw->DeliverAudioMediaSample(); + } + + + + /*Inspect PES-Header */ +/* UINT header_length=buffer[(packet.pos_buffer+8)%bufferlength]+8/*is this right*; +*/ + if (*samplepos==0 && packet.type!=MPTYPE_MPEG_AUDIO_LAYER3) {//stripheader + headerstrip=buffer[packet.pos_buffer+8]+9; + if (packet.type == MPTYPE_AC3) headerstrip+=4; //skip ac3 bytes + *samplepos+=headerstrip; + if ( packet.synched ) { + vw->DeliverAudioMediaSample();//write out old data + reftime1=packet.presentation_time; + reftime2=reftime1+1; + firstsynched=true; + } else { + if (!firstsynched) {// + *samplepos=packet.length;//if we have not processed at least one + return packet.length;//synched packet ignore it! + } + } + } + BYTE *ms_buf; + UINT ms_length; + UINT ms_pos; + UINT haveToCopy; + if (!vw->getCurrentAudioMediaSample(&ms) || ms==NULL) {// get the current sample + //samplepos=0; + //MILLISLEEP(10); + return *samplepos; + } + ms_pos=ms->GetActualDataLength(); + ms_length=ms->GetSize(); + haveToCopy=min(ms_length-ms_pos,packet.length-*samplepos); + if ((ms_length-ms_pos)<1) { + vw->DeliverAudioMediaSample(); //we are full! + if (!vw->getCurrentAudioMediaSample(&ms) || ms==NULL) {// get the current sample + //samplepos=0; + //MILLISLEEP(10); + return *samplepos; + } + ms_pos=ms->GetActualDataLength(); + ms_length=ms->GetSize(); + haveToCopy=min(ms_length-ms_pos,packet.length-*samplepos); + } + ms->GetPointer(&ms_buf); + + + if (ms_pos==0) {//will only be changed on first packet + if (packet.disconti) { + ms->SetDiscontinuity(TRUE); + } else { + ms->SetDiscontinuity(FALSE); + } + if (packet.synched) { + ms->SetSyncPoint(TRUE); + ms->SetTime(&reftime1,&reftime2); + + //ms->SetTime(NULL,NULL); + ms->SetMediaTime(NULL, NULL); + if (reftime1<0) ms->SetPreroll(TRUE); + else ms->SetPreroll(FALSE); + }else { + ms->SetSyncPoint(FALSE); + ms->SetTime(NULL,NULL); + ms->SetMediaTime(NULL, NULL); + ms->SetPreroll(FALSE); + // ms->SetSyncPoint(TRUE); + } + } + if (packet.type!=vw->lastAType()) { + vw->changeAType(packet.type,ms); + ms->SetDiscontinuity(TRUE); + } + + + memcpy(ms_buf+ms_pos,buffer+packet.pos_buffer+*samplepos,haveToCopy); + + ms->SetActualDataLength(haveToCopy+ms_pos); + + *samplepos+=haveToCopy; + + return haveToCopy+headerstrip; + +} + +int AudioWin::dsInitAudioFilter(IGraphBuilder* dsgraphbuilder) +{ + HRESULT hres; + IFilterGraph2*fg2=NULL; + VideoWin *vw=(VideoWin*)Video::getInstance(); + if (dsgraphbuilder->QueryInterface(IID_IFilterGraph2,(void**)&fg2)!= S_OK) + { + Log::getInstance()->log("AudiooWin", Log::WARN , "Failed querying for FilterGraph2 Interface!"); + return 0; + } + IBaseFilter*audiofilter; + if (aud_type!=Audio::MP3) { + audiofilter = getAudioFilter(); + } else { + audiofilter = getMp3AudioFilter(); + } + if (dsgraphbuilder->AddFilter(audiofilter,NULL) != S_OK) + { + Log::getInstance()->log("AudioWin", Log::WARN , "Failed adding Video Filter!"); + return 0; + } + IEnumPins *pinenum=NULL; + bool error=false; + if (audiofilter->EnumPins(&pinenum) == S_OK) + { + IPin *current=NULL; + ULONG fetch=0; + bool firststep=false; + while (pinenum->Next(1,¤t,&fetch)==S_OK) + { + PIN_DIRECTION dir; + if (current->QueryDirection(&dir)==S_OK) + { + if (dir == PINDIR_INPUT) + { + if (vw->getSourceFilter()->GetAudioPin()->Connect(current,NULL)==S_OK) + { + current->Release(); + firststep=true; + break; + } + } + } + current->Release(); + } + if (firststep==false) + { + Log::getInstance()->log("AudioWin", Log::WARN , "Audio Filter has no suitable input!"); + audiofilter->Release(); + return 0; + } + bool secondstep=false; + pinenum->Reset(); + while (pinenum->Next(1,¤t,&fetch)==S_OK) + { + PIN_DIRECTION dir; + if (current->QueryDirection(&dir)==S_OK) + { + if (dir == PINDIR_OUTPUT) + { + + if (fg2->RenderEx((IPin*)current/*video*/, + 0,NULL) ==S_OK) + { + current->Release(); + secondstep=true; + break; + } + } + } + current->Release(); + } + if (secondstep==false) + { + Log::getInstance()->log("AudioWin", Log::WARN , "Audio Filter has no suitable output!"); + audiofilter->Release(); + + return 0; + } + + audiofilter->Release(); + pinenum->Release(); + + } + + + + fg2->Release(); + return 1; +} + + +IBaseFilter *AudioWin::getAudioFilter() +{ + IBaseFilter *curfilter= NULL; + bool notset=false; + if (audiofilterselected == -1) + { + int i; + for (i = 0;i BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) + { + if (curfilter != NULL && notset) + { + VDR *vdr=VDR::getInstance(); + if (vdr != NULL) + { + vdr->configSave("DirectShow","AudioFilter", + audiofilterlist[audiofilterselected].displayname); + } + } + + moni->Release(); + delete [] name; + bindctx->Release(); + return curfilter; + } + bindctx->Release(); + delete [] name; + return NULL; + } + return NULL; +} + +IBaseFilter *AudioWin::getMp3AudioFilter() +{ + IBaseFilter *curfilter= NULL; + bool notset=false; + if (mp3audiofilterselected == -1) + { + int i; + for (i = 0;i BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) + { + if (curfilter != NULL && notset) + { + VDR *vdr=VDR::getInstance(); + if (vdr != NULL) + { + vdr->configSave("DirectShow","Mp3AudioFilter", + mp3audiofilterlist[mp3audiofilterselected].displayname); + } + } + + moni->Release(); + delete [] name; + bindctx->Release(); + return curfilter; + } + bindctx->Release(); + delete [] name; + return NULL; + } + return NULL; +} + + +bool AudioWin::addOptionPagesToWTB(WTabBar *wtb) +{ + Boxx *box=new WWinAudioFilter(); + wtb->addTab(tr("Audio Filter"), box); + + + box=new WWinMp3AudioFilter(); + wtb->addTab(tr("Mp3 Audio Filter"), box); + + + return true; +} + +const AudioFilterDescList *AudioWin::getAudioFilterList(int &selected) +{ + selected=audiofilterselected; + return &audiofilterlist; +} + +const AudioFilterDescList *AudioWin::getMp3AudioFilterList(int &selected) +{ + selected=mp3audiofilterselected; + return &mp3audiofilterlist; +} +bool AudioWin::selectMp3AudioFilter(int filter) +{ + mp3audiofilterselected=filter; + return true; + +} + +bool AudioWin::selectAudioFilter(int filter) +{ + audiofilterselected=filter; + return true; + +} + +long long AudioWin::SetStartOffset(long long curreftime, bool *rsync){ + VideoWin *vw=(VideoWin*)Video::getInstance(); + return vw->SetStartAudioOffset(curreftime,rsync); +} + +void AudioWin::ResetTimeOffsets() { + VideoWin *vw=(VideoWin*)Video::getInstance(); + vw->ResetTimeOffsets(); +} + +bool AudioWin::supportsAc3(){ + VideoWin *vw=(VideoWin*)Video::getInstance(); + return vw->supportsAc3(); +} + +#ifdef DEV +int AudioWin::test() +{ + return 0; +} +#endif + + + diff --git a/bitmap.h b/bitmap.h index 654775f..115cf31 100644 --- a/bitmap.h +++ b/bitmap.h @@ -39,6 +39,7 @@ class Palette const std::vector& getCrVector() const { return Cr; } const std::vector& getCbVector() const { return Cb; } const std::vector& getAVector() const { return A; } + const UINT getNumColours()const{return numColours;}; private: const static UINT MAX_DEPTH = 8; std::vector colour; @@ -70,4 +71,5 @@ class Bitmap void setAllIndices(UCHAR index); }; + #endif diff --git a/boxstack.cc b/boxstack.cc index cbf2dac..739dd5a 100644 --- a/boxstack.cc +++ b/boxstack.cc @@ -1,616 +1,617 @@ -/* - 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. -*/ - -#include "boxstack.h" - -#include "command.h" -#include "remote.h" -#include "log.h" - -BoxStack* BoxStack::instance = NULL; - -BoxStack::BoxStack() -{ - if (instance) return; - instance = this; - initted = 0; - numBoxes = 0; -} - -BoxStack::~BoxStack() -{ - instance = NULL; -} - -BoxStack* BoxStack::getInstance() -{ - return instance; -} - -int BoxStack::init() -{ - if (initted) return 0; - initted = 1; - -#ifndef WIN32 - pthread_mutex_init(&boxLock, NULL); -#else - boxLock = CreateMutex(NULL,FALSE,NULL); -#endif - - return 1; -} - -int BoxStack::shutdown() -{ - if (!initted) return 0; - - // FIXME don't think this can work properly, removeAll leaves the wallpaper there! - removeAll(); - - initted = 0; - return 1; -} - -int BoxStack::add(Boxx* v) -{ - if (!initted) return 0; - Log::getInstance()->log("BoxStack", Log::DEBUG, "add called"); -#ifndef WIN32 - pthread_mutex_lock(&boxLock); -#else - WaitForSingleObject(boxLock, INFINITE); -#endif - Log::getInstance()->log("BoxStack", Log::DEBUG, "Locked for add"); - - if (numBoxes == 16) - { //Error - Log::getInstance()->log("BoxStack", Log::ERR, "More than 16 boxes! Unlocked for add"); -#ifndef WIN32 - pthread_mutex_unlock(&boxLock); -#else - ReleaseMutex(boxLock); -#endif - return 0; - } - boxes[numBoxes++] = v; - -#ifndef WIN32 - pthread_mutex_unlock(&boxLock); -#else - ReleaseMutex(boxLock); -#endif - - Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for add"); - - return 1; -} - -// ---------------------------------------------------- REMOVE CODE - -int BoxStack::remove(Boxx* toDelete) -{ - if (!initted) return 0; - - #ifndef WIN32 - pthread_mutex_lock(&boxLock); - #else - WaitForSingleObject(boxLock, INFINITE); - #endif - Log::getInstance()->log("BoxStack", Log::DEBUG, "Locked for remove"); - - if (numBoxes == 0) - { - #ifndef WIN32 - pthread_mutex_unlock(&boxLock); - #else - ReleaseMutex(boxLock); - #endif - Log::getInstance()->log("BoxStack", Log::ERR, "Unlocked for remove numBoxes == 0"); - return 0; - } - -// Log::getInstance()->log("BoxStack", Log::DEBUG, "entering remove, numBoxes=%i", numBoxes); - - int i; - - if (toDelete == NULL) - { - toDelete = boxes[numBoxes-1]; - i = numBoxes - 1; - } - else - { - // to be deleted box is more likely to be at the top - for (i = numBoxes-1; i >= 0; i--) - { -// Log::getInstance()->log("BoxStack", Log::DEBUG, "todel: %p, i=%i, boxes[i]=%p", toDelete, i, boxes[i]); - if (boxes[i] == toDelete) break; - } - - if (i == -1) - { - // not a Box we have! - // FIXME - #ifndef WIN32 - pthread_mutex_unlock(&boxLock); - #else - ReleaseMutex(boxLock); - #endif - Log::getInstance()->log("BoxStack", Log::ERR, "Unlocked for remove - no boxx deleted"); - return 0; - } - } - -#ifndef WIN32 - pthread_mutex_unlock(&boxLock); -#else - ReleaseMutex(boxLock); -#endif - -toDelete->preDelete(); - -#ifndef WIN32 - pthread_mutex_lock(&boxLock); -#else - WaitForSingleObject(boxLock, INFINITE); -#endif - -// Log::getInstance()->log("BoxStack", Log::DEBUG, "Starting deleteBox"); - deleteBox(i); -// Log::getInstance()->log("BoxStack", Log::DEBUG, "Done deleteBox"); - - // Shift the boxes on top down one - --numBoxes; - for(int j = i; j < numBoxes; j++) boxes[j] = boxes[j+1]; - - // If there is only the wallpaper left signal command - if (numBoxes == 1) - { - Message* m = new Message(); - m->to = Command::getInstance(); - m->message = Message::LAST_VIEW_CLOSE; - Command::getInstance()->postMessageNoLock(m); - } - -#ifndef WIN32 - pthread_mutex_unlock(&boxLock); -#else - ReleaseMutex(boxLock); -#endif - Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for remove"); - - // Delete the box - //AVO: do this delete outside the lock to allow for recursive calls within the destructor - // as this box is not in the stack any more, there is no chance for a second delete - Log::getInstance()->log("BoxStack", Log::DEBUG, "remove: going to delete boxx %p, num %d", toDelete, numBoxes); - delete toDelete; - - return 1; -} - -///////////////////////////////////////////////////////////////////////////// -// NEW STUFF -///////////////////////////////////////////////////////////////////////////// - -void BoxStack::deleteBox(int z) -{ -// Log::getInstance()->log("BoxStack", Log::DEBUG, "Delete box %i of %i", z, numBoxes); - RegionList rl; - boxSplit(boxes[z]->area, z + 1, numBoxes, 1, rl); - while(!rl.empty()) - { - repaintRevealed(z, rl.front()); - rl.pop_front(); - } -} - -void BoxStack::update(Boxx* toUpdate, Region* regionToUpdate) -{ - Log::getInstance()->log("BoxStack", Log::DEBUG, "Update called"); - -#ifndef WIN32 - pthread_mutex_lock(&boxLock); -#else - WaitForSingleObject(boxLock, INFINITE); -#endif - Log::getInstance()->log("BoxStack", Log::DEBUG, "Locked for update"); - - // Get the z index of the box - int z = 0; - if (toUpdate) - { - for (z = 0; z < numBoxes; z++) - { - if (boxes[z] == toUpdate) break; - } - - if (z == numBoxes) - { - // not a Box we have! - #ifndef WIN32 - pthread_mutex_unlock(&boxLock); - #else - ReleaseMutex(boxLock); - #endif - Log::getInstance()->log("BoxStack", Log::ERR, "Unlocked for update! box not inside boxstack"); - return; - } - } - else - { - toUpdate = boxes[0]; - } - - // get the region for the whole box, could be less than that - // for smaller updates - - Region r = toUpdate->area; - - if (regionToUpdate) - { - // Can be null if the whole box should be updated - // If this is given the numbers are relative to the size of the box, not the screen - - r.x += regionToUpdate->x; - r.y += regionToUpdate->y; - r.w = regionToUpdate->w; - r.h = regionToUpdate->h; - } - - RegionList rl; - - Region r2; - boxSplit(r, z+1, numBoxes, 1, rl); - while(!rl.empty()) - { - r2 = rl.front(); - r2.z = z; - boxes[z]->blt(r2); - rl.pop_front(); - } - -#ifndef WIN32 - pthread_mutex_unlock(&boxLock); -#else - ReleaseMutex(boxLock); -#endif - Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for update"); -} - -void BoxStack::repaintRevealed(int x, Region r) -{ - RegionList rl; - boxSplit(r, x - 1, -1, -1, rl); - - Region r2; - while(!rl.empty()) - { - r2 = rl.front(); - boxes[r2.z]->blt(r2); - rl.pop_front(); - } -} - -void BoxStack::boxSplit(Region r, int start, int end, int direction, RegionList& rl) -{ -// printf("Y= S=%i E=%i D=%i: Boxsplit: %i %i %i %i\n", start, end, direction, r.x, r.y, r.w, r.h); - - for(int z = start; z != end; z += direction) - { - if (r.overlappedBy(boxes[z]->area)) - { -// printf("Z=%i S=%i E=%i D=%i: %i overlaps\n", z, start, end, direction, z); - - int top = r.y; - int btm = r.y2(); - - if (boxes[z]->area.y > r.y) - { -// printf("Z=%i S=%i E=%i D=%i: Case 1 for %i %i %i %i split by %i: %i %i %i %i\n", z, start, end, direction, r.x, r.y, r.w, r.h, z, boxes[z]->area.x, boxes[z]->area.y, boxes[z]->area.w, boxes[z]->area.h); - top = boxes[z]->area.y; - Region newR; - newR.x = r.x; - newR.y = r.y; - newR.w = r.w; - newR.h = boxes[z]->area.y - r.y; - boxSplit(newR, z + direction, end, direction, rl); - - if (direction == -1) - { - Region newR2; - newR2.x = r.x; - newR2.y = boxes[z]->area.y; - newR2.w = r.w; - newR2.h = r.h - newR.h; - boxSplit(newR2, z, end, -1, rl); - return; - } - } - - if (boxes[z]->area.y2() < r.y2()) - { -// printf("Z=%i S=%i E=%i D=%i: Case 2 for %i %i %i %i split by %i: %i %i %i %i\n", z, start, end, direction, r.x, r.y, r.w, r.h, z, boxes[z]->area.x, boxes[z]->area.y, boxes[z]->area.w, boxes[z]->area.h); - btm = boxes[z]->area.y2(); - Region newR; - newR.x = r.x; - newR.y = boxes[z]->area.y2() + 1; - newR.w = r.w; - newR.h = r.y2() - newR.y + 1; - boxSplit(newR, z + direction, end, direction, rl); - - if (direction == -1) - { - Region newR2; - newR2.x = r.x; - newR2.y = r.y; - newR2.w = r.w; - newR2.h = r.h - newR.h; - boxSplit(newR2, z, end, -1, rl); - return; - } - } - - if (boxes[z]->area.x > r.x) - { -// printf("Z=%i S=%i E=%i D=%i: Case 3 for %i %i %i %i split by %i: %i %i %i %i\n", z, start, end, direction, r.x, r.y, r.w, r.h, z, boxes[z]->area.x, boxes[z]->area.y, boxes[z]->area.w, boxes[z]->area.h); - Region newR; - newR.x = r.x; - newR.y = top; - newR.w = boxes[z]->area.x - r.x; - newR.h = btm - top + 1; - boxSplit(newR, z + direction, end, direction, rl); - - if (direction == -1) - { - Region newR2; - newR2.x = r.x + newR.w; - newR2.y = r.y; - newR2.w = r.w - newR.w; - newR2.h = r.h; - boxSplit(newR2, z, end, -1, rl); - return; - } - } - - if (boxes[z]->area.x2() < r.x2()) - { -// printf("Z=%i S=%i E=%i D=%i: Case 4 for %i %i %i %i split by %i: %i %i %i %i\n", z, start, end, direction, r.x, r.y, r.w, r.h, z, boxes[z]->area.x, boxes[z]->area.y, boxes[z]->area.w, boxes[z]->area.h); - Region newR; - newR.x = boxes[z]->area.x2() + 1; - newR.y = top; - newR.w = r.x2() - newR.x + 1; - newR.h = btm - top + 1; - boxSplit(newR, z + direction, end, direction, rl); - - if (direction == -1) - { - Region newR2; - newR2.x = r.x; - newR2.y = r.y; - newR2.w = r.w - newR.w; - newR2.h = r.h; - boxSplit(newR2, z, end, -1, rl); - return; - } - } - - if (direction == -1) - { - // we are going down the stack - // r is underlapped by boxes[z] - // but we have not split - // Therefore this region under test is - // completely covering boxes[z] - - // don't go any further down, generate a region and quit - -// printf("Repaint region: %i %i %i %i\n", r.x, r.y, r.w, r.h); - r.z = z; - rl.push_front(r); - } - -// printf("Returning from Z=%i\n", z); - return; - } - else - { -// printf("Z=%i S=%i E=%i D=%i: %i does not overlap\n", z, start, end, direction, z); - } - } - - // if direction = 1 then we have come to a region that is - // entirely clear of higher boxes and needs to be redrawn - - // if direction = -1 then we have come to a region that is on - // the very bottom with nothing below it to repaint. - // do nothing. stale window data will be left on screen? - - if (direction == 1) - { - rl.push_front(r); - } -} - -///////////////////////////////////////////////////////////////////////////// -// END NEW STUFF -///////////////////////////////////////////////////////////////////////////// - -// ---------------------------------------------------- END OF REMOVE CODE - - -void BoxStack::removeAll() -{ - // 1.. Don't delete wallpaper. No point. - - // Need locking on this one?? - - // This is pretty silly now that preDelete needs mutex unlocked - - Boxx* toDel = NULL; - - while(numBoxes > 1) - { - #ifndef WIN32 - pthread_mutex_lock(&boxLock); - #else - WaitForSingleObject(boxLock, INFINITE); - #endif - - if (numBoxes == 1) - { - #ifndef WIN32 - pthread_mutex_unlock(&boxLock); - #else - ReleaseMutex(boxLock); - #endif - break; - } - - toDel = boxes[numBoxes - 1]; - - #ifndef WIN32 - pthread_mutex_unlock(&boxLock); - #else - ReleaseMutex(boxLock); - #endif - - toDel->preDelete(); - - #ifndef WIN32 - pthread_mutex_lock(&boxLock); - #else - WaitForSingleObject(boxLock, INFINITE); - #endif - - // If boxes[numBoxes - 1] isn't toDel then there's a problem - if (boxes[numBoxes - 1] == toDel) - { - --numBoxes; - } - else - { - Log::getInstance()->log("BoxStack", Log::ERR, "Can this actually happen? Why?"); - toDel = NULL; - } - - #ifndef WIN32 - pthread_mutex_unlock(&boxLock); - #else - ReleaseMutex(boxLock); - #endif - - //AVO: do the delete outside the lock to allow for recursive deletes - Log::getInstance()->log("BoxStack", Log::DEBUG, "going to delete boxx %p, num=%d", toDel, numBoxes); - if (toDel) delete toDel; - } -} - -int BoxStack::handleCommand(int command) -{ - int retVal; - int retVal2 = 0; - int i; - - if (command != Remote::NA_NONE) - { - // handle command return values - // 0 - drop through to next box - // 1 - dont drop to next box, but not handled - // 2 - handled - stop command here - // 4 - handled - delete this box - - for (i=numBoxes-1; i>=0; i--) - { -// Log::getInstance()->log("BoxStack", Log::DEBUG, "Giving command to i=%i", i); - retVal = boxes[i]->handleCommand(command); - if (retVal == 1) - { - // not handled but don't give to any more boxes - return 0; - } - - if (retVal == 2) - { - // command handled - retVal2 = 1; - break; - } - else if (retVal == 4) - { -// Log::getInstance()->log("BoxStack", Log::DEBUG, "Return 4: i=%i, boxes[i]=%p", i, boxes[i]); - remove(boxes[i]); - retVal2 = 1; - break; - } - } - } - else - { - // fake the return code - retVal2 = 2; - } - - return retVal2; -} - -void BoxStack::processMessage(Message* m) -{ - if (m->to != this) - { - for (int i = numBoxes-1; i >= 0; i--) - { - if (boxes[i] == m->to) - { - Log::getInstance()->log("BoxStack", Log::DEBUG, "sending message from box %p to box %p %lu", m->from, m->to, m->message); - boxes[i]->processMessage(m); - return; - } - } - return; - } - - /* Handle mouse events*/ - // They come in with m->to = NULL? and just need to be delivered to top box? - if ((numBoxes > 1) && ((m->message == Message::MOUSE_MOVE) || (m->message == Message::MOUSE_LBDOWN))) - { - boxes[numBoxes-1]->processMessage(m); - return; - } - - Log::getInstance()->log("BoxStack", Log::DEBUG, "it's for meeee!"); - - switch(m->message) - { - case Message::CLOSE_ME: - { - remove((Boxx*)m->from); - break; - } - case Message::ADD_VIEW: // currently not used by anything but it might come in useful again - { - Boxx* toAdd = (Boxx*)m->parameter; - add(toAdd); - toAdd->draw(); - update(toAdd); - break; - } - } -} +/* + 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. +*/ + +#include "boxstack.h" + +#include "command.h" +#include "remote.h" +#include "log.h" + +BoxStack* BoxStack::instance = NULL; + +BoxStack::BoxStack() +{ + if (instance) return; + instance = this; + initted = 0; + numBoxes = 0; +} + +BoxStack::~BoxStack() +{ + instance = NULL; +} + +BoxStack* BoxStack::getInstance() +{ + return instance; +} + +int BoxStack::init() +{ + if (initted) return 0; + initted = 1; + +#ifndef WIN32 + pthread_mutex_init(&boxLock, NULL); +#else + boxLock = CreateMutex(NULL,FALSE,NULL); +#endif + + return 1; +} + +int BoxStack::shutdown() +{ + if (!initted) return 0; + + // FIXME don't think this can work properly, removeAll leaves the wallpaper there! + removeAll(); + + initted = 0; + return 1; +} + +int BoxStack::add(Boxx* v) +{ + if (!initted) return 0; + Log::getInstance()->log("BoxStack", Log::DEBUG, "add called"); +#ifndef WIN32 + pthread_mutex_lock(&boxLock); +#else + WaitForSingleObject(boxLock, INFINITE); +#endif + Log::getInstance()->log("BoxStack", Log::DEBUG, "Locked for add"); + + if (numBoxes == 16) + { //Error + Log::getInstance()->log("BoxStack", Log::ERR, "More than 16 boxes! Unlocked for add"); +#ifndef WIN32 + pthread_mutex_unlock(&boxLock); +#else + ReleaseMutex(boxLock); +#endif + return 0; + } + boxes[numBoxes++] = v; + +#ifndef WIN32 + pthread_mutex_unlock(&boxLock); +#else + ReleaseMutex(boxLock); +#endif + + Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for add"); + + return 1; +} + +// ---------------------------------------------------- REMOVE CODE + +int BoxStack::remove(Boxx* toDelete) +{ + if (!initted) return 0; + + #ifndef WIN32 + pthread_mutex_lock(&boxLock); + #else + WaitForSingleObject(boxLock, INFINITE); + #endif + Log::getInstance()->log("BoxStack", Log::DEBUG, "Locked for remove"); + + if (numBoxes == 0) + { + #ifndef WIN32 + pthread_mutex_unlock(&boxLock); + #else + ReleaseMutex(boxLock); + #endif + Log::getInstance()->log("BoxStack", Log::ERR, "Unlocked for remove numBoxes == 0"); + return 0; + } + +// Log::getInstance()->log("BoxStack", Log::DEBUG, "entering remove, numBoxes=%i", numBoxes); + + int i; + + if (toDelete == NULL) + { + toDelete = boxes[numBoxes-1]; + i = numBoxes - 1; + } + else + { + // to be deleted box is more likely to be at the top + for (i = numBoxes-1; i >= 0; i--) + { +// Log::getInstance()->log("BoxStack", Log::DEBUG, "todel: %p, i=%i, boxes[i]=%p", toDelete, i, boxes[i]); + if (boxes[i] == toDelete) break; + } + + if (i == -1) + { + // not a Box we have! + // FIXME + #ifndef WIN32 + pthread_mutex_unlock(&boxLock); + #else + ReleaseMutex(boxLock); + #endif + Log::getInstance()->log("BoxStack", Log::ERR, "Unlocked for remove - no boxx deleted"); + return 0; + } + } + +#ifndef WIN32 + pthread_mutex_unlock(&boxLock); +#else + ReleaseMutex(boxLock); +#endif + +toDelete->preDelete(); + +#ifndef WIN32 + pthread_mutex_lock(&boxLock); +#else + WaitForSingleObject(boxLock, INFINITE); +#endif + +// Log::getInstance()->log("BoxStack", Log::DEBUG, "Starting deleteBox"); + deleteBox(i); +// Log::getInstance()->log("BoxStack", Log::DEBUG, "Done deleteBox"); + + // Shift the boxes on top down one + --numBoxes; + for(int j = i; j < numBoxes; j++) boxes[j] = boxes[j+1]; + + // If there is only the wallpaper left signal command + if (numBoxes == 1) + { + Message* m = new Message(); + m->to = Command::getInstance(); + m->message = Message::LAST_VIEW_CLOSE; + Command::getInstance()->postMessageNoLock(m); + } + +#ifndef WIN32 + pthread_mutex_unlock(&boxLock); +#else + ReleaseMutex(boxLock); +#endif + Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for remove"); + + // Delete the box + //AVO: do this delete outside the lock to allow for recursive calls within the destructor + // as this box is not in the stack any more, there is no chance for a second delete + Log::getInstance()->log("BoxStack", Log::DEBUG, "remove: going to delete boxx %p, num %d", toDelete, numBoxes); + delete toDelete; + + return 1; +} + +///////////////////////////////////////////////////////////////////////////// +// NEW STUFF +///////////////////////////////////////////////////////////////////////////// + +void BoxStack::deleteBox(int z) +{ +// Log::getInstance()->log("BoxStack", Log::DEBUG, "Delete box %i of %i", z, numBoxes); + RegionList rl; + boxSplit(boxes[z]->area, z + 1, numBoxes, 1, rl); + while(!rl.empty()) + { + repaintRevealed(z, rl.front()); + rl.pop_front(); + } +} + +void BoxStack::update(Boxx* toUpdate, Region* regionToUpdate) +{ + Log::getInstance()->log("BoxStack", Log::DEBUG, "Update called"); + +#ifndef WIN32 + pthread_mutex_lock(&boxLock); +#else + WaitForSingleObject(boxLock, INFINITE); +#endif + Log::getInstance()->log("BoxStack", Log::DEBUG, "Locked for update"); + + // Get the z index of the box + int z = 0; + if (toUpdate) + { + for (z = 0; z < numBoxes; z++) + { + if (boxes[z] == toUpdate) break; + } + + if (z == numBoxes) + { + // not a Box we have! + #ifndef WIN32 + pthread_mutex_unlock(&boxLock); + #else + ReleaseMutex(boxLock); + #endif + Log::getInstance()->log("BoxStack", Log::ERR, "Unlocked for update! box not inside boxstack"); + return; + } + } + else + { + toUpdate = boxes[0]; + } + + // get the region for the whole box, could be less than that + // for smaller updates + + Region r = toUpdate->area; + + if (regionToUpdate) + { + // Can be null if the whole box should be updated + // If this is given the numbers are relative to the size of the box, not the screen + + r.x += regionToUpdate->x; + r.y += regionToUpdate->y; + r.w = regionToUpdate->w; + r.h = regionToUpdate->h; + } + + RegionList rl; + + Region r2; + boxSplit(r, z+1, numBoxes, 1, rl); + while(!rl.empty()) + { + r2 = rl.front(); + r2.z = z; + boxes[z]->blt(r2); + rl.pop_front(); + } + +#ifndef WIN32 + pthread_mutex_unlock(&boxLock); +#else + ReleaseMutex(boxLock); +#endif + Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for update"); +} + +void BoxStack::repaintRevealed(int x, Region r) +{ + RegionList rl; + boxSplit(r, x - 1, -1, -1, rl); + + Region r2; + while(!rl.empty()) + { + r2 = rl.front(); + boxes[r2.z]->blt(r2); + rl.pop_front(); + } +} + +void BoxStack::boxSplit(Region r, int start, int end, int direction, RegionList& rl) +{ +// printf("Y= S=%i E=%i D=%i: Boxsplit: %i %i %i %i\n", start, end, direction, r.x, r.y, r.w, r.h); + + for(int z = start; z != end; z += direction) + { + if (r.overlappedBy(boxes[z]->area)) + { +// printf("Z=%i S=%i E=%i D=%i: %i overlaps\n", z, start, end, direction, z); + + int top = r.y; + int btm = r.y2(); + + if (boxes[z]->area.y > r.y) + { +// printf("Z=%i S=%i E=%i D=%i: Case 1 for %i %i %i %i split by %i: %i %i %i %i\n", z, start, end, direction, r.x, r.y, r.w, r.h, z, boxes[z]->area.x, boxes[z]->area.y, boxes[z]->area.w, boxes[z]->area.h); + top = boxes[z]->area.y; + Region newR; + newR.x = r.x; + newR.y = r.y; + newR.w = r.w; + newR.h = boxes[z]->area.y - r.y; + boxSplit(newR, z + direction, end, direction, rl); + + if (direction == -1) + { + Region newR2; + newR2.x = r.x; + newR2.y = boxes[z]->area.y; + newR2.w = r.w; + newR2.h = r.h - newR.h; + boxSplit(newR2, z, end, -1, rl); + return; + } + } + + if (boxes[z]->area.y2() < r.y2()) + { +// printf("Z=%i S=%i E=%i D=%i: Case 2 for %i %i %i %i split by %i: %i %i %i %i\n", z, start, end, direction, r.x, r.y, r.w, r.h, z, boxes[z]->area.x, boxes[z]->area.y, boxes[z]->area.w, boxes[z]->area.h); + btm = boxes[z]->area.y2(); + Region newR; + newR.x = r.x; + newR.y = boxes[z]->area.y2() + 1; + newR.w = r.w; + newR.h = r.y2() - newR.y + 1; + boxSplit(newR, z + direction, end, direction, rl); + + if (direction == -1) + { + Region newR2; + newR2.x = r.x; + newR2.y = r.y; + newR2.w = r.w; + newR2.h = r.h - newR.h; + boxSplit(newR2, z, end, -1, rl); + return; + } + } + + if (boxes[z]->area.x > r.x) + { +// printf("Z=%i S=%i E=%i D=%i: Case 3 for %i %i %i %i split by %i: %i %i %i %i\n", z, start, end, direction, r.x, r.y, r.w, r.h, z, boxes[z]->area.x, boxes[z]->area.y, boxes[z]->area.w, boxes[z]->area.h); + Region newR; + newR.x = r.x; + newR.y = top; + newR.w = boxes[z]->area.x - r.x; + newR.h = btm - top + 1; + boxSplit(newR, z + direction, end, direction, rl); + + if (direction == -1) + { + Region newR2; + newR2.x = r.x + newR.w; + newR2.y = r.y; + newR2.w = r.w - newR.w; + newR2.h = r.h; + boxSplit(newR2, z, end, -1, rl); + return; + } + } + + if (boxes[z]->area.x2() < r.x2()) + { +// printf("Z=%i S=%i E=%i D=%i: Case 4 for %i %i %i %i split by %i: %i %i %i %i\n", z, start, end, direction, r.x, r.y, r.w, r.h, z, boxes[z]->area.x, boxes[z]->area.y, boxes[z]->area.w, boxes[z]->area.h); + Region newR; + newR.x = boxes[z]->area.x2() + 1; + newR.y = top; + newR.w = r.x2() - newR.x + 1; + newR.h = btm - top + 1; + boxSplit(newR, z + direction, end, direction, rl); + + if (direction == -1) + { + Region newR2; + newR2.x = r.x; + newR2.y = r.y; + newR2.w = r.w - newR.w; + newR2.h = r.h; + boxSplit(newR2, z, end, -1, rl); + return; + } + } + + if (direction == -1) + { + // we are going down the stack + // r is underlapped by boxes[z] + // but we have not split + // Therefore this region under test is + // completely covering boxes[z] + + // don't go any further down, generate a region and quit + +// printf("Repaint region: %i %i %i %i\n", r.x, r.y, r.w, r.h); + r.z = z; + rl.push_front(r); + } + +// printf("Returning from Z=%i\n", z); + return; + } + else + { +// printf("Z=%i S=%i E=%i D=%i: %i does not overlap\n", z, start, end, direction, z); + } + } + + // if direction = 1 then we have come to a region that is + // entirely clear of higher boxes and needs to be redrawn + + // if direction = -1 then we have come to a region that is on + // the very bottom with nothing below it to repaint. + // do nothing. stale window data will be left on screen? + + if (direction == 1) + { + rl.push_front(r); + } +} + +///////////////////////////////////////////////////////////////////////////// +// END NEW STUFF +///////////////////////////////////////////////////////////////////////////// + +// ---------------------------------------------------- END OF REMOVE CODE + + +void BoxStack::removeAll() +{ + // 1.. Don't delete wallpaper. No point. + + // Need locking on this one?? + + // This is pretty silly now that preDelete needs mutex unlocked + + Boxx* toDel = NULL; + + while(numBoxes > 1) + { + #ifndef WIN32 + pthread_mutex_lock(&boxLock); + #else + WaitForSingleObject(boxLock, INFINITE); + #endif + + if (numBoxes == 1) + { + #ifndef WIN32 + pthread_mutex_unlock(&boxLock); + #else + ReleaseMutex(boxLock); + #endif + break; + } + + toDel = boxes[numBoxes - 1]; + + #ifndef WIN32 + pthread_mutex_unlock(&boxLock); + #else + ReleaseMutex(boxLock); + #endif + + toDel->preDelete(); + + #ifndef WIN32 + pthread_mutex_lock(&boxLock); + #else + WaitForSingleObject(boxLock, INFINITE); + #endif + + // If boxes[numBoxes - 1] isn't toDel then there's a problem + if (boxes[numBoxes - 1] == toDel) + { + --numBoxes; + } + else + { + Log::getInstance()->log("BoxStack", Log::ERR, "Can this actually happen? Why?"); + toDel = NULL; + } + + #ifndef WIN32 + pthread_mutex_unlock(&boxLock); + #else + ReleaseMutex(boxLock); + #endif + + //AVO: do the delete outside the lock to allow for recursive deletes + Log::getInstance()->log("BoxStack", Log::DEBUG, "going to delete boxx %p, num=%d", toDel, numBoxes); + if (toDel) delete toDel; + } +} + +int BoxStack::handleCommand(int command) +{ + int retVal; + int retVal2 = 0; + int i; + + if (command != Remote::NA_NONE) + { + // handle command return values + // 0 - drop through to next box + // 1 - dont drop to next box, but not handled + // 2 - handled - stop command here + // 4 - handled - delete this box + + for (i=numBoxes-1; i>=0; i--) + { +// Log::getInstance()->log("BoxStack", Log::DEBUG, "Giving command to i=%i", i); + retVal = boxes[i]->handleCommand(command); + if (retVal == 1) + { + // not handled but don't give to any more boxes + return 0; + } + + if (retVal == 2) + { + // command handled + retVal2 = 1; + break; + } + else if (retVal == 4) + { +// Log::getInstance()->log("BoxStack", Log::DEBUG, "Return 4: i=%i, boxes[i]=%p", i, boxes[i]); + remove(boxes[i]); + retVal2 = 1; + break; + } + } + } + else + { + // fake the return code + retVal2 = 2; + } + + return retVal2; +} + +void BoxStack::processMessage(Message* m) +{ + if (m->to != this) + { + for (int i = numBoxes-1; i >= 0; i--) + { + if (boxes[i] == m->to) + { + Log::getInstance()->log("BoxStack", Log::DEBUG, "sending message from box %p to box %p %lu", m->from, m->to, m->message); + boxes[i]->processMessage(m); + return; + } + } + return; + } + + /* Handle mouse events*/ + // They come in with m->to = NULL? and just need to be delivered to top box? + if ((numBoxes > 1) && ((m->message == Message::MOUSE_MOVE) || (m->message == Message::MOUSE_LBDOWN) + || (m->message == Message::MOUSE_ANDROID_SCROLL))) + { + boxes[numBoxes-1]->processMessage(m); + return; + } + + Log::getInstance()->log("BoxStack", Log::DEBUG, "it's for meeee!"); + + switch(m->message) + { + case Message::CLOSE_ME: + { + remove((Boxx*)m->from); + break; + } + case Message::ADD_VIEW: // currently not used by anything but it might come in useful again + { + Boxx* toAdd = (Boxx*)m->parameter; + add(toAdd); + toAdd->draw(); + update(toAdd); + break; + } + } +} diff --git a/boxx.cc b/boxx.cc index 0bc3570..1705370 100644 --- a/boxx.cc +++ b/boxx.cc @@ -1,412 +1,439 @@ -/* - Copyright 2007 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 "boxx.h" -#include "bitmap.h" -#include "log.h" -#include - -char Boxx::numBoxxes = 0; - -Boxx::Boxx() -{ - // I want a parent box or a surface. - parent = NULL; - surface = NULL; - - area.x = 0; - area.y = 0; - area.w = 0; - area.h = 0; - - paraVSpace = 6; // default gap for drawPara - - backgroundColourSet = false; - visible = true; - - numBoxxes++; - Log::getInstance()->log("Boxx", Log::DEBUG, "Construct, now %u", numBoxxes); -} - -Boxx::~Boxx() -{ - if (surface) delete surface; - numBoxxes--; - Log::getInstance()->log("Boxx", Log::DEBUG, "Destruct, now %u", numBoxxes); -} - -void Boxx::draw() -{ - //Log::getInstance()->log("Boxx", Log::DEBUG, "Draw this %p surface %p", this, surface); - if (backgroundColourSet) fillColour(backgroundColour); - - Boxx* currentBoxx; - vector::iterator j; - for (j = children.begin(); j != children.end(); j++) - { - currentBoxx = *j; - if (currentBoxx->getVisible()) currentBoxx->draw(); - } -} - -void Boxx::setSize(UINT w, UINT h) -{ - area.w = w; - area.h = h; -} - -void Boxx::setPosition(UINT x, UINT y) -{ - area.x = x; - area.y = y; -} - -void Boxx::createBuffer() -{ - surface = new Surface_TYPE(); - surface->create(area.w, area.h); -} - -void Boxx::add(Boxx* newChild) -{ - newChild->setParent(this); - children.push_back(newChild); -} - -void Boxx::remove(Boxx* oldChild) -{ - for(vector::iterator i = children.begin(); i != children.end(); i++) - { - if (*i == oldChild) - { - children.erase(i); - return; - } - } - Log::getInstance()->log("Boxx", Log::ERR, "Remove child box called, child %p not found", oldChild); -} - -void Boxx::setParent(Boxx* newParent) -{ - parent = newParent; -} - -void Boxx::setBackgroundColour(const Colour& Tcolour) -{ - backgroundColour = Tcolour; - backgroundColourSet = true; -} - -void Boxx::setVisible(bool isVisible) -{ - visible = isVisible; -} - -bool Boxx::getVisible() -{ - return visible; -} - -void Boxx::setGap(UINT gap) -{ - paraVSpace = gap; -} - -void Boxx::blt(Region& r) -{ - /* surface update to screen needs: - source x distance into this surface - source y distance into this surface - width of update - height of update - destination x on screen - destination y on screen - */ - - if (parent) abort(); // if (parent) then this is a child boxx. It can not blt. - - // this shouldn't be here - r.x -= area.x; - r.y -= area.y; - - surface->updateToScreen(r.x, r.y, r.w, r.h, area.x + r.x, area.y + r.y); - -} - -int Boxx::getScreenX() -{ - if (parent) return area.x + parent->getScreenX(); - return area.x; -} - -int Boxx::getScreenY() -{ - if (parent) return area.y + parent->getScreenY(); - return area.y; -} - -int Boxx::getRootBoxOffsetX() // convert this to be getX and silently do the parent/not thing? same for Y below? -{ - if (parent) return area.x + parent->getRootBoxOffsetX(); - return 0; -} - -int Boxx::getRootBoxOffsetY() -{ - if (parent) return area.y + parent->getRootBoxOffsetY(); - return 0; -} - -int Boxx::getX() -{ - return area.x; -} - -int Boxx::getY() -{ - return area.y; -} - -int Boxx::getX2() -{ - return area.x + area.w; -} - -int Boxx::getY2() -{ - return area.y + area.h; -} - -UINT Boxx::getWidth() -{ - return area.w; -} - -UINT Boxx::getHeight() -{ - return area.h; -} - -// FIXME Clean up the code to use just one of the following - -Region* Boxx::getRegion() -{ - return &area; -} - -Region Boxx::getRegionR() -{ - return area; -} - -void Boxx::getRootBoxRegion(Region* r) -{ - // Returns a region that describes the position of this box on the box with the surface - // To be used for boxstack->update calls - - r->x = getRootBoxOffsetX(); - r->y = getRootBoxOffsetY(); - r->w = area.w; - r->h = area.h; -} - -// Level 1 drawing functions - -void Boxx::fillColour(const Colour& colour) -{ - rectangle(0, 0, area.w, area.h, colour); -} - -void Boxx::drawPara(const char* text, int x, int y, const Colour& colour) -{ - char line[256]; - int lineHeight = surface->getFontHeight() + paraVSpace; - - int lineWidth; - int thisCharWidth; - int textPos; - int linePos; - int ypos; - int printLine; - - textPos = 0; - ypos = y; - - while(1) - { - linePos = 0; - lineWidth = 0; - while(1) - { - printLine = 0; - - if (text[textPos] == '\0') break; - - if (text[textPos] == '\n') - { - textPos++; // ignore the \n - printLine = 1; - break; - } - - thisCharWidth = surface->getCharWidth(text[textPos]); - if ((lineWidth + thisCharWidth) > (int)(area.w - (2 * paraMargin))) - { - // this character would break the right margin - if (text[textPos] == ' ') - { - // this char is a space, ignore and break - textPos++; - break; - } - else - { - // Need to go back to the last space in the line - while ((text[textPos] != ' ') && (linePos >= 0)) - { - textPos--; - linePos--; - } - // Now take the space we just found - textPos++; - break; - } - } - line[linePos++] = text[textPos]; - lineWidth += thisCharWidth; - textPos++; - } - -// line[linePos++] = '\0'; - if (linePos>=0) line[linePos++] = '\0'; //Here is the change - - if (printLine || (linePos > 1)) // if some text was put in line - { - drawText(line, x, ypos, colour); - ypos += lineHeight; - if (ypos > (int)(area.h - lineHeight)) break; - } - else - { - break; - } - } -} - -void Boxx::rectangle(Region& region, const Colour& colour) -{ - rectangle(region.x, region.y, region.w, region.h, colour); -} - -// Level 0 drawing functions - -void Boxx::rectangle(UINT x, UINT y, UINT w, UINT h, const Colour& colour) -{ - if (parent) parent->rectangle(area.x + x, area.y + y, w, h, colour); - else surface->fillblt(x, y, w, h, colour.rgba()); -} - -void Boxx::drawText(const char* text, int x, int y, const Colour& colour) -{ - if (parent) parent->drawText(text, area.x + x, area.y + y, colour); - else surface->drawText(text, x, y, colour.rgba()); -} - -void Boxx::drawText(const char* text, int x, int y, int width, const Colour& colour) -{ - if (parent) parent->drawText(text, area.x + x, area.y + y, width, colour); - else surface->drawText(text, x, y, width, colour.rgba()); -} - -void Boxx::drawTextRJ(const char* text, int x, int y, const Colour& colour) -{ - if (parent) parent->drawTextRJ(text, area.x + x, area.y + y, colour); - else surface->drawTextRJ(text, x, y, colour.rgba()); -} - -void Boxx::drawTextCentre(const char* text, int x, int y, const Colour& colour) -{ - if (parent) parent->drawTextCentre(text, area.x + x, area.y + y, colour); - else surface->drawTextCentre(text, x, y, colour.rgba()); -} - -void Boxx::drawPixelAlpha(UINT x, UINT y, const Colour& colour,bool fastdraw) -{ - if (parent) parent->drawPixelAlpha(area.x + x, area.y + y, colour,fastdraw); - else - { - int c = ( (colour.alpha << 24 ) - | (colour.red << 16) - | (colour.green << 8) - | (colour.blue ) ); - - surface->drawPixel(x, y, c,fastdraw); - } -} - -void Boxx::drawPixel(UINT x, UINT y, const Colour& colour, bool fastdraw) -{ - if (parent) parent->drawPixel(area.x + x, area.y + y, colour,fastdraw); - else - { - int c = ( (0xFF000000 ) - | (colour.red << 16) - | (colour.green << 8) - | (colour.blue ) ); - - surface->drawPixel(x, y, c,fastdraw); - } -} -void Boxx::drawBitmap(UINT x, UINT y, const Bitmap& bm) -{ - if (parent) parent->drawBitmap(area.x + x, area.y + y, bm); - else surface->drawBitmap(x, y, bm); -} - -void Boxx::startFastDraw() -{ - if (parent) parent->startFastDraw(); - else - { - surface->startFastDraw(); - } -} - -void Boxx::endFastDraw() -{ - if (parent) parent->endFastDraw(); - else - { - surface->endFastDraw(); - } -} - - -int Boxx::charWidth(char c) -{ - if (parent) return parent->charWidth(c); - else return surface->getCharWidth(c); -} - -Surface * Boxx::getSurface() { - if (parent) return parent->getSurface(); - return surface; -} - +/* + Copyright 2007 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 "boxx.h" +#include "bitmap.h" +#include "log.h" +#include "osd.h" +#include + +char Boxx::numBoxxes = 0; + +Boxx::Boxx() +{ + // I want a parent box or a surface. + parent = NULL; + surface = NULL; + + area.x = 0; + area.y = 0; + area.w = 0; + area.h = 0; + + paraVSpace = 6; // default gap for drawPara + + backgroundColourSet = false; + visible = true; + + numBoxxes++; + Log::getInstance()->log("Boxx", Log::DEBUG, "Construct, now %u", numBoxxes); +} + +Boxx::~Boxx() +{ + if (surface) delete surface; + numBoxxes--; + Log::getInstance()->log("Boxx", Log::DEBUG, "Destruct, now %u", numBoxxes); +} + +void Boxx::draw() +{ + //Log::getInstance()->log("Boxx", Log::DEBUG, "Draw this %p surface %p", this, surface); + if (backgroundColourSet) fillColour(backgroundColour); + + Boxx* currentBoxx; + vector::iterator j; + //int count=0; + for (j = children.begin(); j != children.end(); j++) + { + currentBoxx = *j; + // Log::getInstance()->log("Boxx", Log::DEBUG, "Draw child %d %d", count,currentBoxx); + if (currentBoxx->getVisible()) currentBoxx->draw(); + // count++; + } + // Log::getInstance()->log("Boxx", Log::DEBUG, "Draw this %p surface %p End", this, surface); +} + +void Boxx::setSize(UINT w, UINT h) +{ + area.w = w; + area.h = h; +} + +void Boxx::setPosition(UINT x, UINT y) +{ + area.x = x; + area.y = y; +} + +void Boxx::createBuffer() +{ + surface = Osd::getInstance()->createNewSurface(); + surface->create(area.w, area.h); +} + +void Boxx::add(Boxx* newChild) +{ + newChild->setParent(this); + children.push_back(newChild); +} + +void Boxx::remove(Boxx* oldChild) +{ + for(vector::iterator i = children.begin(); i != children.end(); i++) + { + if (*i == oldChild) + { + children.erase(i); + return; + } + } + Log::getInstance()->log("Boxx", Log::ERR, "Remove child box called, child %p not found", oldChild); +} + +void Boxx::setParent(Boxx* newParent) +{ + parent = newParent; +} + +void Boxx::setBackgroundColour(const Colour& Tcolour) +{ + backgroundColour = Tcolour; + backgroundColourSet = true; +} + +void Boxx::setVisible(bool isVisible) +{ + visible = isVisible; +} + +bool Boxx::getVisible() +{ + return visible; +} + +void Boxx::setGap(UINT gap) +{ + paraVSpace = gap; +} + +void Boxx::blt(Region& r) +{ + /* surface update to screen needs: + source x distance into this surface + source y distance into this surface + width of update + height of update + destination x on screen + destination y on screen + */ + + if (parent) abort(); // if (parent) then this is a child boxx. It can not blt. + + // this shouldn't be here + r.x -= area.x; + r.y -= area.y; + + surface->updateToScreen(r.x, r.y, r.w, r.h, area.x + r.x, area.y + r.y); + +} + +int Boxx::getScreenX() +{ + if (parent) return area.x + parent->getScreenX(); + return area.x; +} + +int Boxx::getScreenY() +{ + if (parent) return area.y + parent->getScreenY(); + return area.y; +} + +int Boxx::getRootBoxOffsetX() // convert this to be getX and silently do the parent/not thing? same for Y below? +{ + if (parent) return area.x + parent->getRootBoxOffsetX(); + return 0; +} + +int Boxx::getRootBoxOffsetY() +{ + if (parent) return area.y + parent->getRootBoxOffsetY(); + return 0; +} + +int Boxx::getX() +{ + return area.x; +} + +int Boxx::getY() +{ + return area.y; +} + +int Boxx::getX2() +{ + return area.x + area.w; +} + +int Boxx::getY2() +{ + return area.y + area.h; +} + +UINT Boxx::getWidth() +{ + return area.w; +} + +UINT Boxx::getHeight() +{ + return area.h; +} + +// FIXME Clean up the code to use just one of the following + +Region* Boxx::getRegion() +{ + return &area; +} + +Region Boxx::getRegionR() +{ + return area; +} + +void Boxx::getRootBoxRegion(Region* r) +{ + // Returns a region that describes the position of this box on the box with the surface + // To be used for boxstack->update calls + + r->x = getRootBoxOffsetX(); + r->y = getRootBoxOffsetY(); + r->w = area.w; + r->h = area.h; +} + +// Level 1 drawing functions + +void Boxx::fillColour(const Colour& colour) +{ + rectangle(0, 0, area.w, area.h, colour); +} + +void Boxx::drawPara(const char* text, int x, int y, const Colour& colour) +{ + char line[256]; + int lineHeight = getFontHeight() + paraVSpace; + + int lineWidth; + int thisCharWidth; + int textPos; + int linePos; + int ypos; + int printLine; + + textPos = 0; + ypos = y; + + while(1) + { + linePos = 0; + lineWidth = 0; + while(1) + { + printLine = 0; + + if (text[textPos] == '\0') break; + + if (text[textPos] == '\n') + { + textPos++; // ignore the \n + printLine = 1; + break; + } + + thisCharWidth = charWidth(text[textPos]); + if ((lineWidth + thisCharWidth) > (int)(area.w - (2 * paraMargin))) + { + // this character would break the right margin + if (text[textPos] == ' ') + { + // this char is a space, ignore and break + textPos++; + break; + } + else + { + // Need to go back to the last space in the line + while ((text[textPos] != ' ') && (linePos >= 0)) + { + textPos--; + linePos--; + } + // Now take the space we just found + textPos++; + break; + } + } + line[linePos++] = text[textPos]; + lineWidth += thisCharWidth; + textPos++; + } + +// line[linePos++] = '\0'; + if (linePos>=0) line[linePos++] = '\0'; //Here is the change + + if (printLine || (linePos > 1)) // if some text was put in line + { + drawText(line, x, ypos, colour); + ypos += lineHeight; + if (ypos > (int)(area.h - lineHeight)) break; + } + else + { + break; + } + } +} + +void Boxx::rectangle(Region& region, const Colour& colour) +{ + rectangle(region.x, region.y, region.w, region.h, colour); +} + +// Level 0 drawing functions + +void Boxx::rectangle(UINT x, UINT y, UINT w, UINT h, const Colour& colour) +{ + if (parent) parent->rectangle(area.x + x, area.y + y, w, h, colour); + else surface->fillblt(x, y, w, h, colour.rgba()); +} + +void Boxx::drawText(const char* text, int x, int y, const Colour& colour) +{ + if (parent) parent->drawText(text, area.x + x, area.y + y, colour); + else surface->drawText(text, x, y, colour.rgba()); +} + +void Boxx::drawText(const char* text, int x, int y, int width, const Colour& colour) +{ + if (parent) parent->drawText(text, area.x + x, area.y + y, width, colour); + else surface->drawText(text, x, y, width, colour.rgba()); +} + +void Boxx::drawTextRJ(const char* text, int x, int y, const Colour& colour) +{ + if (parent) parent->drawTextRJ(text, area.x + x, area.y + y, colour); + else surface->drawTextRJ(text, x, y, colour.rgba()); +} + +void Boxx::drawTextCentre(const char* text, int x, int y, const Colour& colour) +{ + if (parent) parent->drawTextCentre(text, area.x + x, area.y + y, colour); + else surface->drawTextCentre(text, x, y, colour.rgba()); +} + +void Boxx::drawPixelAlpha(UINT x, UINT y, const Colour& colour,bool fastdraw) +{ + if (parent) parent->drawPixelAlpha(area.x + x, area.y + y, colour,fastdraw); + else + { + int c = ( (colour.alpha << 24 ) + | (colour.red << 16) + | (colour.green << 8) + | (colour.blue ) ); + + surface->drawPixel(x, y, c,fastdraw); + } +} + +void Boxx::drawPixel(UINT x, UINT y, const Colour& colour, bool fastdraw) +{ + if (parent) parent->drawPixel(area.x + x, area.y + y, colour,fastdraw); + else + { + int c = ( (0xFF000000 ) + | (colour.red << 16) + | (colour.green << 8) + | (colour.blue ) ); + + surface->drawPixel(x, y, c,fastdraw); + } +} + +void Boxx::drawTTChar(int ox, int oy,int x, int y, cTeletextChar c) +{ + if (parent) parent->drawTTChar(area.x + ox, area.y + oy, x,y,c); + else if (surface) surface->drawTTChar(ox, oy,x,y,c); + +} + +void Boxx::drawBitmap(UINT x, UINT y, const Bitmap& bm) +{ + if (parent) parent->drawBitmap(area.x + x, area.y + y, bm); + else if (surface) surface->drawBitmap(x, y, bm); +} + +void Boxx::drawJpeg(const char *fileName,int x, int y,int *width, int *height) +{ + if (parent) parent->drawJpeg(fileName,area.x +x,area.y +y,width,height); + else if (surface) surface->drawJpeg(fileName,x,y,width,height); +} + +int Boxx::getFontHeight() +{ + if (parent) return parent->getFontHeight(); + else if (surface) return surface->getFontHeight(); + else return 18; +} + +void Boxx::startFastDraw() +{ + if (parent) parent->startFastDraw(); + else + { + if (surface) surface->startFastDraw(); + } +} + +void Boxx::endFastDraw() +{ + if (parent) parent->endFastDraw(); + else + { + if (surface) surface->endFastDraw(); + } +} + + +int Boxx::charWidth(char c) +{ + if (parent) return parent->charWidth(c); + else if (surface) return surface->getCharWidth(c); + else return 10; //? +} + +Surface * Boxx::getSurface() { + if (parent) return parent->getSurface(); + return surface; +} + diff --git a/boxx.h b/boxx.h index b38f0a5..5a4da51 100644 --- a/boxx.h +++ b/boxx.h @@ -1,189 +1,186 @@ -/* - 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 BOXX_H -#define BOXX_H - -#include -#include - -using namespace std; - -#include "colour.h" -#include "region.h" -#include "message.h" - -#ifdef WIN32 -#include "surfacewin.h" -#else - -#ifdef _MIPS_ARCH -#include "surfacedirectfb.h" -#else -#include "surfacemvp.h" -#endif - -#endif - -class Bitmap; - -class Boxx -{ - public: - Boxx(); - virtual ~Boxx(); - - virtual void setSize(UINT w, UINT h); // virtual? really? - void setPosition(UINT x, UINT y); // Set position on parent. Even numbers only!!! - void createBuffer(); // Make this a root view that goes in the BoxStack - virtual void draw(); - - - void setGap(UINT gap); - void setBackgroundColour(const Colour& colour); - void setVisible(bool isVisible); - - - // The following are supposed to be abstract functions - // However, it is useful to be able to make instances of Boxx - // Therefore the following stubs are provided. - virtual void preDelete() {} - virtual int handleCommand(int x) { return 0; } - virtual void processMessage(Message* m) {} - virtual bool mouseMove(int x, int y) { return false; } - virtual bool mouseLBDOWN(int x, int y) { return false; } - virtual void deactivateAllControls() {} - - /* preDelete - - I think it's functionally equivalent to e.g. delete timers in Boxx::preDelete - because the only place where a Boxx is deleted is in - BoxStack::remove. There is now a call in BoxStack::remove to Boxx::preDelete - The reason for this is to stop timercalls calling BoxStack::update at the - same time BoxStack::remove is locked trying to delete the Boxx - */ - - // Get functions - int getScreenX(); // where is it on screen - int getScreenY(); - int getRootBoxOffsetX(); // where is it relative to the top-parent in the boxstack - int getRootBoxOffsetY(); - int getX(); // where is it relative to its parent - int getX2(); // .. and the right edge - int getY(); - int getY2(); - UINT getWidth(); - UINT getHeight(); - bool getVisible(); - Region* getRegion(); // Not to be used for changing the region - Region getRegionR(); // Same but as an object - void getRootBoxRegion(Region*); - - // Drawing functions level 1 - void fillColour(const Colour& colour); - void drawPara(const char* text, int x, int y, const Colour& colour); - - // Drawing functions level 0 - void rectangle(UINT x, UINT y, UINT w, UINT h, const Colour& colour); - void rectangle(Region& region, const Colour& colour); - - void drawText(const char* text, int x, int y, const Colour& colour); - void drawText(const char* text, int x, int y, int width, const Colour& colour); - void drawTextRJ(const char* text, int x, int y, const Colour& colour); - void drawTextCentre(const char* text, int x, int y, const Colour& colour); - void drawPixel(UINT x, UINT y, const Colour& colour, bool fastdraw=false); - void drawBitmap(UINT x, UINT y, const Bitmap& bm); - void drawPixelAlpha(UINT x, UINT y, const Colour& colour,bool fastdraw=false); - - /* This is for system which need a locking of the drawing surface to speed up drawing */ - void startFastDraw(); - void endFastDraw(); - - int charWidth(char c); - - void add(Boxx*); // a boxx has a set of child boxxs - void remove(Boxx*); - - /* - The following function sets the child's parent pointer without adding the child to the children vector. - It's a hack (that should be deprecated?) to allow things like WSymbol to be created, do some drawing on - the surface and then be deleted again, leaving the drawing present. - A better design would be to create many WSymbols - one per symbol and leave them created - then the - automatic draw code will be able to redraw the symbols without all that code needing - to be in the derived boxx's draw method. - */ - void TEMPADD(Boxx* child) { child->setParent(this); } - - friend class BoxStack; - protected: - //get the surface this box is drawing to - Surface *getSurface(); - Boxx* parent; - Region area; - vector children; - - void setParent(Boxx*); - void blt(Region& r); - - static const int paraMargin = 10; - UINT paraVSpace; - - Colour backgroundColour; - bool backgroundColourSet; - bool visible; - - static char numBoxxes; - Surface* surface; -}; - -#endif - - -/* - -New Boxx design - -It's "Boxx" because "Box" was already taken. - -BoxStack replaces Viewman and handles displaying a stack of root boxxs (boxxs with surfaces) on the screen. - -Boxx relaces Box, Widget and parts of View and represents a box with or without a surface. -Let's call a Boxx with a surface a root box, and a boxx without a surface a child box. - -A boxx with a surface (root) is like an old View and is handled by BoxStack. -A boxx without a surface (child) is like and old Widget and needs to be a child box of another boxx (root or not). - -Don't add a boxx with a surface to another boxx. That isn't the design. - -So, when you create a boxx, either that boxx calls createBuffer() on itself, -or some other box add()s the new box to itself. - -Root boxxs can overlap each other - this is managed by boxstack. -Child boxxs within a parent boxx do not overlap each other. -However, a grandchild box can overlap a child box (but must be entirely contained within the child box). - -TBBoxx replaces View but is now only a convenience class for the window dressing stuff. It isn't required anymore since -all the real work that view used to do is done in Boxx now. - -Obseleted classes: Box, View, Viewman, Widget, others? -No code outside boxx should talk about surfaces anymore. Hopefully. - -*/ - +/* + 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 BOXX_H +#define BOXX_H + +#include +#include + +using namespace std; + +#include "colour.h" +#include "region.h" +#include "message.h" + + +#include "surface.h" + +class Bitmap; + +class Boxx +{ + public: + Boxx(); + virtual ~Boxx(); + + virtual void setSize(UINT w, UINT h); // virtual? really? + void setPosition(UINT x, UINT y); // Set position on parent. Even numbers only!!! + void createBuffer(); // Make this a root view that goes in the BoxStack + virtual void draw(); + + + void setGap(UINT gap); + void setBackgroundColour(const Colour& colour); + void setVisible(bool isVisible); + + + // The following are supposed to be abstract functions + // However, it is useful to be able to make instances of Boxx + // Therefore the following stubs are provided. + virtual void preDelete() {} + virtual int handleCommand(int x) { return 0; } + virtual void processMessage(Message* m) {} + virtual bool mouseMove(int x, int y) { return false; } + virtual bool mouseLBDOWN(int x, int y) { return false; } + virtual bool mouseAndroidScroll(int x, int y,int sx, int sy) { return false; } + virtual void deactivateAllControls() {} + + /* preDelete + + I think it's functionally equivalent to e.g. delete timers in Boxx::preDelete + because the only place where a Boxx is deleted is in + BoxStack::remove. There is now a call in BoxStack::remove to Boxx::preDelete + The reason for this is to stop timercalls calling BoxStack::update at the + same time BoxStack::remove is locked trying to delete the Boxx + */ + + // Get functions + int getScreenX(); // where is it on screen + int getScreenY(); + int getRootBoxOffsetX(); // where is it relative to the top-parent in the boxstack + int getRootBoxOffsetY(); + int getX(); // where is it relative to its parent + int getX2(); // .. and the right edge + int getY(); + int getY2(); + UINT getWidth(); + UINT getHeight(); + bool getVisible(); + Region* getRegion(); // Not to be used for changing the region + Region getRegionR(); // Same but as an object + void getRootBoxRegion(Region*); + + // Drawing functions level 1 + void fillColour(const Colour& colour); + void drawPara(const char* text, int x, int y, const Colour& colour); + + // Drawing functions level 0 + void rectangle(UINT x, UINT y, UINT w, UINT h, const Colour& colour); + void rectangle(Region& region, const Colour& colour); + + void drawText(const char* text, int x, int y, const Colour& colour); + void drawText(const char* text, int x, int y, int width, const Colour& colour); + void drawTextRJ(const char* text, int x, int y, const Colour& colour); + void drawTextCentre(const char* text, int x, int y, const Colour& colour); + void drawPixel(UINT x, UINT y, const Colour& colour, bool fastdraw=false); + void drawBitmap(UINT x, UINT y, const Bitmap& bm); + void drawPixelAlpha(UINT x, UINT y, const Colour& colour,bool fastdraw=false); + int getFontHeight(); + + void drawJpeg(const char *fileName,int x, int y,int *width, int *height); + + void drawTTChar(int ox, int oy,int x, int y, cTeletextChar c); + + /* This is for system which need a locking of the drawing surface to speed up drawing */ + void startFastDraw(); + void endFastDraw(); + + int charWidth(char c); + + void add(Boxx*); // a boxx has a set of child boxxs + void remove(Boxx*); + + /* + The following function sets the child's parent pointer without adding the child to the children vector. + It's a hack (that should be deprecated?) to allow things like WSymbol to be created, do some drawing on + the surface and then be deleted again, leaving the drawing present. + A better design would be to create many WSymbols - one per symbol and leave them created - then the + automatic draw code will be able to redraw the symbols without all that code needing + to be in the derived boxx's draw method. + */ + void TEMPADD(Boxx* child) { child->setParent(this); } + + friend class BoxStack; + protected: + //get the surface this box is drawing to + Surface *getSurface(); + Boxx* parent; + Region area; + vector children; + + void setParent(Boxx*); + void blt(Region& r); + + static const int paraMargin = 10; + UINT paraVSpace; + + Colour backgroundColour; + bool backgroundColourSet; + bool visible; + + static char numBoxxes; + Surface* surface; +}; + +#endif + + +/* + +New Boxx design + +It's "Boxx" because "Box" was already taken. + +BoxStack replaces Viewman and handles displaying a stack of root boxxs (boxxs with surfaces) on the screen. + +Boxx relaces Box, Widget and parts of View and represents a box with or without a surface. +Let's call a Boxx with a surface a root box, and a boxx without a surface a child box. + +A boxx with a surface (root) is like an old View and is handled by BoxStack. +A boxx without a surface (child) is like and old Widget and needs to be a child box of another boxx (root or not). + +Don't add a boxx with a surface to another boxx. That isn't the design. + +So, when you create a boxx, either that boxx calls createBuffer() on itself, +or some other box add()s the new box to itself. + +Root boxxs can overlap each other - this is managed by boxstack. +Child boxxs within a parent boxx do not overlap each other. +However, a grandchild box can overlap a child box (but must be entirely contained within the child box). + +TBBoxx replaces View but is now only a convenience class for the window dressing stuff. It isn't required anymore since +all the real work that view used to do is done in Boxx now. + +Obseleted classes: Box, View, Viewman, Widget, others? +No code outside boxx should talk about surfaces anymore. Hopefully. + +*/ + diff --git a/command.cc b/command.cc index d2b222e..c7ce89c 100644 --- a/command.cc +++ b/command.cc @@ -1,944 +1,978 @@ -/* - 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 WIN32 -#include -#endif - -#include "command.h" - -#ifdef WIN32 -#include "remotewin.h" -#endif - -#include "led.h" -#include "video.h" -#include "audio.h" -#include "vdr.h" -#include "vvolume.h" -#include "vserverselect.h" -#include "vwelcome.h" -#include "vmute.h" -#include "colour.h" -#include "osd.h" -#include "i18n.h" -#include "timerreceiver.h" -#include "timers.h" -#include "wol.h" -#include "vconnect.h" -#include "message.h" -#include "remote.h" -#include "vinfo.h" -#include "boxx.h" -#include "boxstack.h" -#include "log.h" -#include "vsleeptimer.h" - - -Command* Command::instance = NULL; - -Command::Command() -{ - if (instance) return; - instance = this; - initted = 0; - isStandby = 0; - firstBoot = 1; - connLost = NULL; - crashed = false; - server = NULL; -} - -Command::~Command() -{ - instance = NULL; -} - -Command* Command::getInstance() -{ - return instance; -} - -int Command::init(bool tcrashed, char* tServer) -{ - if (initted) return 0; - initted = 1; - crashed = tcrashed; - server = tServer; - - logger = Log::getInstance(); - boxstack = BoxStack::getInstance(); - remote = Remote::getInstance(); - - remote->InitHWCListwithDefaults(); - - if (!logger || !boxstack || !remote) - { - initted = 0; - return 0; - } -#ifndef WIN32 - pthread_mutex_init(&masterLock, NULL); -#else - masterLock=CreateMutex(NULL,FALSE,NULL); -#endif - - return 1; -} - -int Command::shutdown() -{ - if (!initted) return 0; - initted = 0; - return 1; -} - -void Command::stop() -{ -// VDR::getInstance()->cancelFindingServer(); - logger->log("Command", Log::NOTICE, "Command stop1..."); - - udp.shutdown(); - logger->log("Command", Log::NOTICE, "Command stop2..."); - irun = 0; -} - -void Command::doWallpaper() -{ - Video* video = Video::getInstance(); - - // Blue background - Boxx* bbg = new Boxx(); - bbg->setSize(video->getScreenWidth(), video->getScreenHeight()); - bbg->createBuffer(); - bbg->fillColour(Colour::VIDEOBLUE); - boxstack->add(bbg); - boxstack->update(bbg); - boxstack->remove(bbg); - - // Wallpaper - WJpeg* wallpaperj = new WJpeg(); - wallpaperj->setSize(video->getScreenWidth(), video->getScreenHeight()); - wallpaperj->createBuffer(); - - if (video->getFormat() == Video::PAL) - { - logger->log("Command", Log::DEBUG, "PAL wallpaper selected"); -#ifndef _MIPS_ARCH - wallpaperj->init("/wallpaperPAL.jpg"); -#else - wallpaperj->init("wallpaperPAL.jpg"); -#endif - } - else - { - logger->log("Command", Log::DEBUG, "NTSC wallpaper selected"); - wallpaperj->init("/wallpaperNTSC.jpg"); - } - wallpaperj->draw(); - - boxstack->add(wallpaperj); - boxstack->update(wallpaperj); - - wallpaper = wallpaperj; -} - -void Command::run() -{ - if (!initted) return; - irun = 1; -#ifndef WIN32 - mainPid = getpid(); -#endif - - // just in case - Video::getInstance()->signalOn(); - Led::getInstance()->on(); - - doWallpaper(); - - // End of startup. Lock the mutex and put the first view up -// logger->log("Command", Log::DEBUG, "WANT LOCK"); -#ifndef WIN32 - pthread_mutex_lock(&masterLock); -#else - WaitForSingleObject(masterLock, INFINITE ); -#endif - //logger->log("Command", Log::DEBUG, "LOCKED"); - - if (crashed) - { - buildCrashedBox(); - } - else - { - VConnect* vconnect = new VConnect(server); - boxstack->add(vconnect); - vconnect->run(); - } - - // Start method 2 of getting commands in... - udp.run(this); - - UCHAR button = 0; - while(irun) - { - // unlock and wait - //logger->log("Command", Log::DEBUG, "UNLOCK"); -#ifndef WIN32 - pthread_mutex_unlock(&masterLock); -#else - ReleaseMutex(masterLock); -#endif - - button = remote->getButtonPress(2); // FIXME why is this set to 2 and not 0? so it can quit - // something happened, lock and process - - // logger->log("Command", Log::DEBUG, "WANT LOCK"); -#ifndef WIN32 - pthread_mutex_lock(&masterLock); -#else - WaitForSingleObject(masterLock, INFINITE ); -#endif - // logger->log("Command", Log::DEBUG, "LOCK"); - - if ((button == Remote::NA_NONE) /*|| (button == Remote::NA_UNKNOWN)*/) continue; - - if (button != Remote::NA_SIGNAL) handleCommand(button); - processMessageQueue(); - } - - //logger->log("Command", Log::DEBUG, "UNLOCK"); -#ifndef WIN32 - pthread_mutex_unlock(&masterLock); -#else - ReleaseMutex(masterLock); -#endif - -} - -void Command::postMessage(Message* m) -{ - // This is locked here in case the main loop is not waiting for an event, but is processing one - // it could be killed but then not react to it because the signal wouldn't cause - // remote->getButtonPress to break - // locking the mutex ensures that the master thread is waiting on getButtonPress - - - //logger->log("Command", Log::DEBUG, "WANT LOCK"); -#ifndef WIN32 - pthread_mutex_lock(&masterLock); -#else - WaitForSingleObject(masterLock, INFINITE ); -#endif - //logger->log("Command", Log::DEBUG, "LOCK"); - MessageQueue::postMessage(m); - -#ifndef WIN32 - kill(mainPid, SIGURG); - pthread_mutex_unlock(&masterLock); -#else - ((RemoteWin*)Remote::getInstance())->Signal(); - ReleaseMutex(masterLock); -#endif - //logger->log("Command", Log::DEBUG, "UNLOCK"); -} - -void Command::postMessageNoLock(Message* m) -{ - // As above but use this one if this message is being posted because of a button press - // the mutex is already locked, locking around postMessage is not needed as the - // queue is guaranteed to be run when the button has been processed - MessageQueue::postMessage(m); -} - -bool Command::postMessageIfNotBusy(Message* m) -{ - // Used for Windows mouse events - - //logger->log("Command", Log::DEBUG, "TRY LOCK"); -#ifndef WIN32 - if (pthread_mutex_trylock(&masterLock) != EBUSY) - { - //logger->log("Command", Log::DEBUG, "LOCK"); - MessageQueue::postMessage(m); - kill(mainPid, SIGURG); - pthread_mutex_unlock(&masterLock); - //logger->log("Command", Log::DEBUG, "UNLOCK"); - return true; - } - else - { - return false; - } -#else - switch (WaitForSingleObject(masterLock, 0 )) - { //FIXME this is not "if not busy" check - case WAIT_OBJECT_0: //but with proper argument 0 this did not work - // case WAIT_ABANDONED: - MessageQueue::postMessage(m); - ((RemoteWin*)Remote::getInstance())->Signal(); - ReleaseMutex(masterLock); - return true; - - case WAIT_ABANDONED: return false; - case WAIT_TIMEOUT: return false; - } - return false; -#endif -} - -void Command::postMessageFromOuterSpace(Message* m) -{ - /* - Yet another way of getting messages into Command. This one is for events that - are not standard button presses (or UDP generated buttons). It is also not for - events that are generated as a result of other events (events that can safely - call postMessageNoLock and be guaranteed that the message will be processed - because it is known that the queue is currently being processed). - This is for events that come from outer space and can occur when the master - mutex is locked or not, they need to be queued and executed but it doesn't - matter when. - Actually so far it is for events caused by the video stream - aspect ratio - changes. These can occur when the master mutex is locked and so postMessage - doesn't work. postMessageNoLock doesn't work because if the mutex *isn't* - locked at the time then the message could be sat around a while before - being noticed. - The whole message system was at first supposed to prevent the problem of - calling a function on an object that had just been deleted, by ordering - messages such that all calls are done before object deletion. However, - because of the new centralised messaging system and the fact that BoxStack - locates the destination object before calling it, the messaging system now - allows the kind of sloppy calls it was supposed to stop. Weird huh. This - is mentioned here because the video stream might generate an event just as - the user hits stop. The mutex is locked, and by the time the message - is examined the vvideorec/live has been deleted. This doesn't matter because - boxstack will drop the message if it can't find the matching object to - deliver it to. - Finally, all this is fine and dandy, except that I'm not 100% sure that - this sloppy postMessage and hope a queued signal will force it to be processed - thingy will actually work. Hmmm. - Lastly , I will consider making the naming system a little more sane - if this works. - */ - - logger->log("Command", Log::DEBUG, "PMFOS called"); - MessageQueue::postMessage(m); - -#ifndef WIN32 - kill(mainPid, SIGURG); -#else - ((RemoteWin*)Remote::getInstance())->Signal(); -#endif -} - -void Command::processMessage(Message* m) -{ - // FIXME - a slight modification - how if messagereceivers were to register - // themselves as receivers to avoid the calling-a-deleted-object problem - // then only deliver/register/unregister would have to be protected - - logger->log("Command", Log::DEBUG, "processing message %i", m->message); - - - if (m->to == this) - { - switch(m->message) - { - // << FIXME OBSELETE - case Message::STOP_PLAYBACK: - { - handleCommand(Remote::STOP); // an odd way of doing it, but so simple - break; - } - // Also connection_lost comes from player - anywhere else? - // FIXME OBSELETE >> - - - case Message::VDR_CONNECTED: - { - doJustConnected((VConnect*)m->from); - break; - } - case Message::SCREENSHOT: - { - Osd::getInstance()->screenShot("/out.jpg"); - break; - } - case Message::CONNECTION_LOST: - { - doFromTheTop(true); - break; - } - case Message::UDP_BUTTON: - { - handleCommand(m->parameter); - break; - } - case Message::CHANGE_LANGUAGE: - { - boxstack->removeAll(); - boxstack->update(wallpaper); - I18n::initialize(); - if (!VDR::getInstance()->isConnected()) { connectionLost(); break; } - VWelcome* vw = new VWelcome(); - vw->draw(); - boxstack->add(vw); - boxstack->update(vw); - break; - } - case Message::LAST_VIEW_CLOSE: - { - // Shouldn't be done like this. Some generic message pass back from vinfo perhaps - if (crashed) - { - crashed = false; - doFromTheTop(false); - } - -// VWelcome* vw = new VWelcome(); -// vw->draw(); -// boxstack->add(vw); -// boxstack->update(vw); - - break; - } - } - } - else - { - /* FIXME - - Instead of sending through the boxstack, implement a more generic MessageReceiver interface - and have potential receivers register with something - When a message needs to be delivered, check if the receiver is still registered, if so, deliver the message - This could all be done using the existing big command mutex to keep it simple - */ - - logger->log("Command", Log::DEBUG, "Sending message to boxstack"); - boxstack->processMessage(m); - } -} - -void Command::handleCommand(int button) -{ - if (isStandby && (button != Remote::POWER)) return; - if (!connLost && boxstack->handleCommand(button)) return; // don't send to boxstack if connLost - - // command was not handled - - switch(button) - { - case Remote::DF_LEFT: - case Remote::DF_RIGHT: - case Remote::VOLUMEUP: - case Remote::VOLUMEDOWN: - { - VVolume* v = new VVolume(); - boxstack->add(v); - v->handleCommand(button); // this will draw+show - return; - } - case Remote::MUTE: - { - VMute* v = new VMute(); - v->draw(); - boxstack->add(v); - boxstack->update(v); - return; - } - case Remote::POWER: - { - doStandby(); - return; - } - case Remote::OK: - { - // FIXME - if (!connLost) return; // if connLost, handle Remote::OK - doFromTheTop(false); - return; - } - case Remote::GO: - { - VSleeptimer* sleep = new VSleeptimer(); - boxstack->add(sleep); - sleep->handleCommand(button); // this will draw+show - return; - } - } -} - -void Command::sig1() -{ -#ifdef DEV - Message* m = new Message(); // break into master mutex - m->message = Message::SCREENSHOT; - m->to = this; - postMessage(m); -#endif -} - -void Command::doStandby() -{ - if (isStandby) - { - Video::getInstance()->signalOn(); - Led::getInstance()->on(); - isStandby = 0; - - - VConnect* vconnect = new VConnect(server); - boxstack->add(vconnect); - vconnect->run(); - } - else - { - boxstack->removeAll(); - Video::getInstance()->signalOff(); - boxstack->update(wallpaper); - - VDR::getInstance()->configSave("General", "Last Power State", "Off"); - logger->netLogOff(); - VDR::getInstance()->disconnect(); - Led::getInstance()->off(); - isStandby = 1; - Sleeptimer::getInstance()->shutdown(); -#ifdef WIN32 - stop(); //different behavoiur on windows, we exit -#endif - } -} - -void Command::doFromTheTop(bool which) -{ - if (which) - { - if (connLost) - { - logger->log("Command", Log::NOTICE, "Connection lost dialog already present"); - return; - } - - logger->log("Command", Log::NOTICE, "Doing connection lost dialog"); - connLost = new VInfo(); - connLost->setSize(360, 200); - connLost->createBuffer(); - if (Video::getInstance()->getFormat() == Video::PAL) - connLost->setPosition(190, 170); - else - connLost->setPosition(180, 120); - connLost->setOneLiner(tr("Connection lost")); - connLost->setDropThrough(); - connLost->setBorderOn(1); - connLost->setTitleBarColour(Colour::DANGER); - connLost->okButton(); - connLost->draw(); - boxstack->add(connLost); - boxstack->update(connLost); - remote->clearBuffer(); - } - else - { - logger->netLogOff(); - VDR::getInstance()->disconnect(); - boxstack->removeAll(); - boxstack->update(wallpaper); - connLost = NULL; - - flushMessageQueue(); - remote->clearBuffer(); - - // at this point, everything should be reset to first-go - - VConnect* vconnect = new VConnect(server); - boxstack->add(vconnect); - vconnect->run(); - } -} - -void Command::doReboot() -{ - logger->netLogOff(); - VDR::getInstance()->disconnect(); - // just kill it... - logger->log("Command", Log::NOTICE, "Reboot"); -#ifndef WIN32 -#ifndef _MIPS_ARCH - reboot(LINUX_REBOOT_CMD_RESTART); -#else - stop(); -#endif -#endif //Would we support this on windows? -} - -void Command::connectionLost() -{ - logger->netLogOff(); - Message* m = new Message(); // break into master mutex - m->message = Message::CONNECTION_LOST; - m->to = this; - postMessageFromOuterSpace(m); -} - -void Command::buildCrashedBox() -{ - VInfo* crash = new VInfo(); - crash->setSize(360, 250); - crash->createBuffer(); - if (Video::getInstance()->getFormat() == Video::PAL) - crash->setPosition(190, 146); - else - crash->setPosition(180, 96); - crash->setMainText("Oops, vomp crashed.. :(\nPlease report this crash to the author, with as much detail as possible about what you were doing at the time that might have caused the crash."); - crash->setBorderOn(1); - crash->setTitleBarColour(Colour::DANGER); - crash->okButton(); - crash->setExitable(); - crash->draw(); - boxstack->add(crash); - boxstack->update(crash); -} - -void Command::doJustConnected(VConnect* vconnect) -{ - I18n::initialize(); - if (!VDR::getInstance()->isConnected()) { connectionLost(); return; } - - Video* video = Video::getInstance(); - Audio* audio = Audio::getInstance(); - boxstack->remove(vconnect); - - VInfo* vi = new VInfo(); - vi->setSize(400, 200); - vi->createBuffer(); - if (video->getFormat() == Video::PAL) - vi->setPosition(170, 200); - else - vi->setPosition(160, 150); - vi->setOneLiner(tr("Connected, loading config")); - vi->draw(); - boxstack->add(vi); - boxstack->update(vi); - - VDR* vdr = VDR::getInstance(); - char* config; - - // See if we're supposed to do network logging - config = vdr->configLoad("Advanced", "Network logging"); - if (config && !STRCASECMP(config, "On")) - { - logger->log("Command", Log::INFO, "Turning on network logging"); - logger->netLogOn(); - } - else - { - logger->netLogOn(); - logger->log("Command", Log::INFO, "Turned off network logging"); - } - if (config) delete[] config; - - // See if config says to override video format (PAL/NTSC) - config = vdr->configLoad("General", "Override Video Format"); - if (config) - { - logger->log("Command", Log::DEBUG, "Override Video Format is present"); - - if ( (!strcmp(config, "PAL") && (video->getFormat() == Video::NTSC)) - || (!strcmp(config, "NTSC") && (video->getFormat() == Video::PAL)) ) - { - // Oh sheesh, need to switch format. Bye bye TV... - - // Take everything down - boxstack->removeAll(); - boxstack->remove(wallpaper); - Osd* osd = Osd::getInstance(); - osd->shutdown(); - video->shutdown(); - - // Get video and osd back up with the new mode - if (!strcmp(config, "PAL")) - { - logger->log("Command", Log::DEBUG, "Switching to PAL"); - video->init(Video::PAL); - } - else if (!strcmp(config, "NTSC")) - { - logger->log("Command", Log::DEBUG, "Switching to NTSC"); - video->init(Video::NTSC); - } - osd->init((char*)("/dev/stbgfx")); - - // Put the wallpaper back - doWallpaper(); - - // Re add the vinfo - vi = new VInfo(); - vi->setSize(400, 200); - vi->createBuffer(); - if (video->getFormat() == Video::PAL) - vi->setPosition(170, 200); - else - vi->setPosition(160, 150); - - vi->setOneLiner(tr("Connected, loading config")); - vi->draw(); - boxstack->add(vi); - boxstack->update(vi); - } - else - { - logger->log("Command", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'"); - } - } - else - { - logger->log("Command", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!"); - } - - // Power off if first boot and config says so - if (firstBoot) - { - firstBoot = 0; - - logger->log("Command", Log::DEBUG, "Load power after boot"); - - config = vdr->configLoad("General", "Power After Boot"); - - if (config) - { - if (!STRCASECMP(config, "On")) - { - logger->log("Command", Log::INFO, "Config says Power After Boot = On"); - } - else if (!STRCASECMP(config, "Off")) - { - logger->log("Command", Log::INFO, "Config says Power After Boot = Off"); - doStandby(); - delete[] config; - return; // quit here - } - else if (!STRCASECMP(config, "Last state")) - { - char* lastPowerState = vdr->configLoad("General", "Last Power State"); - if (lastPowerState) - { - if (!STRCASECMP(lastPowerState, "On")) - { - logger->log("Command", Log::INFO, "Config says Last Power State = On"); - } - else if (!STRCASECMP(lastPowerState, "Off")) - { - logger->log("Command", Log::INFO, "Config says Last Power State = Off"); - doStandby(); - delete[] config; - return; // quit here - } - else - { - logger->log("Command", Log::INFO, "Config General/Last Power State not understood"); - } - } - else - { - logger->log("Command", Log::INFO, "Config General/Last Power State not found"); - } - } - else - { - logger->log("Command", Log::INFO, "Config/Power After Boot not understood"); - } - delete[] config; - } - else - { - logger->log("Command", Log::INFO, "Config General/Power After Boot not found"); - } - } - - - // Go S-Video if config says so - - config = vdr->configLoad("TV", "Connection"); - - if (config) - { - if (!STRCASECMP(config, "S-Video")) - { - logger->log("Command", Log::INFO, "Switching to S-Video as Connection=%s", config); - video->setConnection(Video::SVIDEO); - } - else - { - logger->log("Command", Log::INFO, "Switching to RGB/Composite as Connection=%s", config); - video->setConnection(Video::COMPOSITERGB); - } - delete[] config; - } - else - { - logger->log("Command", Log::INFO, "Config TV/S-Video not found"); - } - - // Set remote type - - config = vdr->configLoad("General", "Remote type"); - - if (config) - { - if (!STRCASECMP(config, "New")) - { - logger->log("Command", Log::INFO, "Switching to New remote type"); - remote->setRemoteType(Remote::NEWREMOTE); - } - else - { - logger->log("Command", Log::INFO, "Switching to Old remote type"); - remote->setRemoteType(Remote::OLDREMOTE); - } - delete[] config; - } - else - { - logger->log("Command", Log::INFO, "Config General/Remote type not found"); - remote->setRemoteType(Remote::OLDREMOTE); - } - - - - - // Get TV aspect ratio - - config = vdr->configLoad("TV", "Aspect"); - if (config) - { - if (!STRCASECMP(config, "16:9")) - { - logger->log("Command", Log::INFO, "/// Switching to TV aspect 16:9"); - video->setTVsize(Video::ASPECT16X9); - } - else - { - logger->log("Command", Log::INFO, "/// Switching to TV aspect 4:3"); - video->setTVsize(Video::ASPECT4X3); - } - delete[] config; - } - else - { - logger->log("Command", Log::INFO, "Config TV/Aspect type not found, going 4:3"); - video->setTVsize(Video::ASPECT4X3); - } - - config = vdr->configLoad("TV", "Widemode"); - if (config) - { - if (!STRCASECMP(config, "Letterbox")) - { - logger->log("Command", Log::INFO, "Setting letterbox mode"); - video->setMode(Video::LETTERBOX); - } - else - { - logger->log("Command", Log::INFO, "Setting chop-sides mode"); - video->setMode(Video::NORMAL); - } - delete[] config; - } - else - { - logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode"); - video->setMode(Video::NORMAL); - } - - config = vdr->configLoad("Advanced", "TCP receive window"); - if (config) - { - size_t newTCPsize = atoi(config); - delete[] config; - - logger->log("Command", Log::INFO, "Setting TCP window size %i", newTCPsize); - vdr->setReceiveWindow(newTCPsize); - } - else - { - logger->log("Command", Log::INFO, "TCP window size not found, setting 2048"); - vdr->setReceiveWindow(2048); // Default - } - - config = vdr->configLoad("Advanced", "Disable WOL"); - if (config) - { - if (!STRCASECMP(config, "Yes")) - { - logger->log("Command", Log::INFO, "Config says disable WOL"); - Wol::getInstance()->setEnabled(false); - } - else - { - logger->log("Command", Log::INFO, "Config says enable WOL"); - Wol::getInstance()->setEnabled(true); - } - - delete[] config; - } - else - { - logger->log("Command", Log::INFO, "By default, enable WOL"); - Wol::getInstance()->setEnabled(true); - } - /* device dependend config */ - audio->loadOptionsfromServer(vdr); - video->loadOptionsfromServer(vdr); - remote->loadOptionsfromServer(vdr); - // config done - - // Save power state = on - - vdr->configSave("General", "Last Power State", "On"); - - // Make sure connection didn't die - if (!vdr->isConnected()) - { - Command::getInstance()->connectionLost(); - } - else - { - boxstack->remove(vi); - - VWelcome* vw = new VWelcome(); - vw->draw(); - boxstack->add(vw); - boxstack->update(vw); - - // Enter pre-keys here -// handleCommand(Remote::OK); -// handleCommand(Remote::THREE); -// handleCommand(Remote::SIX); -// handleCommand(Remote::OK); -// handleCommand(Remote::UP); -// handleCommand(Remote::PLAY); -// handleCommand(Remote::DOWN); -// handleCommand(Remote::DOWN); -// handleCommand(Remote::DOWN); - // handleCommand(Remote::OK); -// handleCommand(Remote::RED); - } -} +/* + 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 WIN32 +#include +#endif + +#include "command.h" + +#ifdef WIN32 +#include "remotewin.h" +#endif + +#ifdef __ANDROID__ +#include "remoteandroid.h" +#endif + +#include "led.h" +#include "video.h" +#include "audio.h" +#include "vdr.h" +#include "vvolume.h" +#include "vserverselect.h" +#include "vwelcome.h" +#include "vmute.h" +#include "colour.h" +#include "osd.h" +#include "i18n.h" +#include "timerreceiver.h" +#include "timers.h" +#include "wol.h" +#include "vconnect.h" +#include "message.h" +#include "remote.h" +#include "vinfo.h" +#include "boxx.h" +#include "boxstack.h" +#include "log.h" +#include "vsleeptimer.h" + + +Command* Command::instance = NULL; + +Command::Command() +{ + if (instance) return; + instance = this; + initted = 0; + isStandby = 0; + firstBoot = 1; + connLost = NULL; + crashed = false; + server = NULL; +} + +Command::~Command() +{ + instance = NULL; +} + +Command* Command::getInstance() +{ + return instance; +} + +int Command::init(bool tcrashed, char* tServer) +{ + if (initted) return 0; + initted = 1; + crashed = tcrashed; + server = tServer; + + logger = Log::getInstance(); + boxstack = BoxStack::getInstance(); + remote = Remote::getInstance(); + + remote->InitHWCListwithDefaults(); + + if (!logger || !boxstack || !remote) + { + initted = 0; + return 0; + } +#ifndef WIN32 + pthread_mutex_init(&masterLock, NULL); +#else + masterLock=CreateMutex(NULL,FALSE,NULL); +#endif + + return 1; +} + +int Command::shutdown() +{ + if (!initted) return 0; + initted = 0; + return 1; +} + +void Command::stop() +{ +// VDR::getInstance()->cancelFindingServer(); + logger->log("Command", Log::NOTICE, "Command stop1..."); + + udp.shutdown(); + logger->log("Command", Log::NOTICE, "Command stop2..."); + irun = 0; +} + +void Command::doWallpaper() +{ + Video* video = Video::getInstance(); + + // Blue background + Boxx* bbg = new Boxx(); + bbg->setSize(video->getScreenWidth(), video->getScreenHeight()); + bbg->createBuffer(); + bbg->fillColour(Colour::VIDEOBLUE); + boxstack->add(bbg); + boxstack->update(bbg); + boxstack->remove(bbg); + + // Wallpaper + WJpeg* wallpaperj = new WJpegTYPE(); + wallpaperj->setSize(video->getScreenWidth(), video->getScreenHeight()); + wallpaperj->createBuffer(); + + if (video->getFormat() == Video::PAL) + { + logger->log("Command", Log::DEBUG, "PAL wallpaper selected"); +#ifndef _MIPS_ARCH + wallpaperj->init("/wallpaperPAL.jpg"); +#else + wallpaperj->init("wallpaperPAL.jpg"); +#endif + } + else + { + logger->log("Command", Log::DEBUG, "NTSC wallpaper selected"); + wallpaperj->init("/wallpaperNTSC.jpg"); + } + wallpaperj->draw(); + + boxstack->add(wallpaperj); + boxstack->update(wallpaperj); + + wallpaper = wallpaperj; +} + +void Command::run() +{ + if (!initted) return; + irun = 1; +#ifndef WIN32 + mainPid = getpid(); +#endif + + // just in case + Video::getInstance()->signalOn(); + Led::getInstance()->on(); + + doWallpaper(); + + // End of startup. Lock the mutex and put the first view up +// logger->log("Command", Log::DEBUG, "WANT LOCK"); +#ifndef WIN32 + pthread_mutex_lock(&masterLock); +#else + WaitForSingleObject(masterLock, INFINITE ); +#endif + //logger->log("Command", Log::DEBUG, "LOCKED"); + + if (crashed) + { + buildCrashedBox(); + } + else + { + VConnect* vconnect = new VConnect(server); + boxstack->add(vconnect); + vconnect->run(); + } + + // Start method 2 of getting commands in... + udp.run(this); + + UCHAR button = 0; + while(irun) + { + // unlock and wait + //logger->log("Command", Log::DEBUG, "UNLOCK"); +#ifndef WIN32 + pthread_mutex_unlock(&masterLock); +#else + ReleaseMutex(masterLock); +#endif + + button = remote->getButtonPress(2); // FIXME why is this set to 2 and not 0? so it can quit + // something happened, lock and process + + // logger->log("Command", Log::DEBUG, "WANT LOCK"); +#ifndef WIN32 + pthread_mutex_lock(&masterLock); +#else + WaitForSingleObject(masterLock, INFINITE ); +#endif + // logger->log("Command", Log::DEBUG, "LOCK"); + + if ((button == Remote::NA_NONE) /*|| (button == Remote::NA_UNKNOWN)*/) continue; + + if (button != Remote::NA_SIGNAL) handleCommand(button); + processMessageQueue(); + + } + + //logger->log("Command", Log::DEBUG, "UNLOCK"); +#ifndef WIN32 + pthread_mutex_unlock(&masterLock); +#else + ReleaseMutex(masterLock); +#endif + + +} + +void Command::postMessage(Message* m) +{ + // This is locked here in case the main loop is not waiting for an event, but is processing one + // it could be killed but then not react to it because the signal wouldn't cause + // remote->getButtonPress to break + // locking the mutex ensures that the master thread is waiting on getButtonPress + + + //logger->log("Command", Log::DEBUG, "WANT LOCK"); +#ifndef WIN32 + pthread_mutex_lock(&masterLock); +#else + WaitForSingleObject(masterLock, INFINITE ); +#endif + //logger->log("Command", Log::DEBUG, "LOCK"); + MessageQueue::postMessage(m); + +#ifndef WIN32 +#ifndef __ANDROID__ + kill(mainPid, SIGURG); +#else + ((RemoteAndroid*)Remote::getInstance())->Signal(); +#endif + pthread_mutex_unlock(&masterLock); +#else + ((RemoteWin*)Remote::getInstance())->Signal(); + ReleaseMutex(masterLock); +#endif + //logger->log("Command", Log::DEBUG, "UNLOCK"); +} + +void Command::postMessageNoLock(Message* m) +{ + // As above but use this one if this message is being posted because of a button press + // the mutex is already locked, locking around postMessage is not needed as the + // queue is guaranteed to be run when the button has been processed + MessageQueue::postMessage(m); +} + +bool Command::postMessageIfNotBusy(Message* m) +{ + // Used for Windows mouse events + + //logger->log("Command", Log::DEBUG, "TRY LOCK"); +#ifndef WIN32 + if (pthread_mutex_trylock(&masterLock) != EBUSY) + { + //logger->log("Command", Log::DEBUG, "LOCK"); + MessageQueue::postMessage(m); +#ifndef __ANDROID__ + kill(mainPid, SIGURG); +#else + ((RemoteAndroid*)Remote::getInstance())->Signal(); +#endif + pthread_mutex_unlock(&masterLock); + //logger->log("Command", Log::DEBUG, "UNLOCK"); + return true; + } + else + { + return false; + } +#else + switch (WaitForSingleObject(masterLock, 0 )) + { //FIXME this is not "if not busy" check + case WAIT_OBJECT_0: //but with proper argument 0 this did not work + // case WAIT_ABANDONED: + MessageQueue::postMessage(m); + ((RemoteWin*)Remote::getInstance())->Signal(); + ReleaseMutex(masterLock); + return true; + + case WAIT_ABANDONED: return false; + case WAIT_TIMEOUT: return false; + } + return false; +#endif +} + +void Command::postMessageFromOuterSpace(Message* m) +{ + /* + Yet another way of getting messages into Command. This one is for events that + are not standard button presses (or UDP generated buttons). It is also not for + events that are generated as a result of other events (events that can safely + call postMessageNoLock and be guaranteed that the message will be processed + because it is known that the queue is currently being processed). + This is for events that come from outer space and can occur when the master + mutex is locked or not, they need to be queued and executed but it doesn't + matter when. + Actually so far it is for events caused by the video stream - aspect ratio + changes. These can occur when the master mutex is locked and so postMessage + doesn't work. postMessageNoLock doesn't work because if the mutex *isn't* + locked at the time then the message could be sat around a while before + being noticed. + The whole message system was at first supposed to prevent the problem of + calling a function on an object that had just been deleted, by ordering + messages such that all calls are done before object deletion. However, + because of the new centralised messaging system and the fact that BoxStack + locates the destination object before calling it, the messaging system now + allows the kind of sloppy calls it was supposed to stop. Weird huh. This + is mentioned here because the video stream might generate an event just as + the user hits stop. The mutex is locked, and by the time the message + is examined the vvideorec/live has been deleted. This doesn't matter because + boxstack will drop the message if it can't find the matching object to + deliver it to. + Finally, all this is fine and dandy, except that I'm not 100% sure that + this sloppy postMessage and hope a queued signal will force it to be processed + thingy will actually work. Hmmm. + Lastly , I will consider making the naming system a little more sane + if this works. + */ + + logger->log("Command", Log::DEBUG, "PMFOS called"); + MessageQueue::postMessage(m); + +#ifndef WIN32 +#ifndef __ANDROID__ + kill(mainPid, SIGURG); +#else + ((RemoteAndroid*)Remote::getInstance())->Signal(); +#endif +#else + ((RemoteWin*)Remote::getInstance())->Signal(); +#endif +} + +void Command::processMessage(Message* m) +{ + // FIXME - a slight modification - how if messagereceivers were to register + // themselves as receivers to avoid the calling-a-deleted-object problem + // then only deliver/register/unregister would have to be protected + + logger->log("Command", Log::DEBUG, "processing message %i", m->message); + + + if (m->to == this) + { + switch(m->message) + { + // << FIXME OBSELETE + case Message::STOP_PLAYBACK: + { + handleCommand(Remote::STOP); // an odd way of doing it, but so simple + break; + } + // Also connection_lost comes from player - anywhere else? + // FIXME OBSELETE >> + + + case Message::VDR_CONNECTED: + { + doJustConnected((VConnect*)m->from); + break; + } + case Message::SCREENSHOT: + { + Osd::getInstance()->screenShot("/out.jpg"); + break; + } + case Message::CONNECTION_LOST: + { + doFromTheTop(true); + break; + } + case Message::UDP_BUTTON: + { + handleCommand(m->parameter); + break; + } + case Message::CHANGE_LANGUAGE: + { + boxstack->removeAll(); + boxstack->update(wallpaper); + I18n::initialize(); + if (!VDR::getInstance()->isConnected()) { connectionLost(); break; } + VWelcome* vw = new VWelcome(); + vw->draw(); + boxstack->add(vw); + boxstack->update(vw); + break; + } + case Message::LAST_VIEW_CLOSE: + { + // Shouldn't be done like this. Some generic message pass back from vinfo perhaps + if (crashed) + { + crashed = false; + doFromTheTop(false); + } + +// VWelcome* vw = new VWelcome(); +// vw->draw(); +// boxstack->add(vw); +// boxstack->update(vw); + + break; + } + } + } + else + { + /* FIXME + + Instead of sending through the boxstack, implement a more generic MessageReceiver interface + and have potential receivers register with something + When a message needs to be delivered, check if the receiver is still registered, if so, deliver the message + This could all be done using the existing big command mutex to keep it simple + */ + + logger->log("Command", Log::DEBUG, "Sending message to boxstack"); + boxstack->processMessage(m); + } +} + +void Command::handleCommand(int button) +{ + if (isStandby && (button != Remote::POWER)) return; + if (!connLost && boxstack->handleCommand(button)) return; // don't send to boxstack if connLost + + // command was not handled + + switch(button) + { + case Remote::DF_LEFT: + case Remote::DF_RIGHT: + case Remote::VOLUMEUP: + case Remote::VOLUMEDOWN: + { + VVolume* v = new VVolume(); + boxstack->add(v); + v->handleCommand(button); // this will draw+show + return; + } + case Remote::MUTE: + { + VMute* v = new VMute(); + v->draw(); + boxstack->add(v); + boxstack->update(v); + return; + } + case Remote::POWER: + { + doStandby(); + return; + } + case Remote::OK: + { + // FIXME + if (!connLost) return; // if connLost, handle Remote::OK + doFromTheTop(false); + return; + } + case Remote::GO: + { + VSleeptimer* sleep = new VSleeptimer(); + boxstack->add(sleep); + sleep->handleCommand(button); // this will draw+show + return; + } + } +} + +void Command::sig1() +{ +#ifdef DEV + Message* m = new Message(); // break into master mutex + m->message = Message::SCREENSHOT; + m->to = this; + postMessage(m); +#endif +} + +void Command::doStandby() +{ + if (isStandby) + { + Video::getInstance()->signalOn(); + Led::getInstance()->on(); + isStandby = 0; + + + VConnect* vconnect = new VConnect(server); + boxstack->add(vconnect); + vconnect->run(); + } + else + { + boxstack->removeAll(); + Video::getInstance()->signalOff(); + boxstack->update(wallpaper); + + VDR::getInstance()->configSave("General", "Last Power State", "Off"); + logger->unsetExternLogger(); + VDR::getInstance()->disconnect(); + Led::getInstance()->off(); + isStandby = 1; + Sleeptimer::getInstance()->shutdown(); +#ifdef WIN32 + stop(); //different behavoiur on windows, we exit +#endif + } +} + +void Command::doFromTheTop(bool which) +{ + if (which) + { + if (connLost) + { + logger->log("Command", Log::NOTICE, "Connection lost dialog already present"); + return; + } + + logger->log("Command", Log::NOTICE, "Doing connection lost dialog"); + connLost = new VInfo(); + connLost->setSize(360, 200); + connLost->createBuffer(); + if (Video::getInstance()->getFormat() == Video::PAL) + connLost->setPosition(190, 170); + else + connLost->setPosition(180, 120); + connLost->setOneLiner(tr("Connection lost")); + connLost->setDropThrough(); + connLost->setBorderOn(1); + connLost->setTitleBarColour(Colour::DANGER); + connLost->okButton(); + connLost->draw(); + boxstack->add(connLost); + boxstack->update(connLost); + remote->clearBuffer(); + } + else + { + logger->unsetExternLogger(); + VDR::getInstance()->disconnect(); + boxstack->removeAll(); + boxstack->update(wallpaper); + connLost = NULL; + + flushMessageQueue(); + remote->clearBuffer(); + + // at this point, everything should be reset to first-go + + VConnect* vconnect = new VConnect(server); + boxstack->add(vconnect); + vconnect->run(); + } +} + +void Command::doReboot() +{ + + logger->unsetExternLogger(); + VDR::getInstance()->disconnect(); + // just kill it... + logger->log("Command", Log::NOTICE, "Reboot"); +#ifndef WIN32 +#ifdef VOMP_PLATTFORM_MVP + reboot(LINUX_REBOOT_CMD_RESTART); +#else + stop(); + +#ifdef __ANDROID__ + exit(0); +#endif + +#endif +#endif //Would we support this on windows? +} + +void Command::connectionLost() +{ + logger->unsetExternLogger(); + Message* m = new Message(); // break into master mutex + m->message = Message::CONNECTION_LOST; + m->to = this; + postMessageFromOuterSpace(m); +} + +void Command::buildCrashedBox() +{ + VInfo* crash = new VInfo(); + crash->setSize(360, 250); + crash->createBuffer(); + if (Video::getInstance()->getFormat() == Video::PAL) + crash->setPosition(190, 146); + else + crash->setPosition(180, 96); + crash->setMainText("Oops, vomp crashed.. :(\nPlease report this crash to the author, with as much detail as possible about what you were doing at the time that might have caused the crash."); + crash->setBorderOn(1); + crash->setTitleBarColour(Colour::DANGER); + crash->okButton(); + crash->setExitable(); + crash->draw(); + boxstack->add(crash); + boxstack->update(crash); +} + +void Command::doJustConnected(VConnect* vconnect) +{ + I18n::initialize(); + if (!VDR::getInstance()->isConnected()) { connectionLost(); return; } + + Video* video = Video::getInstance(); + Audio* audio = Audio::getInstance(); + boxstack->remove(vconnect); + + VInfo* vi = new VInfo(); + vi->setSize(400, 200); + vi->createBuffer(); + if (video->getFormat() == Video::PAL) + vi->setPosition(170, 200); + else + vi->setPosition(160, 150); + vi->setOneLiner(tr("Connected, loading config")); + vi->draw(); + boxstack->add(vi); + boxstack->update(vi); + + VDR* vdr = VDR::getInstance(); + char* config; + + // See if we're supposed to do network logging + config = vdr->configLoad("Advanced", "Network logging"); + if (config && !STRCASECMP(config, "On")) + { + logger->log("Command", Log::INFO, "Turning on network logging"); + logger->setExternLogger(vdr); + } + else + { + logger->unsetExternLogger(); + logger->log("Command", Log::INFO, "Turned off network logging"); + } + if (config) delete[] config; + + // See if config says to override video format (PAL/NTSC) + config = vdr->configLoad("General", "Override Video Format"); + if (config) + { + logger->log("Command", Log::DEBUG, "Override Video Format is present"); + + if ( (!strcmp(config, "PAL") && (video->getFormat() == Video::NTSC)) + || (!strcmp(config, "NTSC") && (video->getFormat() == Video::PAL)) ) + { + // Oh sheesh, need to switch format. Bye bye TV... + + // Take everything down + boxstack->removeAll(); + boxstack->remove(wallpaper); + Osd* osd = Osd::getInstance(); +#ifndef __ANDROID__ + osd->shutdown(); +#endif + video->shutdown(); + + // Get video and osd back up with the new mode + if (!strcmp(config, "PAL")) + { + logger->log("Command", Log::DEBUG, "Switching to PAL"); + video->init(Video::PAL); + } + else if (!strcmp(config, "NTSC")) + { + logger->log("Command", Log::DEBUG, "Switching to NTSC"); + video->init(Video::NTSC); + } +#ifndef __ANDROID__ + //we do not init twice + osd->init((char*)("/dev/stbgfx")); +#endif + + // Put the wallpaper back + doWallpaper(); + + // Re add the vinfo + vi = new VInfo(); + vi->setSize(400, 200); + vi->createBuffer(); + if (video->getFormat() == Video::PAL) + vi->setPosition(170, 200); + else + vi->setPosition(160, 150); + + vi->setOneLiner(tr("Connected, loading config")); + vi->draw(); + boxstack->add(vi); + boxstack->update(vi); + } + else + { + logger->log("Command", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'"); + } + } + else + { + logger->log("Command", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!"); + } + + // Power off if first boot and config says so + if (firstBoot) + { + firstBoot = 0; + + logger->log("Command", Log::DEBUG, "Load power after boot"); + + config = vdr->configLoad("General", "Power After Boot"); + + if (config) + { + if (!STRCASECMP(config, "On")) + { + logger->log("Command", Log::INFO, "Config says Power After Boot = On"); + } + else if (!STRCASECMP(config, "Off")) + { + logger->log("Command", Log::INFO, "Config says Power After Boot = Off"); + doStandby(); + delete[] config; + return; // quit here + } + else if (!STRCASECMP(config, "Last state")) + { + char* lastPowerState = vdr->configLoad("General", "Last Power State"); + if (lastPowerState) + { + if (!STRCASECMP(lastPowerState, "On")) + { + logger->log("Command", Log::INFO, "Config says Last Power State = On"); + } + else if (!STRCASECMP(lastPowerState, "Off")) + { + logger->log("Command", Log::INFO, "Config says Last Power State = Off"); + doStandby(); + delete[] config; + return; // quit here + } + else + { + logger->log("Command", Log::INFO, "Config General/Last Power State not understood"); + } + } + else + { + logger->log("Command", Log::INFO, "Config General/Last Power State not found"); + } + } + else + { + logger->log("Command", Log::INFO, "Config/Power After Boot not understood"); + } + delete[] config; + } + else + { + logger->log("Command", Log::INFO, "Config General/Power After Boot not found"); + } + } + + + // Go S-Video if config says so + + config = vdr->configLoad("TV", "Connection"); + + if (config) + { + if (!STRCASECMP(config, "S-Video")) + { + logger->log("Command", Log::INFO, "Switching to S-Video as Connection=%s", config); + video->setConnection(Video::SVIDEO); + } + else + { + logger->log("Command", Log::INFO, "Switching to RGB/Composite as Connection=%s", config); + video->setConnection(Video::COMPOSITERGB); + } + delete[] config; + } + else + { + logger->log("Command", Log::INFO, "Config TV/S-Video not found"); + } + + // Set remote type + + config = vdr->configLoad("General", "Remote type"); + + if (config) + { + if (!STRCASECMP(config, "New")) + { + logger->log("Command", Log::INFO, "Switching to New remote type"); + remote->setRemoteType(Remote::NEWREMOTE); + } + else + { + logger->log("Command", Log::INFO, "Switching to Old remote type"); + remote->setRemoteType(Remote::OLDREMOTE); + } + delete[] config; + } + else + { + logger->log("Command", Log::INFO, "Config General/Remote type not found"); + remote->setRemoteType(Remote::OLDREMOTE); + } + + + + + // Get TV aspect ratio + + config = vdr->configLoad("TV", "Aspect"); + if (config) + { + if (!STRCASECMP(config, "16:9")) + { + logger->log("Command", Log::INFO, "/// Switching to TV aspect 16:9"); + video->setTVsize(Video::ASPECT16X9); + } + else + { + logger->log("Command", Log::INFO, "/// Switching to TV aspect 4:3"); + video->setTVsize(Video::ASPECT4X3); + } + delete[] config; + } + else + { + logger->log("Command", Log::INFO, "Config TV/Aspect type not found, going 4:3"); + video->setTVsize(Video::ASPECT4X3); + } + + config = vdr->configLoad("TV", "Widemode"); + if (config) + { + if (!STRCASECMP(config, "Letterbox")) + { + logger->log("Command", Log::INFO, "Setting letterbox mode"); + video->setMode(Video::LETTERBOX); + } + else + { + logger->log("Command", Log::INFO, "Setting chop-sides mode"); + video->setMode(Video::NORMAL); + } + delete[] config; + } + else + { +#ifdef __ANDROID__ + logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting letterbox mode"); + video->setMode(Video::LETTERBOX); +#else + logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode"); + video->setMode(Video::NORMAL); +#endif + } + + config = vdr->configLoad("Advanced", "TCP receive window"); + if (config) + { + size_t newTCPsize = atoi(config); + delete[] config; + + logger->log("Command", Log::INFO, "Setting TCP window size %i", newTCPsize); + vdr->setReceiveWindow(newTCPsize); + } + else + { + logger->log("Command", Log::INFO, "TCP window size not found, setting 2048"); + vdr->setReceiveWindow(2048); // Default + } + + config = vdr->configLoad("Advanced", "Disable WOL"); + if (config) + { + if (!STRCASECMP(config, "Yes")) + { + logger->log("Command", Log::INFO, "Config says disable WOL"); + Wol::getInstance()->setEnabled(false); + } + else + { + logger->log("Command", Log::INFO, "Config says enable WOL"); + Wol::getInstance()->setEnabled(true); + } + + delete[] config; + } + else + { + logger->log("Command", Log::INFO, "By default, enable WOL"); + Wol::getInstance()->setEnabled(true); + } + /* device dependend config */ + audio->loadOptionsfromServer(vdr); + video->loadOptionsfromServer(vdr); + remote->loadOptionsfromServer(vdr); + // config done + + // Save power state = on + + vdr->configSave("General", "Last Power State", "On"); + + // Make sure connection didn't die + if (!vdr->isConnected()) + { + Command::getInstance()->connectionLost(); + } + else + { + boxstack->remove(vi); + + VWelcome* vw = new VWelcome(); + vw->draw(); + boxstack->add(vw); + boxstack->update(vw); + + // Enter pre-keys here +// handleCommand(Remote::OK); +// handleCommand(Remote::THREE); +// handleCommand(Remote::SIX); +// handleCommand(Remote::OK); +// handleCommand(Remote::UP); +// handleCommand(Remote::PLAY); +// handleCommand(Remote::DOWN); +// handleCommand(Remote::DOWN); +// handleCommand(Remote::DOWN); + // handleCommand(Remote::OK); +// handleCommand(Remote::RED); + } +} diff --git a/defines.h b/defines.h index f082f53..7a9abbb 100644 --- a/defines.h +++ b/defines.h @@ -1,89 +1,135 @@ -/* - 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 DEFINES_H -#define DEFINES_H - -typedef unsigned char UCHAR; -typedef unsigned short USHORT; -typedef unsigned int UINT; -typedef unsigned long ULONG; -typedef unsigned long long ULLONG; - -#define OPTIONTYPE_TEXT 1 -#define OPTIONTYPE_INT 2 - -ULLONG htonll(ULLONG a); -ULLONG ntohll(ULLONG a); -void MILLISLEEP(ULONG a); - -#ifdef WIN32 - - #define Surface_TYPE SurfaceWin - #define Thread_TYPE ThreadWin - #define ThreadID_TYPE unsigned int - - #define SNPRINTF _snprintf - #define VSNPRINTF _vsnprintf - #define STRCASECMP _stricmp - #define STRCASESTR StrStrI -/* #define STRTOULL _strtoui64 */ - #define STRTOUL strtoul - #define CLOSESOCKET closesocket - -#else - - int max(int, int); - int min(UINT, int); -#ifdef _MIPS_ARCH - #define Surface_TYPE SurfaceDirectFB -#else - #define Surface_TYPE SurfaceMVP -#endif - - #define Thread_TYPE ThreadP - #include - #define ThreadID_TYPE pthread_t - - #define SNPRINTF snprintf - #define VSNPRINTF vsnprintf - #define STRCASECMP strcasecmp - #define STRCASESTR strcasestr - #define STRTOUL strtoul - #define CLOSESOCKET close - -#endif - -/* -typedef struct -{ - UINT id; // Used for working out what has changed at the end - char *title; // Name of the option - char *configSection; // Which section of the config file - char *configParam; // Parameter name in the config file - UINT optionType; // 1 for text, 2 for int - UINT optionCount; // How many choices? - UINT defaultOption; // Serial of the default choice (base 0), or actual option in int mode - int startInt; // Starting int for int mode - const char * const * options; // Text for the options (null for int mode) -} OPTIONDATA; -*/ - -#endif +/* + 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 DEFINES_H +#define DEFINES_H + +typedef unsigned char UCHAR; +typedef unsigned short USHORT; +typedef unsigned int UINT; +typedef unsigned long ULONG; +typedef unsigned long long ULLONG; + +#define OPTIONTYPE_TEXT 1 +#define OPTIONTYPE_INT 2 + +//ULLONG htonll(ULLONG a); +//ULLONG ntohll(ULLONG a); +void MILLISLEEP(ULONG a); + +#ifdef WIN32 + + #define Surface_TYPE SurfaceWin + #define Thread_TYPE ThreadWin + #define ThreadID_TYPE unsigned int + + #define SNPRINTF _snprintf + #define VSNPRINTF _vsnprintf + #define STRCASECMP _stricmp + #define STRCASESTR StrStrI +/* #define STRTOULL _strtoui64 */ + #define STRTOUL strtoul + #define CLOSESOCKET closesocket + + +#else + + int max(int, int); + int min(UINT, int); +/*#ifdef _MIPS_ARCH + #define Surface_TYPE SurfaceDirectFB +#else + #define Surface_TYPE SurfaceMVP +#endif*/ +#ifdef __ANDROID__ + #define Thread_TYPE ThreadPAndroid + +#else + #define Thread_TYPE ThreadP + +#endif + #include + #define ThreadID_TYPE pthread_t + + #define SNPRINTF snprintf + #define VSNPRINTF vsnprintf + #define STRCASECMP strcasecmp + #define STRCASESTR strcasestr + #define STRTOUL strtoul + #define CLOSESOCKET close + +// add here defines for plattform specific objects +#ifdef VOMP_PLATTFORM_RASPBERRY + #define Remote_TYPE RemoteLinux // Generic Remote under Linux (Konsole!, not X) will support in the end: + #define RemoteStartDev ""//No devices passed + + // Keyboard + // remotes under /dev/event + // HDMI CEC + //lirc? + #define Mtd_TYPE MtdRaspberry //this is device dependent + #define Led_TYPE LedRaspberry //this is device dependent + #define Osd_TYPE OsdOpenGL // This OpenGL ES 2.0, in the moment only for raspberry, but might be splitted for other devices + #define OsdStartDev "" + #define Audio_TYPE AudioVPE // This is Audio based on VPE (Vomp Presentation Engine) should support OpenMax and Alsa with ffmpeg in the end + #define Video_TYPE VideoVPEOGL // This is Video based on VPE (Vomp Presentation Engine) should support OpenMax and ffmpeg and opengl in the end + +#endif +#ifdef VOMP_PLATTFORM_MVP + #define Remote_TYPE RemoteMVP + #define RemoteStartDev "/dev/rawir" + #define Mtd_TYPE MtdMVP + #define Led_TYPE LedMVP + #define Osd_TYPE OsdMVP + #define OsdStartDev "/dev/stbgfx" + #define Audio_TYPE AudioMVP + #define Video_TYPE VideoMVP +#endif + +#ifdef VOMP_PLATTFORM_NMT // This was the attempt to port vomp to nmt, it failed but maybe the code is useful at some time + #define Remote_TYPE RemoteLirc + #define RemoteStartDev "/dev/lircd" + #define Mtd_TYPE MtdNMT + #define Led_TYPE LedMVP + #define Osd_TYPE OsdDirectFB + #define OsdStartDev "" + #define Audio_TYPE AudioNMT + #define Video_TYPE VideoNMT + +#endif + +#endif + +/* +typedef struct +{ + UINT id; // Used for working out what has changed at the end + char *title; // Name of the option + char *configSection; // Which section of the config file + char *configParam; // Parameter name in the config file + UINT optionType; // 1 for text, 2 for int + UINT optionCount; // How many choices? + UINT defaultOption; // Serial of the default choice (base 0), or actual option in int mode + int startInt; // Starting int for int mode + const char * const * options; // Text for the options (null for int mode) +} OPTIONDATA; +*/ + +#endif diff --git a/demuxer.cc b/demuxer.cc index 3d3f358..e2e2fe7 100644 --- a/demuxer.cc +++ b/demuxer.cc @@ -1,947 +1,993 @@ -/* - Copyright 2005-2008 Mark Calderbank - Copyright 2007 Marten Richter (AC3 support) - - 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 "demuxer.h" - -#include "callback.h" -#include "dvbsubtitles.h" -#include "log.h" - -#include - -#include - -#define DEMUXER_SEQ_HEAD 0x000001B3 -#define DEMUXER_PIC_HEAD 0x00000101 - -#define DEMUXER_H264_ACCESS_UNIT 0x00000109 -#define DEMUXER_H264_SEQ_PARAMETER_SET 0x00000107 - - -#define SEEK_THRESHOLD 150000 // About 1.5 seconds - -// Statics -const int Demuxer::FrameRates[9] = { 0, 23, 24, 25, 29, 30, 50, 59, 60 }; -Demuxer* Demuxer::instance = NULL; - -class NALUUnit { -public: - NALUUnit(const UCHAR* buf,UINT length_buf); - ~NALUUnit(); - -inline UINT getBits(UINT num_bits); - UINT getUe(); - int getSe(); - bool isEonalu() {return eonalu;}; - -protected: - UCHAR* nalu_buf; - UINT nalu_length; - UINT pos; - UCHAR bit_pos; - UCHAR working_byte; - UINT last_bytes; - bool eonalu; -}; - -NALUUnit::NALUUnit(const UCHAR *buf, UINT length_buf) -{ - nalu_length=0; - nalu_buf=NULL; - pos=0; - bit_pos=0; - working_byte=0; - last_bytes=0; - eonalu=false; - - UINT nalu_start=0; - UINT nalu_end=0; - UINT pattern =(((UINT)buf[ 0] << 16) | - ((UINT)buf[1] << 8) | - (UINT)buf[2] ); - nalu_start=3; - while (pattern != 0x000001) - { - if (++nalu_start >= length_buf) return; - pattern = ((pattern << 8) | buf[nalu_start])&0x00FFFFFF; - } - nalu_end=nalu_start+1; - pattern = ((pattern << 8) | buf[nalu_end])&0x00FFFFFF; - - while (pattern != 0x000001 && pattern != 0x000000) - { - if (++nalu_end >= length_buf) { nalu_end+=3;break;}; - pattern = ((pattern << 8) | buf[nalu_end])&0x00FFFFFF; - } - nalu_end-=3; - nalu_end=min(length_buf-1,nalu_end); - nalu_length=nalu_end-nalu_start; - nalu_buf=(UCHAR*)malloc(nalu_length); - memcpy(nalu_buf,buf+nalu_start,nalu_length); - pos=1; -} - -NALUUnit::~NALUUnit() -{ - if (nalu_buf) free(nalu_buf); -} - -inline UINT NALUUnit::getBits(UINT num_bits) -{ - if (num_bits==0) return 0; //??? - UINT remain_bits=num_bits; - UINT work=0; - //May be slow, but should work! - while (remain_bits>0) { - if (bit_pos==0) { - if (pos>bit_pos) & (0xFF>>(8-fetch_bits))); - work|=(working_byte &(0xFF>>(bit_pos)))>>(8-fetch_bits-bit_pos); - remain_bits-=fetch_bits; - bit_pos=(bit_pos+fetch_bits)%8; - } - return work; -} - -UINT NALUUnit::getUe() -{ - int leadbits=-1; - bool bit; - for( bit = 0; !bit && !eonalu; leadbits++ ) - bit = getBits(1); - if (eonalu) return true; - return ((1 << leadbits)-1)+getBits(leadbits); -} - -int NALUUnit::getSe() -{ - UINT input=getUe(); - if (input==0) return 0; - int output=((input+1)>>1); - if (input & 0x1) output*=-1; - return output; -} - - - -static const int PESPacket_initial_size = 2000; - -// PESPacket methods -PESPacket::PESPacket() -{ - data_size = PESPacket_initial_size; - data = (UCHAR*)malloc(data_size); - data[0] = 0x00; - data[1] = 0x00; - data[2] = 0x01; - init(0); -} - -PESPacket::PESPacket(const PESPacket& packet) -{ - copyFrom(packet); -} - -PESPacket& PESPacket::operator=(const PESPacket& packet) -{ - if (this != &packet) - { - if (data) free(data); - copyFrom(packet); - } - return *this; -} - -PESPacket::~PESPacket() -{ - if (data) free(data); -} - -void PESPacket::copyFrom(const PESPacket& packet) -{ - length = packet.length; - size = packet.size; - packetType = packet.packetType; - substream = packet.substream; - seq_header = packet.seq_header; - data_size = size; - data = (UCHAR*)malloc(data_size); - memcpy(data, packet.data, data_size); -} - -void PESPacket::init(UCHAR type, UCHAR sub) -{ - length = 0; - size = 6; - data[4] = data[5] = 0; - data[3] = type; - packetType = type; - substream = sub; - seq_header = 1; // Unknown seq_header status -} - -void PESPacket::truncate() -{ - init(packetType,substream); -} - -int PESPacket::write(const UCHAR *buf, int len) -{ - if (size + len > 0x10000) return 0; - if (size + len > data_size) - { // Reallocate - UINT new_data_size = max(data_size + data_size / 2, data_size + len); - if (new_data_size > 0x10000) new_data_size = 0x10000; - data_size = new_data_size; - data = (UCHAR*)realloc(data, data_size); - } - memcpy(data + size, buf, len); - length += len; - size += len; - data[4] = (length >> 8); - data[5] = (length & 0xFF); - // We have added data - reset seq_header indicator if necessary - if (seq_header == 0) seq_header = 1; // Reset to 'unknown' - return 1; -} - -ULLONG PESPacket::getPTS() const -{ - if ( ( (packetType >= Demuxer::PESTYPE_AUD0 && - packetType <= Demuxer::PESTYPE_AUDMAX) - || - (packetType >= Demuxer::PESTYPE_VID0 && - packetType <= Demuxer::PESTYPE_VIDMAX) - || - packetType == Demuxer::PESTYPE_PRIVATE_1 - ) - && size >= 14 && data[7] & 0x80) - { - return ( (ULLONG)(data[ 9] & 0x0E) << 29) | - ( (ULLONG)(data[10]) << 22 ) | - ( (ULLONG)(data[11] & 0xFE) << 14 ) | - ( (ULLONG)(data[12]) << 7 ) | - ( (ULLONG)(data[13] & 0xFE) >> 1 ); - } - else return PTS_INVALID; -} - -UCHAR PESPacket::operator[] (UINT index) const -{ - if (index >= size) - return 0; - else - return data[index]; -} - -UINT PESPacket::findPictureHeader(bool h264) const -{ - if (size < 12) return 0; - UINT pattern = ( ((UINT)data[ 8] << 24) | - ((UINT)data[ 9] << 16) | - ((UINT)data[10] << 8) | - (UINT)data[11] ); - UINT pos = 11; - if (h264) { - - while (pattern != DEMUXER_H264_ACCESS_UNIT) - { - if (++pos >= size) return 0; - pattern = (pattern << 8) | data[pos]; - } - return pos-3; - } else { - while (pattern != DEMUXER_PIC_HEAD) - { - if (++pos >= size) return 0; - pattern = (pattern << 8) | data[pos]; - } - return pos-3; - } -} - -UINT PESPacket::countPictureHeaders(bool h264) const -{ - if (size < 12) return 0; - UINT pattern = ( ((UINT)data[ 8] << 24) | - ((UINT)data[ 9] << 16) | - ((UINT)data[10] << 8) | - (UINT)data[11] ); - UINT pos = 11; - UINT count=0; - if (h264) { - - while (pos= size) - { - seq_header = 0; - return 0; - } - pattern = (pattern << 8) | data[pos]; - } - seq_header = pos - 3; - } - else - { - while (pattern != DEMUXER_SEQ_HEAD) - { - if (++pos >= size) - { - seq_header = 0; - return 0; - } - pattern = (pattern << 8) | data[pos]; - } - seq_header = pos - 3; - } - return seq_header; -} - -// Demuxer methods -Demuxer::Demuxer() -{ - if (instance) return; - instance = this; - initted = false; - callback = NULL; - arcnt = 0; - vid_seeking = aud_seeking = false; - video_pts = audio_pts = 0; - ispre_1_3_19 = false; - packetnum=0; - h264 = false; - fps = 25.0; -} - -Demuxer::~Demuxer() -{ - shutdown(); - instance = NULL; -} - -Demuxer* Demuxer::getInstance() -{ - return instance; -} - -int Demuxer::init(Callback* tcallback, DrainTarget* audio, DrainTarget* video, DrainTarget* teletext, - ULONG demuxMemoryV, ULONG demuxMemoryA, ULONG demuxMemoryT,double infps, DVBSubtitles* tsubtitles) -{ - if (!initted) - { - if ( !videostream.init(video, demuxMemoryV) || - !audiostream.init(audio, demuxMemoryA) || - !teletextstream.init(teletext, demuxMemoryT)) - { - Log::getInstance()->log("Demuxer", Log::CRIT, - "Failed to initialize demuxer"); - shutdown(); - return 0; - } - } - if (teletext) { - isteletextdecoded = true; - } else { - isteletextdecoded = false; - } - fps=infps; - reset(); - initted = true; - subtitles = tsubtitles; - callback = tcallback; - return 1; -} - -void Demuxer::reset() -{ - Log::getInstance()->log("Demuxer", Log::DEBUG, "Reset called"); - flush(); - video_current = audio_current = teletext_current = subtitle_current = -1; - horizontal_size = vertical_size = 0; - aspect_ratio = (enum AspectRatio) 0; - frame_rate = bit_rate = 0; - ispre_1_3_19 = false; - h264 = false; - packetnum=0; - - for (int i = 0; i <= (PESTYPE_AUDMAX - PESTYPE_AUD0); i++) - { - avail_mpaudchan[i] = false; - } - for (int i = 0; i <= (PESTYPE_SUBSTREAM_AC3MAX - PESTYPE_SUBSTREAM_AC30); i++) - { - avail_ac3audchan[i] = false; - } - for (int i = 0; i <= (PESTYPE_SUBSTREAM_DVBSUBTITLEMAX - PESTYPE_SUBSTREAM_DVBSUBTITLE0); i++) - { - avail_dvbsubtitlechan[i] = false; - } -} - -int Demuxer::shutdown() -{ - videostream.shutdown(); - audiostream.shutdown(); - teletextstream.shutdown(); - initted = false; - return 1; -} - -void Demuxer::flush() -{ - Log::getInstance()->log("Demuxer", Log::DEBUG, "Flush called"); - - videostream.flush(); - audiostream.flush(); - teletextstream.flush(); -} - -void Demuxer::flushAudio() -{ - audiostream.flush(); -} - -void Demuxer::seek() -{ - vid_seeking = aud_seeking = true; - video_pts = audio_pts = teletext_pts = 0; -} - -void Demuxer::setAudioStream(int id) -{ - audio_current = id; -} - -void Demuxer::setVideoStream(int id) -{ - video_current = id; -} - -void Demuxer::setTeletextStream(int id) -{ - teletext_current = id; -} - -void Demuxer::setDVBSubtitleStream(int id) -{ - subtitle_current = id; -} - -void Demuxer::setAspectRatio(enum AspectRatio ar) -{ - if (aspect_ratio != ar) - { - Log::getInstance()->log("Demux", Log::DEBUG, - "Aspect ratio difference signalled"); - if (++arcnt > 3) // avoid changing aspect ratio if glitch in signal - { - arcnt = 0; - aspect_ratio = ar; - if (callback) callback->call(this); - } - } - else - arcnt = 0; -} - -bool Demuxer::writeAudio() -{ - return audiostream.drain(); -} - -bool Demuxer::writeVideo() -{ - return videostream.drain(); -} - -bool Demuxer::writeTeletext() -{ - return teletextstream.drain(); -} - -bool Demuxer::submitPacket(PESPacket& packet) -{ - UINT sent = 0; - UCHAR packet_type = packet.getPacketType(); - const UCHAR* packetdata = packet.getData(); - if (packet_type >= PESTYPE_VID0 && packet_type <= PESTYPE_VIDMAX) - { - if (video_current == -1) video_current = packet_type; - if (video_current == packet_type && !vid_seeking) - { - sent = videostream.put(&packetdata[0], packet.getSize(), h264?MPTYPE_VIDEO_H264:MPTYPE_VIDEO_MPEG2,packetnum); - if (sent) packetnum++; - } - else - sent = packet.getSize(); - } - else if (packet_type >= PESTYPE_AUD0 && packet_type <= PESTYPE_AUDMAX) - { - if (audio_current == -1) audio_current = packet_type; - avail_mpaudchan[packet_type - PESTYPE_AUD0] = true; - if (audio_current == packet_type && !aud_seeking) - { - sent = audiostream.put(&packetdata[0], packet.getSize(), MPTYPE_MPEG_AUDIO,packetnum); - if (sent) packetnum++; - } - else - sent = packet.getSize(); - } - else if (packet_type == PESTYPE_PRIVATE_1 && - packet.getSubstream() >= PESTYPE_SUBSTREAM_AC30 && - packet.getSubstream() <= PESTYPE_SUBSTREAM_AC3MAX) - { - avail_ac3audchan[packet.getSubstream() - PESTYPE_SUBSTREAM_AC30] = true; - if (packet.getSubstream() == audio_current) - { - sent = audiostream.put(&packetdata[0], packet.getSize(), (ispre_1_3_19)? MPTYPE_AC3_PRE13 : MPTYPE_AC3,packetnum); - if (sent) packetnum++; - } - else - { - sent = packet.getSize(); - } - } - else if (packet_type == PESTYPE_PRIVATE_1 && - packet.getSubstream() >= PESTYPE_SUBSTREAM_DVBSUBTITLE0 && - packet.getSubstream() <= PESTYPE_SUBSTREAM_DVBSUBTITLEMAX) - { - avail_dvbsubtitlechan[packet.getSubstream()-PESTYPE_SUBSTREAM_DVBSUBTITLE0]=true; - if (subtitle_current == -1) subtitle_current = packet.getSubstream(); - if (subtitles && packet.getSubstream()==subtitle_current) - { - subtitles->put(packet); - } - sent = packet.getSize(); - } - else if (isteletextdecoded && packet_type == PESTYPE_PRIVATE_1 && - packet.getSubstream() >= PESTYPE_SUBSTREAM_TELETEXT0 && - packet.getSubstream() <= PESTYPE_SUBSTREAM_TELETEXTMAX) - { - - if (teletext_current == -1) teletext_current = packet.getSubstream(); - if (teletext_current == packet.getSubstream() ) - { - sent = teletextstream.put(&packetdata[0], packet.getSize(), MPTYPE_TELETEXT,packetnum); - } - else - { - sent = packet.getSize(); - } - } - else - { - sent = packet.getSize(); - } - - if (sent < packet.getSize()) // Stream is full. - return false; - else - return true; -} - -void Demuxer::parsePacketDetails(PESPacket& packet) -{ - if (packet.getPacketType() >= PESTYPE_AUD0 && - packet.getPacketType() <= PESTYPE_AUDMAX) - { - // Extract audio PTS if it exists - if (packet.hasPTS()) - { - audio_pts = packet.getPTS(); - // We continue to seek on the audio if the video PTS that we - // are trying to match is ahead of the audio PTS by at most - // SEEK_THRESHOLD. We consider the possibility of PTS wrap. - if (aud_seeking && !vid_seeking && - !( (video_pts_seek > audio_pts && - video_pts_seek - audio_pts < SEEK_THRESHOLD) - || - (video_pts_seek < audio_pts && - video_pts_seek + (1LL<<33) - audio_pts < SEEK_THRESHOLD) )) - { - aud_seeking = 0; - Log::getInstance()->log("Demuxer", Log::DEBUG, - "Leaving audio sync: Audio PTS = %llu", audio_pts); - } - } - } - else if (packet.getPacketType() == PESTYPE_PRIVATE_1) // Private stream - { - //Inspired by vdr's device.c - int payload_begin = packet[8]+9; - unsigned char substream_id = packet[payload_begin]; - unsigned char substream_type = substream_id & 0xF0; - unsigned char substream_index = substream_id & 0x1F; -pre_1_3_19_Recording: //This is for old recordings stuff and live TV - if (ispre_1_3_19) - { - substream_id = PESTYPE_PRIVATE_1; - substream_type = 0x80; - substream_index = 0; - } - switch (substream_type) - { - case 0x20://SPU - case 0x30://SPU - packet.setSubstream(substream_id); - break; - case 0xA0: //LPCM //not supported yet, is there any LPCM transmissio out there? - break; - case 0x80: //ac3, currently only one ac3 track per recording supported - packet.setSubstream(substream_type+substream_index); - - // Extract audio PTS if it exists - if (packet.hasPTS()) - { - audio_pts = packet.getPTS(); - // We continue to seek on the audio if the video PTS that we - // are trying to match is ahead of the audio PTS by at most - // SEEK_THRESHOLD. We consider the possibility of PTS wrap. - if (aud_seeking && !vid_seeking && - !( (video_pts_seek > audio_pts && - video_pts_seek - audio_pts < SEEK_THRESHOLD) - || - (video_pts_seek < audio_pts && - video_pts_seek + (1LL<<33) - audio_pts < SEEK_THRESHOLD) )) - { - aud_seeking = 0; - Log::getInstance()->log("Demuxer", Log::DEBUG, "Leaving audio sync: Audio PTS = %llu", audio_pts); - } - } - break; - case 0x10: //Teletext Is this correct? - packet.setSubstream(substream_id); - // Extract teletxt PTS if it exists - if (packet.hasPTS()) - { - teletext_pts = packet.getPTS(); - } - break; - default: - if (!ispre_1_3_19) - { - ispre_1_3_19=true; //switching to compat mode and live tv mode - goto pre_1_3_19_Recording; - } - else - { - packet.setSubstream(0); - } - break; - } - } - else if (packet.getPacketType() >= PESTYPE_VID0 && - packet.getPacketType() <= PESTYPE_VIDMAX) - { - // Extract video PTS if it exists - if (packet.hasPTS()) video_pts = packet.getPTS(); - - // If there is a sequence header, extract information - UINT pos = packet.findSeqHeader(h264); - if (pos > 1) - { - if (!h264) { - pos += 4; - if (pos+6 >= packet.getSize()) return; - horizontal_size = ((int)packet[pos] << 4) | ((int)packet[pos+1] >> 4); - - vertical_size = (((int)packet[pos+1] & 0xf) << 8) | (int)packet[pos+2]; - - setAspectRatio((enum AspectRatio)(packet[pos+3] >> 4)); - frame_rate = packet[pos+3] & 0x0f; - if (frame_rate >= 1 && frame_rate <= 8) - frame_rate = FrameRates[frame_rate]; - else - frame_rate = 0; - bit_rate = ((int)packet[pos+4] << 10) | - ((int)packet[pos+5] << 2) | - ((int)packet[pos+6] >> 6); - } - else - { - /* Chris and Mark I know this is ugly, should we move this to a method of PESPacket or to NALUUnit what would be better? - This looks so ugly since the header includes variable length parts and I have to parse through the whole header to get the wanted information*/ - NALUUnit nalu(packet.getData()+pos,packet.getSize()-pos); - profile=nalu.getBits(8); - nalu.getBits(8); //constraints - nalu.getBits(8); //level_idc - nalu.getUe(); //seq_parameter_set_id - int chroma=1; - if (profile==100 || profile==110 || profile==122 || profile==144) - { - chroma=nalu.getUe(); - if (chroma==3) - { - nalu.getBits(1); - } - nalu.getUe(); //bit depth lume - nalu.getUe(); //bit depth chrome - nalu.getBits(1); - if (nalu.getBits(1)) - { - for (int i=0;i<8;i++){ - if (nalu.getBits(1)) - { - if (i<6) - { - UINT lastscale=8; - UINT nextscale=8; - for (int j=0;j<16;j++) { - if (nextscale!=0) { - UINT delta=nalu.getSe(); - nextscale=(lastscale+delta+256)%256; - } - lastscale=(nextscale==0)?lastscale:nextscale; - } - } - else - { - UINT lastscale=8; - UINT nextscale=8; - for (int j=0;j<64;j++) { - if (nextscale!=0) { - UINT delta=nalu.getSe(); - nextscale=(lastscale+delta+256)%256; - } - lastscale=(nextscale==0)?lastscale:nextscale; - } - } - } - } - } - } - int chromunitx=1; - int chromunity=1; - switch (chroma) { - case 0: - chromunitx=chromunity=1; break; - case 1: - chromunitx=chromunity=2; break; - case 2: - chromunitx=2;chromunity=1; break; - case 3: - chromunitx=chromunity=1; break; - }; - - nalu.getUe(); //log2framenum - UINT temp=nalu.getUe(); - if (temp==0) //pict order - nalu.getUe(); - else if (temp==1) { - nalu.getBits(1); - nalu.getSe(); - nalu.getSe(); - UINT temp2=nalu.getUe(); - for (int i=0;ilog("Demuxer", Log::DEBUG, - "Entering audio sync: Video PTS = %llu", video_pts); - Log::getInstance()->log("Demuxer", Log::DEBUG, - "Entering audio sync: Audio PTS = %llu", audio_pts); - } - return; - } - } -} - -UINT Demuxer::stripAudio(UCHAR* buf, UINT len) -{ - UINT read_pos = 0, write_pos = 0; - UINT pattern, packet_length; - if (len < 4) return 0; - pattern = (buf[0] << 16) | (buf[1] << 8) | (buf[2]); - while (read_pos + 7 <= len) - { - pattern = ((pattern & 0xFFFFFF) << 8) | buf[read_pos+3]; - if (pattern < (0x100|PESTYPE_VID0) || pattern > (0x100|PESTYPE_VIDMAX)) - read_pos++; - else - { - packet_length = ((buf[read_pos+4] << 8) | (buf[read_pos+5])) + 6; - if (read_pos + packet_length > len) - read_pos = len; - else - { - if (read_pos != write_pos) - memmove(buf+write_pos, buf+read_pos, packet_length); - read_pos += packet_length; - write_pos += packet_length; - pattern = (buf[read_pos] << 16) | (buf[read_pos+1] << 8) - | (buf[read_pos+2]); - } - } - } - return write_pos; -} - -bool Demuxer::scanForVideo(UCHAR* buf, UINT len, bool &ish264) -{ - UINT pos = 3; - UINT pattern; - ish264=false; - if (len < 4) return false; - pattern = (buf[0] << 16) | (buf[1] << 8) | (buf[2]); - while (pos < len) - { - pattern = ((pattern & 0xFFFFFF) << 8) | buf[pos++]; - if (pattern >= (0x100|PESTYPE_VID0) && pattern <= (0x100|PESTYPE_VIDMAX)) - return true; - } - return false; -} - -bool* Demuxer::getmpAudioChannels() -{ - return avail_mpaudchan; -} - -bool* Demuxer::getac3AudioChannels() -{ - return avail_ac3audchan; -} - -bool* Demuxer::getSubtitleChannels() -{ - return avail_dvbsubtitlechan; -} - -int Demuxer::getselSubtitleChannel() -{ - return subtitle_current; -} - -int Demuxer::getselAudioChannel() -{ - return audio_current; -} - - +/* + Copyright 2005-2008 Mark Calderbank + Copyright 2007 Marten Richter (AC3 support) + + 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 "demuxer.h" + +#include "callback.h" +#include "dvbsubtitles.h" +#include "log.h" + +#include + +#include + +#define DEMUXER_SEQ_HEAD 0x000001B3 +#define DEMUXER_PIC_HEAD 0x00000101 + +#define DEMUXER_H264_ACCESS_UNIT 0x00000109 +#define DEMUXER_H264_SEQ_PARAMETER_SET 0x00000107 + + +#define SEEK_THRESHOLD 150000 // About 1.5 seconds + +// Statics +const int Demuxer::FrameRates[9] = { 0, 23, 24, 25, 29, 30, 50, 59, 60 }; +Demuxer* Demuxer::instance = NULL; + +class NALUUnit { +public: + NALUUnit(const UCHAR* buf,UINT length_buf); + ~NALUUnit(); + +inline UINT getBits(UINT num_bits); + UINT getUe(); + int getSe(); + bool isEonalu() {return eonalu;}; + +protected: + UCHAR* nalu_buf; + UINT nalu_length; + UINT pos; + UCHAR bit_pos; + UCHAR working_byte; + UINT last_bytes; + bool eonalu; +}; + +NALUUnit::NALUUnit(const UCHAR *buf, UINT length_buf) +{ + nalu_length=0; + nalu_buf=NULL; + pos=0; + bit_pos=0; + working_byte=0; + last_bytes=0; + eonalu=false; + + UINT nalu_start=0; + UINT nalu_end=0; + UINT pattern =(((UINT)buf[ 0] << 16) | + ((UINT)buf[1] << 8) | + (UINT)buf[2] ); + nalu_start=3; + while (pattern != 0x000001) + { + if (++nalu_start >= length_buf) return; + pattern = ((pattern << 8) | buf[nalu_start])&0x00FFFFFF; + } + nalu_end=nalu_start+1; + pattern = ((pattern << 8) | buf[nalu_end])&0x00FFFFFF; + + while (pattern != 0x000001 && pattern != 0x000000) + { + if (++nalu_end >= length_buf) { nalu_end+=3;break;}; + pattern = ((pattern << 8) | buf[nalu_end])&0x00FFFFFF; + } + nalu_end-=3; + nalu_end=min(length_buf-1,nalu_end); + nalu_length=nalu_end-nalu_start; + nalu_buf=(UCHAR*)malloc(nalu_length); + memcpy(nalu_buf,buf+nalu_start,nalu_length); + pos=1; +} + +NALUUnit::~NALUUnit() +{ + if (nalu_buf) free(nalu_buf); +} + +inline UINT NALUUnit::getBits(UINT num_bits) +{ + if (num_bits==0) return 0; //??? + UINT remain_bits=num_bits; + UINT work=0; + //May be slow, but should work! + while (remain_bits>0) { + if (bit_pos==0) { + if (pos>bit_pos) & (0xFF>>(8-fetch_bits))); + work|=(working_byte &(0xFF>>(bit_pos)))>>(8-fetch_bits-bit_pos); + remain_bits-=fetch_bits; + bit_pos=(bit_pos+fetch_bits)%8; + } + return work; +} + +UINT NALUUnit::getUe() +{ + int leadbits=-1; + bool bit; + for( bit = 0; !bit && !eonalu; leadbits++ ) + bit = getBits(1); + if (eonalu) return true; + return ((1 << leadbits)-1)+getBits(leadbits); +} + +int NALUUnit::getSe() +{ + UINT input=getUe(); + if (input==0) return 0; + int output=((input+1)>>1); + if (input & 0x1) output*=-1; + return output; +} + + + +static const int PESPacket_initial_size = 2000; + +// PESPacket methods +PESPacket::PESPacket() +{ + data_size = PESPacket_initial_size; + data = (UCHAR*)malloc(data_size); + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0x01; + init(0); +} + +PESPacket::PESPacket(const PESPacket& packet) +{ + copyFrom(packet); +} + +PESPacket& PESPacket::operator=(const PESPacket& packet) +{ + if (this != &packet) + { + if (data) free(data); + copyFrom(packet); + } + return *this; +} + +PESPacket::~PESPacket() +{ + if (data) free(data); +} + +void PESPacket::copyFrom(const PESPacket& packet) +{ + length = packet.length; + size = packet.size; + packetType = packet.packetType; + substream = packet.substream; + seq_header = packet.seq_header; + data_size = size; + data = (UCHAR*)malloc(data_size); + memcpy(data, packet.data, data_size); +} + +void PESPacket::init(UCHAR type, UCHAR sub) +{ + length = 0; + size = 6; + data[4] = data[5] = 0; + data[3] = type; + packetType = type; + substream = sub; + seq_header = 1; // Unknown seq_header status + +} + +void PESPacket::truncate() +{ + init(packetType,substream); +} + + + +int PESPacket::write(const UCHAR *buf, int len) +{ + + + if (size + len > 0x10000) return 0; + if (size + len > data_size) + { // Reallocate + UINT new_data_size = max(data_size + data_size / 2, data_size + len); + if (new_data_size > 0x10000) new_data_size = 0x10000; + data_size = new_data_size; + data = (UCHAR*)realloc(data, data_size); + } + memcpy(data + size, buf, len); + length += len; + size += len; + data[4] = (length >> 8); + data[5] = (length & 0xFF); + // We have added data - reset seq_header indicator if necessary + if (seq_header == 0) seq_header = 1; // Reset to 'unknown' + return 1; +} + +ULLONG PESPacket::getPTS() const +{ + if ( ( (packetType >= Demuxer::PESTYPE_AUD0 && + packetType <= Demuxer::PESTYPE_AUDMAX) + || + (packetType >= Demuxer::PESTYPE_VID0 && + packetType <= Demuxer::PESTYPE_VIDMAX) + || + packetType == Demuxer::PESTYPE_PRIVATE_1 + ) + && size >= 14 && data[7] & 0x80) + { + return ( (ULLONG)(data[ 9] & 0x0E) << 29) | + ( (ULLONG)(data[10]) << 22 ) | + ( (ULLONG)(data[11] & 0xFE) << 14 ) | + ( (ULLONG)(data[12]) << 7 ) | + ( (ULLONG)(data[13] & 0xFE) >> 1 ); + } + else return PTS_INVALID; +} + +UCHAR PESPacket::operator[] (UINT index) const +{ + if (index >= size) + return 0; + else + return data[index]; +} + +UINT PESPacket::findPictureHeader(bool h264) const +{ + if (size < 12) return 0; + UINT pattern = ( ((UINT)data[ 8] << 24) | + ((UINT)data[ 9] << 16) | + ((UINT)data[10] << 8) | + (UINT)data[11] ); + UINT pos = 11; + if (h264) { + + while (pattern != DEMUXER_H264_ACCESS_UNIT) + { + if (++pos >= size) return 0; + pattern = (pattern << 8) | data[pos]; + } + return pos-3; + } else { + while (pattern != DEMUXER_PIC_HEAD) + { + if (++pos >= size) return 0; + pattern = (pattern << 8) | data[pos]; + } + return pos-3; + } +} + +UINT PESPacket::countPictureHeaders(bool h264) const +{ + if (size < 12) return 0; + UINT pattern = ( ((UINT)data[ 8] << 24) | + ((UINT)data[ 9] << 16) | + ((UINT)data[10] << 8) | + (UINT)data[11] ); + UINT pos = 11; + UINT count=0; + if (h264) { + + while (pos= size) + { + seq_header = 0; + return 0; + } + pattern = (pattern << 8) | data[pos]; + } + seq_header = pos - 3; + } + else + { + while (pattern != DEMUXER_SEQ_HEAD) + { + if (++pos >= size) + { + seq_header = 0; + return 0; + } + pattern = (pattern << 8) | data[pos]; + } + seq_header = pos - 3; + } + return seq_header; +} + +// Demuxer methods +Demuxer::Demuxer() +{ + if (instance) return; + instance = this; + initted = false; + callback = NULL; + arcnt = 0; + vid_seeking = aud_seeking = false; + video_pts = audio_pts = 0; + ispre_1_3_19 = false; + packetnum=0; + h264 = false; + fps = 25.0; +} + +Demuxer::~Demuxer() +{ + shutdown(); + instance = NULL; +} + +Demuxer* Demuxer::getInstance() +{ + return instance; +} + +int Demuxer::init(Callback* tcallback, DrainTarget* audio, DrainTarget* video, DrainTarget* teletext, + ULONG demuxMemoryV, ULONG demuxMemoryA, ULONG demuxMemoryT,double infps, DVBSubtitles* tsubtitles) +{ + if (!initted) + { + if ( !videostream.init(video, demuxMemoryV) || + !audiostream.init(audio, demuxMemoryA) || + !teletextstream.init(teletext, demuxMemoryT)) + { + Log::getInstance()->log("Demuxer", Log::CRIT, + "Failed to initialize demuxer"); + shutdown(); + return 0; + } + } + if (teletext) { + isteletextdecoded = true; + } else { + isteletextdecoded = false; + } + fps=infps; + reset(); + initted = true; + subtitles = tsubtitles; + callback = tcallback; + return 1; +} + +void Demuxer::reset() +{ + Log::getInstance()->log("Demuxer", Log::DEBUG, "Reset called"); + flush(); + video_current = audio_current = teletext_current = subtitle_current = -1; + horizontal_size = vertical_size = 0; + aspect_ratio = (enum AspectRatio) 0; + frame_rate = bit_rate = 0; + ispre_1_3_19 = false; + h264 = false; + packetnum=0; + + for (int i = 0; i <= (PESTYPE_AUDMAX - PESTYPE_AUD0); i++) + { + avail_mpaudchan[i] = false; + } + for (int i = 0; i <= (PESTYPE_SUBSTREAM_AC3MAX - PESTYPE_SUBSTREAM_AC30); i++) + { + avail_ac3audchan[i] = false; + } + for (int i = 0; i <= (PESTYPE_SUBSTREAM_DVBSUBTITLEMAX - PESTYPE_SUBSTREAM_DVBSUBTITLE0); i++) + { + avail_dvbsubtitlechan[i] = false; + } +} + +int Demuxer::shutdown() +{ + videostream.shutdown(); + audiostream.shutdown(); + teletextstream.shutdown(); + initted = false; + return 1; +} + +void Demuxer::flush() +{ + Log::getInstance()->log("Demuxer", Log::DEBUG, "Flush called"); + + videostream.flush(); + audiostream.flush(); + teletextstream.flush(); +} + +void Demuxer::flushAudio() +{ + audiostream.flush(); +} + +void Demuxer::seek() +{ + vid_seeking = aud_seeking = true; + video_pts = audio_pts = teletext_pts = 0; +} + +void Demuxer::setAudioStream(int id) +{ + audio_current = id; +} + +void Demuxer::setVideoStream(int id) +{ + video_current = id; +} + +void Demuxer::setTeletextStream(int id) +{ + teletext_current = id; +} + +void Demuxer::setDVBSubtitleStream(int id) +{ + subtitle_current = id; +} + +void Demuxer::setAspectRatio(enum AspectRatio ar) +{ + if (aspect_ratio != ar) + { + Log::getInstance()->log("Demux", Log::DEBUG, + "Aspect ratio difference signalled"); + if (++arcnt > 3) // avoid changing aspect ratio if glitch in signal + { + arcnt = 0; + aspect_ratio = ar; + if (callback) callback->call(this); + } + } + else + arcnt = 0; +} + +bool Demuxer::writeAudio() +{ + return audiostream.drain(); +} + +bool Demuxer::writeVideo() +{ + return videostream.drain(); +} + +bool Demuxer::writeTeletext() +{ + return teletextstream.drain(); +} + +bool Demuxer::submitPacket(PESPacket& packet) +{ + UINT sent = 0; + UCHAR packet_type = packet.getPacketType(); + const UCHAR* packetdata = packet.getData(); + if (packet_type >= PESTYPE_VID0 && packet_type <= PESTYPE_VIDMAX) + { + if (video_current == -1) video_current = packet_type; + if (video_current == packet_type && !vid_seeking) + { + sent = videostream.put(&packetdata[0], packet.getSize(), h264?MPTYPE_VIDEO_H264:MPTYPE_VIDEO_MPEG2,packetnum); + if (sent) packetnum++; + } + else + sent = packet.getSize(); + } + else if (packet_type >= PESTYPE_AUD0 && packet_type <= PESTYPE_AUDMAX) + { + + if (audio_current == -1) audio_current = packet_type; + avail_mpaudchan[packet_type - PESTYPE_AUD0] = true; + if (audio_current == packet_type && !aud_seeking) + { + sent = audiostream.put(&packetdata[0], packet.getSize(), MPTYPE_MPEG_AUDIO,packetnum); + if (sent) packetnum++; + } + else + sent = packet.getSize(); + } + else if (packet_type == PESTYPE_PRIVATE_1 && + packet.getSubstream() >= PESTYPE_SUBSTREAM_AC30 && + packet.getSubstream() <= PESTYPE_SUBSTREAM_AC3MAX) + { + avail_ac3audchan[packet.getSubstream() - PESTYPE_SUBSTREAM_AC30] = true; + if (packet.getSubstream() == audio_current) + { + sent = audiostream.put(&packetdata[0], packet.getSize(), (ispre_1_3_19)? MPTYPE_AC3_PRE13 : MPTYPE_AC3,packetnum); + if (sent) packetnum++; + } + else + { + sent = packet.getSize(); + } + } + else if (packet_type == PESTYPE_PRIVATE_1 && + packet.getSubstream() >= PESTYPE_SUBSTREAM_DVBSUBTITLE0 && + packet.getSubstream() <= PESTYPE_SUBSTREAM_DVBSUBTITLEMAX) + { + avail_dvbsubtitlechan[packet.getSubstream()-PESTYPE_SUBSTREAM_DVBSUBTITLE0]=true; + if (subtitle_current == -1) subtitle_current = packet.getSubstream(); + if (subtitles && packet.getSubstream()==subtitle_current) + { + subtitles->put(packet); + } + sent = packet.getSize(); + } + else if (isteletextdecoded && packet_type == PESTYPE_PRIVATE_1 && + packet.getSubstream() >= PESTYPE_SUBSTREAM_TELETEXT0 && + packet.getSubstream() <= PESTYPE_SUBSTREAM_TELETEXTMAX) + { + + if (teletext_current == -1) teletext_current = packet.getSubstream(); + if (teletext_current == packet.getSubstream()) + { + sent = teletextstream.put(&packetdata[0], packet.getSize(), MPTYPE_TELETEXT,packetnum); + } + else + { + sent = packet.getSize(); + } + } + else + { + sent = packet.getSize(); + } + + if (sent < packet.getSize()) // Stream is full. + return false; + else + return true; +} + +void Demuxer::parsePacketDetails(PESPacket& packet) +{ + if (packet.getPacketType() >= PESTYPE_AUD0 && + packet.getPacketType() <= PESTYPE_AUDMAX) + { + // Extract audio PTS if it exists + if (packet.hasPTS()) + { + audio_pts = packet.getPTS(); + // We continue to seek on the audio if the video PTS that we + // are trying to match is ahead of the audio PTS by at most + // SEEK_THRESHOLD. We consider the possibility of PTS wrap. + if (aud_seeking && !vid_seeking && + !( (video_pts_seek > audio_pts && + video_pts_seek - audio_pts < SEEK_THRESHOLD) + || + (video_pts_seek < audio_pts && + video_pts_seek + (1LL<<33) - audio_pts < SEEK_THRESHOLD) )) + { + aud_seeking = 0; + Log::getInstance()->log("Demuxer", Log::DEBUG, + "Leaving audio sync: Audio PTS = %llu", audio_pts); + } + } + } + else if (packet.getPacketType() == PESTYPE_PRIVATE_1) // Private stream + { + //Inspired by vdr's device.c + int payload_begin = packet[8]+9; + unsigned char substream_id = packet[payload_begin]; + unsigned char substream_type = substream_id & 0xF0; + unsigned char substream_index = substream_id & 0x1F; +pre_1_3_19_Recording: //This is for old recordings stuff and live TV + if (ispre_1_3_19) + { + substream_id = PESTYPE_PRIVATE_1; + substream_type = 0x80; + substream_index = 0; + } + switch (substream_type) + { + case 0x20://SPU + case 0x30://SPU + packet.setSubstream(substream_id); + break; + case 0xA0: //LPCM //not supported yet, is there any LPCM transmissio out there? + break; + case 0x80: //ac3, currently only one ac3 track per recording supported + packet.setSubstream(substream_type+substream_index); + + // Extract audio PTS if it exists + if (packet.hasPTS()) + { + audio_pts = packet.getPTS(); + // We continue to seek on the audio if the video PTS that we + // are trying to match is ahead of the audio PTS by at most + // SEEK_THRESHOLD. We consider the possibility of PTS wrap. + if (aud_seeking && !vid_seeking && + !( (video_pts_seek > audio_pts && + video_pts_seek - audio_pts < SEEK_THRESHOLD) + || + (video_pts_seek < audio_pts && + video_pts_seek + (1LL<<33) - audio_pts < SEEK_THRESHOLD) )) + { + aud_seeking = 0; + Log::getInstance()->log("Demuxer", Log::DEBUG, "Leaving audio sync: Audio PTS = %llu", audio_pts); + } + } + break; + case 0x10: //Teletext Is this correct? + packet.setSubstream(substream_id); + // Extract teletxt PTS if it exists + if (packet.hasPTS()) + { + teletext_pts = packet.getPTS(); + } + break; + default: + if (!ispre_1_3_19) + { + ispre_1_3_19=true; //switching to compat mode and live tv mode + goto pre_1_3_19_Recording; + } + else + { + packet.setSubstream(0); + } + break; + } + } + else if (packet.getPacketType() >= PESTYPE_VID0 && + packet.getPacketType() <= PESTYPE_VIDMAX) + { + // Extract video PTS if it exists + if (packet.hasPTS()) video_pts = packet.getPTS(); + + // If there is a sequence header, extract information + UINT pos = packet.findSeqHeader(h264); + if (pos > 1) + { + if (!h264) { + pos += 4; + if (pos+6 >= packet.getSize()) return; + horizontal_size = ((int)packet[pos] << 4) | ((int)packet[pos+1] >> 4); + + vertical_size = (((int)packet[pos+1] & 0xf) << 8) | (int)packet[pos+2]; + + setAspectRatio((enum AspectRatio)(packet[pos+3] >> 4)); + frame_rate = packet[pos+3] & 0x0f; + if (frame_rate >= 1 && frame_rate <= 8) + frame_rate = FrameRates[frame_rate]; + else + frame_rate = 0; + bit_rate = ((int)packet[pos+4] << 10) | + ((int)packet[pos+5] << 2) | + ((int)packet[pos+6] >> 6); + } + else + { + /* Chris and Mark I know this is ugly, should we move this to a method of PESPacket or to NALUUnit what would be better? + This looks so ugly since the header includes variable length parts and I have to parse through the whole header to get the wanted information*/ + NALUUnit nalu(packet.getData()+pos,packet.getSize()-pos); + profile=nalu.getBits(8); + nalu.getBits(8); //constraints + nalu.getBits(8); //level_idc + nalu.getUe(); //seq_parameter_set_id + int chroma=1; + if (profile==100 || profile==110 || profile==122 || profile==144) + { + chroma=nalu.getUe(); + if (chroma==3) + { + nalu.getBits(1); + } + nalu.getUe(); //bit depth lume + nalu.getUe(); //bit depth chrome + nalu.getBits(1); + if (nalu.getBits(1)) + { + for (int i=0;i<8;i++){ + if (nalu.getBits(1)) + { + if (i<6) + { + UINT lastscale=8; + UINT nextscale=8; + for (int j=0;j<16;j++) { + if (nextscale!=0) { + UINT delta=nalu.getSe(); + nextscale=(lastscale+delta+256)%256; + } + lastscale=(nextscale==0)?lastscale:nextscale; + } + } + else + { + UINT lastscale=8; + UINT nextscale=8; + for (int j=0;j<64;j++) { + if (nextscale!=0) { + UINT delta=nalu.getSe(); + nextscale=(lastscale+delta+256)%256; + } + lastscale=(nextscale==0)?lastscale:nextscale; + } + } + } + } + } + } + int chromunitx=1; + int chromunity=1; + switch (chroma) { + case 0: + chromunitx=chromunity=1; break; + case 1: + chromunitx=chromunity=2; break; + case 2: + chromunitx=2;chromunity=1; break; + case 3: + chromunitx=chromunity=1; break; + }; + + nalu.getUe(); //log2framenum + UINT temp=nalu.getUe(); + if (temp==0) //pict order + nalu.getUe(); + else if (temp==1) { + nalu.getBits(1); + nalu.getSe(); + nalu.getSe(); + UINT temp2=nalu.getUe(); + for (int i=0;ilog("Demuxer", Log::DEBUG, + "Entering audio sync: Video PTS = %llu", video_pts); + Log::getInstance()->log("Demuxer", Log::DEBUG, + "Entering audio sync: Audio PTS = %llu", audio_pts); + } + return; + } + } +} + +UINT Demuxer::stripAudio(UCHAR* buf, UINT len) +{ + UINT read_pos = 0, write_pos = 0; + UINT pattern, packet_length; + if (len < 4) return 0; + pattern = (buf[0] << 16) | (buf[1] << 8) | (buf[2]); + while (read_pos + 7 <= len) + { + pattern = ((pattern & 0xFFFFFF) << 8) | buf[read_pos+3]; + if (pattern < (0x100|PESTYPE_VID0) || pattern > (0x100|PESTYPE_VIDMAX)) + read_pos++; + else + { + packet_length = ((buf[read_pos+4] << 8) | (buf[read_pos+5])) + 6; + if (read_pos + packet_length > len) + read_pos = len; + else + { + if (read_pos != write_pos) + memmove(buf+write_pos, buf+read_pos, packet_length); + read_pos += packet_length; + write_pos += packet_length; + pattern = (buf[read_pos] << 16) | (buf[read_pos+1] << 8) + | (buf[read_pos+2]); + } + } + } + return write_pos; +} + +void Demuxer::changeTimes(UCHAR* buf, UINT len,UINT playtime) +{ + UINT pattern, packet_length; + UINT read_pos = 0; + if (len < 4) return; + pattern = (buf[0] << 16) | (buf[1] << 8) | (buf[2]); + while (read_pos + 7 <= len) + { + pattern = ((pattern & 0xFFFFFF) << 8) | buf[read_pos+3]; + if (pattern < (0x100|PESTYPE_VID0) || pattern > (0x100|PESTYPE_VIDMAX)) + read_pos++; + else + { + packet_length = ((buf[read_pos+4] << 8) | (buf[read_pos+5])) + 6; + // ok we have a packet figure out if pts and dts are present and replace them + if (read_pos + 19 > len) return; + ULLONG new_ts=playtime*90; //play time is on ms so multiply it by 90 + if (buf[read_pos+7] & 0x80) { // pts is here, replace it + buf[read_pos+9]=0x21 | (( new_ts>>29)& 0xde ); + buf[read_pos+10]=0x00 |(( new_ts>>22)& 0xff ); + buf[read_pos+11]=0x01 | (( new_ts>>14)& 0xfe ); + buf[read_pos+12]=0x00 | (( new_ts>>7)& 0xff ); + buf[read_pos+13]=0x01 | (( new_ts<<1)& 0xfe ); + } + + if (buf[read_pos+7] & 0x40) { // pts is here, replace it + buf[read_pos+14]=0x21 | (( new_ts>>29)& 0xde ); + buf[read_pos+15]=0x00 | (( new_ts>>22)& 0xff ); + buf[read_pos+16]=0x01 | (( new_ts>>14)& 0xfe ); + buf[read_pos+17]=0x00 | (( new_ts>>7)& 0xff ); + buf[read_pos+18]=0x01 | (( new_ts<<1)& 0xfe ); + } + read_pos += packet_length; + pattern = (buf[read_pos] << 16) | (buf[read_pos+1] << 8) + | (buf[read_pos+2]); + } + } + +} + +bool Demuxer::scanForVideo(UCHAR* buf, UINT len, bool &ish264) +{ + UINT pos = 3; + UINT pattern; + ish264=false; + if (len < 4) return false; + pattern = (buf[0] << 16) | (buf[1] << 8) | (buf[2]); + while (pos < len) + { + pattern = ((pattern & 0xFFFFFF) << 8) | buf[pos++]; + if (pattern >= (0x100|PESTYPE_VID0) && pattern <= (0x100|PESTYPE_VIDMAX)) + return true; + } + return false; +} + +bool* Demuxer::getmpAudioChannels() +{ + return avail_mpaudchan; +} + +bool* Demuxer::getac3AudioChannels() +{ + return avail_ac3audchan; +} + +bool* Demuxer::getSubtitleChannels() +{ + return avail_dvbsubtitlechan; +} + +int Demuxer::getselSubtitleChannel() +{ + return subtitle_current; +} + +int Demuxer::getselAudioChannel() +{ + return audio_current; +} + + diff --git a/demuxer.h b/demuxer.h index 85c3470..a05f7bc 100644 --- a/demuxer.h +++ b/demuxer.h @@ -1,233 +1,237 @@ -/* - Copyright 2005-2008 Mark Calderbank - Copyright 2007 Marten Richter (AC3 support) - - 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. -*/ - -/* - -Thanks go to Jon Gettler of the MVPMC project and Stephen Rice for -the demuxer in mvpmc. It was used in the creation of this demuxer; -however, no code was copied verbatim. - -*/ - -#ifndef DEMUXER_H -#define DEMUXER_H - -#include "stream.h" -#include "defines.h" - -class DVBSubtitles; -class Callback; -class DrainTarget; - -class PESPacket -{ - public: - PESPacket(); - PESPacket(const PESPacket& packet); - PESPacket& operator=(const PESPacket& packet); - ~PESPacket(); - void init(UCHAR type, UCHAR sub=0); - void truncate(); - int write(const UCHAR* buf, int len); - - UCHAR operator[] (UINT index) const; - // return data[index] if in bounds, else 0 - // so no proper error condition but never mind for now - const UCHAR* getData() const { return data; } - UINT getLength() const { return length; } - UINT getSize() const { return size; } - UCHAR getPacketType() const { return packetType; } - void setSubstream(UCHAR s) { substream = s; } - UCHAR getSubstream() const { return substream; } - ULLONG getPTS() const; - bool hasPTS() const { return (getPTS() != PTS_INVALID); } - - UINT findPictureHeader(bool h264) const; - UINT findSeqHeader(bool h264) const; - UINT countPictureHeaders(bool h264) const; - static const ULLONG PTS_INVALID = (1LL << 33); - protected: - void copyFrom(const PESPacket& packet); - UCHAR * data; - UINT length, size; - UINT data_size; - UCHAR packetType; - UCHAR substream; - UINT mutable seq_header; // 0 = no, 1 = unknown, else = header offset -}; - -class Demuxer -{ - public: - Demuxer(); - virtual ~Demuxer(); - static Demuxer* getInstance(); - int init(Callback* tcallback, DrainTarget* audio, DrainTarget* video, - DrainTarget* teletext, - ULONG demuxMemoryV, ULONG demuxMemoryA, ULONG demuxMemoryT, double fps=25., - DVBSubtitles* tsubtitles=NULL); - virtual void reset(); - virtual void flush(); - void flushAudio(); - void seek(); - void setVideoStream(int id); - //TODO HANS next virtual necessary? - //virtual void setAudioStream(int id); - void setAudioStream(int id); - void setTeletextStream(int id); - void setDVBSubtitleStream(int id); - bool writeAudio(); - bool writeVideo(); - bool writeTeletext(); - - virtual int scan(UCHAR* buf, int len) = 0; - virtual int findPTS(UCHAR* buf, int len, ULLONG* dest) = 0; - virtual int put(UCHAR* buf, int len) = 0; - virtual void setFrameNum(ULONG frame) {} - virtual void setPacketNum(ULONG packet) {} - virtual ULONG getFrameNumFromPTS(ULLONG pts) {return 0;} - virtual ULONG getPacketNum() {return 0;} - - bool* getmpAudioChannels(); //Maybe virtual ? - bool* getac3AudioChannels(); //Maybe virtual ? - bool* getSubtitleChannels(); - int getselAudioChannel(); - int getselSubtitleChannel(); - bool ish264() {return h264;} - void seth264(bool newh264){h264=newh264;} - - int getHorizontalSize() { return horizontal_size; } - int getVerticalSize() { return vertical_size; } - int getAspectRatio() { return aspect_ratio; } - int getFrameRate() { return frame_rate; } - int getBitRate() { return bit_rate; } - ULLONG getVideoPTS() { return video_pts; } - ULLONG getAudioPTS() { return audio_pts; } - - enum AspectRatio - { - ASPECT_4_3 = 2, - ASPECT_16_9 = 3 - }; - - // Remove all data from a buffer apart from video PES packets. - // Returns the length of the reduced data. - // removed *static function*, due to DemuxerTS - virtual UINT stripAudio(UCHAR* buf, UINT len); - - // Scan a buffer to see if video packets are present. - // Returns true if video exists; false if not. - // removed *static function* for h264 detection - static bool scanForVideo(UCHAR* buf, UINT len, bool &ish264); - - enum PESTYPE - { - PESTYPE_PRIVATE_1 = 0xBD, - - PESTYPE_AUD0 = 0xC0, - PESTYPE_AUD1, PESTYPE_AUD2, PESTYPE_AUD3, PESTYPE_AUD4, - PESTYPE_AUD5, PESTYPE_AUD6, PESTYPE_AUD7, PESTYPE_AUD8, - PESTYPE_AUD9, PESTYPE_AUD10, PESTYPE_AUD11, PESTYPE_AUD12, - PESTYPE_AUD13, PESTYPE_AUD14, PESTYPE_AUD15, PESTYPE_AUD16, - PESTYPE_AUD17, PESTYPE_AUD18, PESTYPE_AUD19, PESTYPE_AUD20, - PESTYPE_AUD21, PESTYPE_AUD22, PESTYPE_AUD23, PESTYPE_AUD24, - PESTYPE_AUD25, PESTYPE_AUD26, PESTYPE_AUD27, PESTYPE_AUD28, - PESTYPE_AUD29, PESTYPE_AUD30, PESTYPE_AUD31, - PESTYPE_AUDMAX = PESTYPE_AUD31, - - PESTYPE_VID0 = 0xE0, - PESTYPE_VID1, PESTYPE_VID2, PESTYPE_VID3, PESTYPE_VID4, - PESTYPE_VID5, PESTYPE_VID6, PESTYPE_VID7, PESTYPE_VID8, - PESTYPE_VID9, PESTYPE_VID10, PESTYPE_VID11, PESTYPE_VID12, - PESTYPE_VID13, PESTYPE_VID14, PESTYPE_VID15, - PESTYPE_VIDMAX = PESTYPE_VID15 - }; - enum PESTYPE_SUBSTREAM - { - PESTYPE_SUBSTREAM_TELETEXT0 = 0x10, - PESTYPE_SUBSTREAM_TELETEXT1,PESTYPE_SUBSTREAM_TELETEXT2, PESTYPE_SUBSTREAM_TELETEXT3, - PESTYPE_SUBSTREAM_TELETEXT4,PESTYPE_SUBSTREAM_TELETEXT5,PESTYPE_SUBSTREAM_TELETEXT6, - PESTYPE_SUBSTREAM_TELETEXT7, PESTYPE_SUBSTREAM_TELETEXT8, PESTYPE_SUBSTREAM_TELETEXT9, - PESTYPE_SUBSTREAM_TELETEXT10,PESTYPE_SUBSTREAM_TELETEXT11,PESTYPE_SUBSTREAM_TELETEXT12, - PESTYPE_SUBSTREAM_TELETEXT13,PESTYPE_SUBSTREAM_TELETEXT14,PESTYPE_SUBSTREAM_TELETEXT15, - PESTYPE_SUBSTREAM_TELETEXTMAX=PESTYPE_SUBSTREAM_TELETEXT15, - PESTYPE_SUBSTREAM_DVBSUBTITLE0=0x20, - PESTYPE_SUBSTREAM_DVBSUBTITLE1,PESTYPE_SUBSTREAM_DVBSUBTITLE2,PESTYPE_SUBSTREAM_DVBSUBTITLE3, - PESTYPE_SUBSTREAM_DVBSUBTITLE4,PESTYPE_SUBSTREAM_DVBSUBTITLE5,PESTYPE_SUBSTREAM_DVBSUBTITLE6, - PESTYPE_SUBSTREAM_DVBSUBTITLE7, - PESTYPE_SUBSTREAM_DVBSUBTITLEMAX=PESTYPE_SUBSTREAM_DVBSUBTITLE7, - PESTYPE_SUBSTREAM_AC30 = 0x80, - PESTYPE_SUBSTREAM_AC31,PESTYPE_SUBSTREAM_AC32, PESTYPE_SUBSTREAM_AC33, - PESTYPE_SUBSTREAM_AC34,PESTYPE_SUBSTREAM_AC35,PESTYPE_SUBSTREAM_AC36, - PESTYPE_SUBSTREAM_AC37, - PESTYPE_SUBSTREAM_AC3MAX = PESTYPE_SUBSTREAM_AC37 - }; - - protected: - // Operations on PES packets - bool submitPacket(PESPacket&); - void parsePacketDetails(PESPacket&); - - // General demuxer objects and status indicators - static Demuxer* instance; - Stream videostream; - Stream audiostream; - DVBSubtitles* subtitles; - Stream teletextstream; - int shutdown(); - bool initted; - bool vid_seeking; - bool aud_seeking; - bool h264; - int video_current, audio_current, teletext_current, subtitle_current; - - // Video stream information - void setAspectRatio(enum AspectRatio); - Callback* callback; - - int horizontal_size; - int vertical_size; - int profile; - enum AspectRatio aspect_ratio; - int arcnt; - int frame_rate; - int bit_rate; - ULLONG video_pts; - ULLONG video_pts_seek; - ULLONG audio_pts; - ULLONG teletext_pts; - bool isteletextdecoded; - - - unsigned int packetnum; - - // Constants - static const int FrameRates[9]; - - double fps; - - bool ispre_1_3_19; - bool avail_mpaudchan[PESTYPE_AUDMAX-PESTYPE_AUD0+1]; - bool avail_ac3audchan[PESTYPE_SUBSTREAM_AC3MAX-PESTYPE_SUBSTREAM_AC30+1]; - bool avail_dvbsubtitlechan[PESTYPE_SUBSTREAM_DVBSUBTITLEMAX-PESTYPE_SUBSTREAM_DVBSUBTITLE0+1]; -}; - -#endif +/* + Copyright 2005-2008 Mark Calderbank + Copyright 2007 Marten Richter (AC3 support) + + 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. +*/ + +/* + +Thanks go to Jon Gettler of the MVPMC project and Stephen Rice for +the demuxer in mvpmc. It was used in the creation of this demuxer; +however, no code was copied verbatim. + +*/ + +#ifndef DEMUXER_H +#define DEMUXER_H + +#include "stream.h" +#include "defines.h" + +class DVBSubtitles; +class Callback; +class DrainTarget; + +class PESPacket +{ + public: + PESPacket(); + PESPacket(const PESPacket& packet); + PESPacket& operator=(const PESPacket& packet); + ~PESPacket(); + void init(UCHAR type, UCHAR sub=0); + void truncate(); + int write(const UCHAR* buf, int len); + + UCHAR operator[] (UINT index) const; + // return data[index] if in bounds, else 0 + // so no proper error condition but never mind for now + const UCHAR* getData() const { return data; } + UINT getLength() const { return length; } + UINT getSize() const { return size; } + UCHAR getPacketType() const { return packetType; } + void setSubstream(UCHAR s) { substream = s; } + UCHAR getSubstream() const { return substream; } + ULLONG getPTS() const; + bool hasPTS() const { return (getPTS() != PTS_INVALID); } + + UINT findPictureHeader(bool h264) const; + UINT findSeqHeader(bool h264) const; + UINT countPictureHeaders(bool h264) const; + static const ULLONG PTS_INVALID = (1LL << 33); + + + + protected: + void copyFrom(const PESPacket& packet); + UCHAR * data; + UINT length, size; + UINT data_size; + UCHAR packetType; + UCHAR substream; + UINT mutable seq_header; // 0 = no, 1 = unknown, else = header offset +}; + +class Demuxer +{ + public: + Demuxer(); + virtual ~Demuxer(); + static Demuxer* getInstance(); + int init(Callback* tcallback, DrainTarget* audio, DrainTarget* video, + DrainTarget* teletext, + ULONG demuxMemoryV, ULONG demuxMemoryA, ULONG demuxMemoryT, double fps=25., + DVBSubtitles* tsubtitles=NULL); + virtual void reset(); + virtual void flush(); + void flushAudio(); + void seek(); + void setVideoStream(int id); + //TODO HANS next virtual necessary? + //virtual void setAudioStream(int id); + void setAudioStream(int id); + void setTeletextStream(int id); + void setDVBSubtitleStream(int id); + bool writeAudio(); + bool writeVideo(); + bool writeTeletext(); + + virtual int scan(UCHAR* buf, int len) = 0; + virtual int findPTS(UCHAR* buf, int len, ULLONG* dest) = 0; + virtual int put(UCHAR* buf, int len) = 0; + virtual void setFrameNum(ULONG frame) {} + virtual void setPacketNum(ULONG packet) {} + virtual ULONG getFrameNumFromPTS(ULLONG pts) {return 0;} + virtual ULONG getPacketNum() {return 0;} + + bool* getmpAudioChannels(); //Maybe virtual ? + bool* getac3AudioChannels(); //Maybe virtual ? + bool* getSubtitleChannels(); + int getselAudioChannel(); + int getselSubtitleChannel(); + bool ish264() {return h264;} + void seth264(bool newh264){h264=newh264;} + + int getHorizontalSize() { return horizontal_size; } + int getVerticalSize() { return vertical_size; } + int getAspectRatio() { return aspect_ratio; } + int getFrameRate() { return frame_rate; } + int getBitRate() { return bit_rate; } + ULLONG getVideoPTS() { return video_pts; } + ULLONG getAudioPTS() { return audio_pts; } + + enum AspectRatio + { + ASPECT_4_3 = 2, + ASPECT_16_9 = 3 + }; + + // Remove all data from a buffer apart from video PES packets. + // Returns the length of the reduced data. + // removed *static function*, due to DemuxerTS + virtual UINT stripAudio(UCHAR* buf, UINT len); + void changeTimes(UCHAR* buf, UINT len,UINT playtime); + + // Scan a buffer to see if video packets are present. + // Returns true if video exists; false if not. + // removed *static function* for h264 detection + static bool scanForVideo(UCHAR* buf, UINT len, bool &ish264); + + enum PESTYPE + { + PESTYPE_PRIVATE_1 = 0xBD, + + PESTYPE_AUD0 = 0xC0, + PESTYPE_AUD1, PESTYPE_AUD2, PESTYPE_AUD3, PESTYPE_AUD4, + PESTYPE_AUD5, PESTYPE_AUD6, PESTYPE_AUD7, PESTYPE_AUD8, + PESTYPE_AUD9, PESTYPE_AUD10, PESTYPE_AUD11, PESTYPE_AUD12, + PESTYPE_AUD13, PESTYPE_AUD14, PESTYPE_AUD15, PESTYPE_AUD16, + PESTYPE_AUD17, PESTYPE_AUD18, PESTYPE_AUD19, PESTYPE_AUD20, + PESTYPE_AUD21, PESTYPE_AUD22, PESTYPE_AUD23, PESTYPE_AUD24, + PESTYPE_AUD25, PESTYPE_AUD26, PESTYPE_AUD27, PESTYPE_AUD28, + PESTYPE_AUD29, PESTYPE_AUD30, PESTYPE_AUD31, + PESTYPE_AUDMAX = PESTYPE_AUD31, + + PESTYPE_VID0 = 0xE0, + PESTYPE_VID1, PESTYPE_VID2, PESTYPE_VID3, PESTYPE_VID4, + PESTYPE_VID5, PESTYPE_VID6, PESTYPE_VID7, PESTYPE_VID8, + PESTYPE_VID9, PESTYPE_VID10, PESTYPE_VID11, PESTYPE_VID12, + PESTYPE_VID13, PESTYPE_VID14, PESTYPE_VID15, + PESTYPE_VIDMAX = PESTYPE_VID15 + }; + enum PESTYPE_SUBSTREAM + { + PESTYPE_SUBSTREAM_TELETEXT0 = 0x10, + PESTYPE_SUBSTREAM_TELETEXT1,PESTYPE_SUBSTREAM_TELETEXT2, PESTYPE_SUBSTREAM_TELETEXT3, + PESTYPE_SUBSTREAM_TELETEXT4,PESTYPE_SUBSTREAM_TELETEXT5,PESTYPE_SUBSTREAM_TELETEXT6, + PESTYPE_SUBSTREAM_TELETEXT7, PESTYPE_SUBSTREAM_TELETEXT8, PESTYPE_SUBSTREAM_TELETEXT9, + PESTYPE_SUBSTREAM_TELETEXT10,PESTYPE_SUBSTREAM_TELETEXT11,PESTYPE_SUBSTREAM_TELETEXT12, + PESTYPE_SUBSTREAM_TELETEXT13,PESTYPE_SUBSTREAM_TELETEXT14,PESTYPE_SUBSTREAM_TELETEXT15, + PESTYPE_SUBSTREAM_TELETEXTMAX=PESTYPE_SUBSTREAM_TELETEXT15, + PESTYPE_SUBSTREAM_DVBSUBTITLE0=0x20, + PESTYPE_SUBSTREAM_DVBSUBTITLE1,PESTYPE_SUBSTREAM_DVBSUBTITLE2,PESTYPE_SUBSTREAM_DVBSUBTITLE3, + PESTYPE_SUBSTREAM_DVBSUBTITLE4,PESTYPE_SUBSTREAM_DVBSUBTITLE5,PESTYPE_SUBSTREAM_DVBSUBTITLE6, + PESTYPE_SUBSTREAM_DVBSUBTITLE7, + PESTYPE_SUBSTREAM_DVBSUBTITLEMAX=PESTYPE_SUBSTREAM_DVBSUBTITLE7, + PESTYPE_SUBSTREAM_AC30 = 0x80, + PESTYPE_SUBSTREAM_AC31,PESTYPE_SUBSTREAM_AC32, PESTYPE_SUBSTREAM_AC33, + PESTYPE_SUBSTREAM_AC34,PESTYPE_SUBSTREAM_AC35,PESTYPE_SUBSTREAM_AC36, + PESTYPE_SUBSTREAM_AC37, + PESTYPE_SUBSTREAM_AC3MAX = PESTYPE_SUBSTREAM_AC37 + }; + + protected: + // Operations on PES packets + bool submitPacket(PESPacket&); + void parsePacketDetails(PESPacket&); + + // General demuxer objects and status indicators + static Demuxer* instance; + Stream videostream; + Stream audiostream; + DVBSubtitles* subtitles; + Stream teletextstream; + int shutdown(); + bool initted; + bool vid_seeking; + bool aud_seeking; + bool h264; + int video_current, audio_current, teletext_current, subtitle_current; + + // Video stream information + void setAspectRatio(enum AspectRatio); + Callback* callback; + + int horizontal_size; + int vertical_size; + int profile; + enum AspectRatio aspect_ratio; + int arcnt; + int frame_rate; + int bit_rate; + ULLONG video_pts; + ULLONG video_pts_seek; + ULLONG audio_pts; + ULLONG teletext_pts; + bool isteletextdecoded; + + + unsigned int packetnum; + + // Constants + static const int FrameRates[9]; + + double fps; + + bool ispre_1_3_19; + bool avail_mpaudchan[PESTYPE_AUDMAX-PESTYPE_AUD0+1]; + bool avail_ac3audchan[PESTYPE_SUBSTREAM_AC3MAX-PESTYPE_SUBSTREAM_AC30+1]; + bool avail_dvbsubtitlechan[PESTYPE_SUBSTREAM_DVBSUBTITLEMAX-PESTYPE_SUBSTREAM_DVBSUBTITLE0+1]; +}; + +#endif diff --git a/demuxeraudio.cc b/demuxeraudio.cc index 2c53b59..09c7ea0 100644 --- a/demuxeraudio.cc +++ b/demuxeraudio.cc @@ -1,1248 +1,1248 @@ -/* - Copyright 2006 Mark Calderbank, Andreas Vogel - - 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 "demuxeraudio.h" -#include "audio.h" -#include "i18n.h" -#include "log.h" - -#define HDRBYTE1 0xff -#define HDRBYTE2 0xe0 -#define HDRBYTE2MASK 0xe0 - - - -class PacketBuffer { - - public: - PacketBuffer(Stream *as,UCHAR strtype) { - log=Log::getInstance(); - audio=as; - streamtype=strtype; - newStream(); - } - //just handle the data (do not deal with headers) - int putInternal(UCHAR* buf,int len,unsigned int &packetnum); - void reset(){ - partPacket=0; - bytesWritten=0; - framelen=DemuxerAudio::PACKET_SIZE; - } - void newStream() { - reset(); - numpackets=0; - numbytes=0; - skipfactor=0; - numskip=0; - } - bool bufferFull() { - return (partPacket>=framelen); - } - //can we write a new packet? - bool bufferEmpty() { - return partPacket==0; - } - //only set this, if buffer empty - //otherwise ignored! - bool setFramelen(int len) { - if (! bufferEmpty() ) return false; - if (len > (int)DemuxerAudio::PACKET_SIZE) return false; - framelen=len; - return true; - } - //how much bytes do we need to fill the packet? - int bytesMissing() { - return framelen-partPacket; - } - int getFramelen() { - return framelen; - } - void setSkipFactor(int factor) { - skipfactor=factor; - numskip=0; - } - private: - void packetWritten() { - numbytes+=framelen; - //log->log("DemuxerAudio::PacketBuffer",Log::DEBUG,"written packet %ld l=%d, bytes %ld",numpackets,framelen,numbytes); - numpackets++; - reset(); - } - bool doSkip(); - UCHAR store[DemuxerAudio::PACKET_SIZE]; // Storage for partial packets - int partPacket; // Length of partial packet stored from previous put() - int bytesWritten; //if they are !=0 and != framelength the stream is full... - int framelen; - Log * log; - Stream * audio; - UCHAR streamtype; - //global counters - ULONG numpackets; - ULONG numbytes; - int skipfactor; - int numskip; -}; - - -DemuxerAudio::DemuxerAudio(int p_vID, int p_aID) -{ - inSync=false; - isStarting=true; - log=Log::getInstance(); - readHeaders=0; - streamtype=Audio::MP3; - buffer=new PacketBuffer(&audiostream,streamtype); -// buffer=new PacketBuffer(&teststream,streamtype); - globalBytesWritten=0; - packetnum=0; - id3=NULL; - info=NULL; - vbr=NULL; - reset(); -} - -DemuxerAudio::~DemuxerAudio() { - delete buffer; - if(info) delete info; - if(id3) delete id3; - if (vbr) delete vbr; -} - -void DemuxerAudio::flush() -{ - Demuxer::flushAudio(); - buffer->newStream(); - tmpFill=0; -} - -void DemuxerAudio::reset() { - buffer->newStream(); - tmpFill=0; - readHeaders=0; - packetnum=0; - outOfSync=0; - globalBytesWritten=0; - if (id3) delete id3; - id3=NULL; - if (info) delete info; - info=NULL; - if (vbr) delete vbr; - vbr=NULL; - inSync=false; - hasHdrInfo=false; - hasVBRInfo=false; - isStarting=true; - hdrBitrate=128000; - hdrSamplingRate=44100; - avrBitrate=0; - hdrFramelen=0; - isStarting=true; -} - -int DemuxerAudio::scan(UCHAR *buf, int len) -{ - //no differend pids here - return 0; -} - -void DemuxerAudio::setVID(int p_vID) -{ -} - -void DemuxerAudio::setAID(int p_aID,int type) -{ -} - -static char * id3_1_genre[] = { - "Blueshhh", - "Classic Rock", - "Country", - "Dance", - "Disco", - "Funk", - "Grunge", - "Hip-Hop", - "Jazz", - "Metal", - "New Age", - "Oldies", - "Other", - "Pop", - "R&B", - "Rap", - "Reggae", - "Rock", - "Techno", - "Industrial", - "Alternative", - "Ska", - "Death Metal", - "Pranks", - "Soundtrack", - "Euro-Techno", - "Ambient", - "Trip-Hop", - "Vocal", - "Jazz+Funk", - "Fusion", - "Trance", - "Classical", - "Instrumental", - "Acid", - "House", - "Game", - "Sound Clip", - "Gospel", - "Noise", - "AlternRock", - "Bass", - "Soul", - "Punk", - "Space", - "Meditative", - "Instrumental Pop", - "Instrumental Rock", - "Ethnic", - "Gothic", - "Darkwave", - "Techno-Industrial", - "Electronic", - "Pop-Folk", - "Eurodance", - "Dream", - "Southern Rock", - "Comedy", - "Cult", - "Gangsta", - "Top 40", - "Christian Rap", - "Pop/Funk", - "Jungle", - "Native American", - "Cabaret", - "New Wave", - "Psychadelic", - "Rave", - "Showtunes", - "Trailer", - "Lo-Fi", - "Tribal", - "Acid Punk", - "Acid Jazz", - "Polka", - "Retro", - "Musical", - "Rock & Roll", - "Hard Rock" -}; - - - -static int bitrateTable[16][5]={ -/* L1,L2,L3,2L1,2L2 */ -/*0000*/ {-1,-1,-1,-1,-1}, -/*0001*/ {32,32,32,32,8}, -/*0010*/ {64,48,40,48,16}, -/*0011*/ {96,56,48,56,24}, -/*0100*/ {128,64,56,64,32}, -/*0101*/ {160,80,64,80,40}, -/*0110*/ {192,96,80,96,48}, -/*0111*/ {224,112,96,112,56}, -/*1000*/ {256,128,112,128,64}, -/*1001*/ {288,160,128,144,80}, -/*1010*/ {320,192,160,160,96}, -/*1011*/ {352,224,192,176,112}, -/*1100*/ {384,256,224,192,128}, -/*1101*/ {416,320,256,224,144}, -/*1110*/ {448,384,320,256,160}, -/*1111*/ {-1,-1,-1,-1,-1} }; - -static int samplingRateTable[4][3]={ -/*00*/ {44100,22050,11025}, -/*01*/ {48000,24000,12000}, -/*10*/ {32000,16000,8000}, -/*11*/ {-1,-1,-1}}; - -//max 7 char! -static const char * mpegString(UCHAR code) { - switch(code) { - case 0: - return "MPEG2.5"; - case 1: - return "RESERV"; - case 2: - return "MPEG 2"; - case 3: - return "MPEG 1"; - } - return "UNKNOWN"; -} - -static const char * layerString(UCHAR code) { - switch(code) { - case 0: - return "Layer reserved"; - case 1: - return "Layer III"; - case 2: - return "Layer II"; - case 3: - return "Layer I"; - } - return "Layer UNKNOWN"; -} -/** - * parse an id3 Header - * provided by Brian Walton - * @returns -1 of nothing found - */ - -int DemuxerAudio::id3_2_3_FrameParse(unsigned char buf[], id3_frame *frame) -{ - if (buf[0] < 0x20 || buf[1] < 0x20 || buf [2] < 0x20 ) return -1; - frame->size = (buf[4] & 0x7F) << 21 | (buf[5] & 0x7F) << 14 | (buf[6] & 0x7F) << 7 | (buf[7] & 0x7F); - if (frame->size == 0) return -1; - //TODO. clearify flags against: - //http://id3.org/id3v2.3.0#head-697d09c50ed7fa96fb66c6b0a9d93585e2652b0b - frame->flags.tagAlterPreserv = (buf[8] & 0x80) >> 7; - frame->flags.filelterPreserv = (buf[8] & 0x40) >> 6; - frame->flags.readOnly = (buf[8] & 0x20) >> 5; - frame->flags.groupId = (buf[9] & 0x20) >> 5; - frame->flags.compression = (buf[9] & 0x80) >> 7; - frame->flags.encryption = (buf[9] & 0x40) >> 6; - frame->flags.unsync = 0; - frame->flags.dataLen = 0; - return 0; -} - - /** - * parse an id3 Header - * provided by Brian Walton - * @returns -1 of nothing found - */ - -int DemuxerAudio::id3_2_2_FrameParse(unsigned char buf[], id3_frame *frame) -{ - if (buf[0] < 0x20 || buf[1] < 0x20 || buf[2] < 0x20) return -1; - frame->size = (buf[3] & 0x7F) << 14 | (buf[4] & 0x7F) << 7 | (buf[5] & 0x7F); - if (frame->size == 0) return -1; - return 0; -} - - -//fill an id3tag from a frame payload -//http://id3.org/id3v2.3.0#head-697d09c50ed7fa96fb66c6b0a9d93585e2652b0b -//http://id3.org/id3v2-00 -static struct tagid { - const char * bytes; - int index; -} knownFrames[]= { - //ID3V2.3 - {"TIT2",1}, //title - {"TPE1",2}, //artist - {"TCON",3}, //genre - {"TRCK",6}, //track - {"TYER",4}, //year - {"TALB",5}, //album - {"TCOM",7}, //composer - {"COMM",8}, //comment - //Text encoding $xx - //Language $xx xx xx - //Short content descrip. $00 (00) - //The actual text - //ID3V2.0 - {"TT2",1 }, - {"TP1",2 }, - {"TCM",7 }, - {"TCO",3 }, //(genreNumber) - {"TAL",5 }, - {"TRK",6 }, - {"TYE",4 }, - {"COM",8 } -}; -#define NUMKNOWN (sizeof(knownFrames)/sizeof(knownFrames[0])) - -/*fill in infos - from an ID3 V2.x, V2.3 frame into the tags structure - frameData must point to the header - framelen is the len without header (10 Bytes for V23, 6 Bytes for v2x) - */ - -#define MAXLEN(tagtype) ((UINT)frameLentagtype)-1?(UINT)frameLen:sizeof(tag->tagtype)-1) -bool DemuxerAudio::fillId3Tag(id3_tag * tag,UCHAR * frameData, int frameLen, int dataOffset, bool v23) { - int tl=v23?4:3; - int tagIndex=-1; - if (tag == NULL) return false; - if (frameLen < 2) return false; - for (UINT i=0;i< NUMKNOWN;i++) { - if(strncmp((char *)frameData,knownFrames[i].bytes,tl) == 0) { - tagIndex=knownFrames[i].index; - break; - } - } - if (tagIndex < 0) return false; - UCHAR encoding=*(frameData+dataOffset); - dataOffset++; - frameLen--; - if (encoding != 0) { - log->log("DemuxerAudio",Log::DEBUG,"unknown encoding for tag %d, tagid %s",encoding, - knownFrames[tagIndex].bytes); - return false; - } - switch(tagIndex) { - case 1: //title - strncpy(tag->title,(char*)(frameData+dataOffset),MAXLEN(title)); - tag->title[MAXLEN(title)]=0; - break; - case 2: //artist - strncpy(tag->artist,(char*)(frameData+dataOffset),MAXLEN(artist)); - tag->artist[MAXLEN(artist)]=0; - break; - case 3: //genre - { - UCHAR * st=frameData+dataOffset; - int genre=0; - if (*st=='(') { - genre=atoi((const char *)(st+1)) && 31; - st=(UCHAR *)id3_1_genre[genre]; - } - strncpy(tag->genre,(char*)st,MAXLEN(genre)); - tag->genre[MAXLEN(genre)]=0; - break; - } - case 4: //year - strncpy(tag->year,(char *)(frameData+dataOffset),MAXLEN(year)); - tag->year[MAXLEN(year)]=0; - break; - case 5: //album - strncpy(tag->album,(char *)(frameData+dataOffset),MAXLEN(album)); - tag->album[MAXLEN(album)]=0; - break; - case 6: //track - strncpy(tag->track,(char *)(frameData+dataOffset),MAXLEN(track)); - tag->track[MAXLEN(track)]=0; - break; - case 7: //composer - strncpy(tag->composer,(char *)(frameData+dataOffset),MAXLEN(composer)); - tag->composer[MAXLEN(composer)]=0; - break; - case 8: //comment - strncpy(tag->comment,(char *)(frameData+dataOffset),MAXLEN(comment)); - tag->comment[MAXLEN(comment)]=0; - break; - default: - return false; - } - - return true; -} - -/** - * parse an id3 Header - * based on code provided by Brian Walton - * @returns -1 of nothing found - * otherwise the id3 info is filled - */ - -int DemuxerAudio::parseID3V2(UCHAR *data, int len) { - int debug=0; - UCHAR * start=data; - id3_header id3header; - id3_frame id3frame; - id3_tag * id3tag=NULL; - //len = read(fd, data, 10); - if (len < 10) { - delete id3tag; - return -1; - } - len-=10; - if(data[0]=='I' && data[1]=='D' && data[2]=='3') - { - id3tag=new id3_tag(); - id3header.major = data[3]; - id3header.minor = data[4]; - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"ID3 V2.%d.%d found\n", id3header.major, id3header.minor); - id3header.flags.unsynchronisation = (data[5] & 0x80)>>7; - id3header.flags.extended_header = (data[5] & 0x40)>>6; - id3header.flags.experimental = (data[5] & 0x20)>>5; - id3header.flags.footer = (data[5] & 0x10)>>4; - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Unsynchronisation flag: %d\n", id3header.flags.unsynchronisation); - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Extended header flag: %d\n", id3header.flags.extended_header); - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Experimental indicator flag: %d\n", id3header.flags.experimental); - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Footer present flag: %d\n", id3header.flags.footer); - id3header.size = (data[6] & 0x7F) << 21 | (data[7] & 0x7F) << 14 | (data[8] & 0x7F) << 7 | (data[9] & 0x7F); - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"ID3 Size: %d\n", id3header.size); - data=start+10; - if (len <= id3header.size) { - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"header size to big %d, only %d bytes available\n",id3header.size,len); - delete id3tag; - return -1; - } - if (id3header.flags.extended_header) - { - int extended_hdr_hdr=4; //still to be discussed (id3.org...) - //read extended header size - if (len < extended_hdr_hdr) { - if (debug) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"extended header found but cannot read\n"); - delete id3tag; - return -1; - } - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"remaining %d chars after extended hdr hdr\n", len); - id3header.extended_header.size = (data[0] & 0x7F) << 21 | (data[1] & 0x7F) << 14 | (data[2] & 0x7F) << 7 | (data[3] & 0x7F); - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Extended header size: %d\n", id3header.extended_header.size); - if (len <= id3header.extended_header.size+extended_hdr_hdr) { - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"extended Header to big, only %d bytes available\n",len); - delete id3tag; - return -1; - } - //lseek(fd, id3header.extended_header.size - 6, SEEK_CUR); - data+=id3header.extended_header.size+extended_hdr_hdr; - len-=id3header.extended_header.size+extended_hdr_hdr; - } - //set the end of the header - UCHAR * eob=start+id3header.size+10; - bool readNext=true; - while (data < eob && readNext) - { - //skip over some padding - found this in lame MCDI tag... - if (*data == 0) { - data++; - continue; - } - readNext=false; - switch(id3header.major) - { - case 2: //ID3 V2.2.x - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"version 2.2 frame, %d : %c %c %c\n", data-start,*data,*(data+1),*(data+2)); - if (data + 6 >= eob) - { - break; - } - if (id3_2_2_FrameParse(data, &id3frame) < 0) - { - break; - } - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"frame size: %d\n", id3frame.size); - fillId3Tag(id3tag,data,id3frame.size,6,false); - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"frame payload: %s\n", data + 6 +1); - data+=6+id3frame.size; - readNext=true; - break; - case 3: //ID3 V2.3.x - { - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"version 2.3 frame, %d : %c %c %c %c\n", data-start, - *data,*(data+1),*(data+2),*(data+3)); - if (data + 10 >= eob) - { - break; - } - if (id3_2_3_FrameParse(data, &id3frame) <0) - { - break; - } - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Frame size: %d\n", id3frame.size); - int dataOffset=10; - if (id3frame.flags.groupId) - { - dataOffset++; - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Frame group: %d\n", data[dataOffset]); - } - if (id3frame.flags.compression) - { - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Frame compressed: %d\n", id3frame.flags.compression); - } - if (id3frame.flags.encryption) - { - dataOffset+=1; - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Frame encryption method: %d\n", data[dataOffset]); - } - fillId3Tag(id3tag,data,id3frame.size-dataOffset+10,dataOffset,true); - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"frame payload: %s\n", data + dataOffset +1); - data+=10+id3frame.size; - readNext=true; - break; - } - default: - //don't support this version - delete id3tag; - return -1; - } - } - - data=eob; - //store the found tag - if (id3) delete id3; - id3=id3tag; - return data-start; - } - return -1; -} - -/** - * parse an id3v1 Header - * based on code provided by Brian Walton - * @returns -1 of nothing found - * otherwise the id3 info is filled - */ -#define MEMCPY(type,len,offset) {int type##max=sizeof(tag->type)-1type)-1:len;strncpy(tag->type,(char*)&data[offset],type##max);tag->type[type##max]=0;} - -int DemuxerAudio::parseID3V1(UCHAR *data, int len) { - int debug=1; - if (len < 128) return -1; - if(data[0]=='T' && data[1]=='A' && data[2]=='G') - { - id3_tag * tag=new id3_tag(); - if (debug != 0)log->log("DemuxerAudio::parseID3V1",Log::DEBUG,"ID3 V1 tag found\n"); - MEMCPY(title,30,3); - MEMCPY(artist,30,33); - MEMCPY(album,30,63); - MEMCPY(year,4,93); - if (data[125]==0 && data[126]!=0) - { //ID3 V1.1 - if (debug != 0)log->log("DemuxerAudio::parseID3V1",Log::DEBUG,"ID3 V1.1 tag\n"); - MEMCPY(comment,29,97); - sprintf(tag->track, "%d", data[126]); - } else { - if (debug != 0)log->log("DemuxerAudio::parseID3V1",Log::DEBUG,"ID3 V1.0 tag\n"); - MEMCPY(comment,30,97); - } - if (data[127] < sizeof(id3_1_genre)/sizeof(id3_1_genre[0])) - { - sprintf(tag->genre, id3_1_genre[data[127]]); - } - if (id3) delete id3; - id3=tag; - return 0; - } - return -1; -} - -//infos from http://www.multiweb.cz/twoinches/MP3inside.htm -int DemuxerAudio::parseVBR(UCHAR *data, int len) { - UCHAR *hdr=findHeader(data,len); - //we expect the header exactly here - if (hdr != data) return -1; - const static char * VBRHDR="Xing"; - int vbridpos=36; - UCHAR mpgtype=(data[1] & 0x18)>>3; - UCHAR chmode=(data[2] & 0xc0) >> 6; - UCHAR layer=(data[2] & 0x06) >>1; - if ( mpgtype == 3 && chmode == 11) vbridpos=21; - if ( mpgtype != 3 && chmode != 11) vbridpos=21; - if ( mpgtype != 3 && chmode == 11) vbridpos=13; - //check for the header ID - if (vbridpos+(int)strlen(VBRHDR)+4 >= len) { - Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"frame to short for VBR header %d",len); - return -1; - } - for (int i=4;ilog("DemuxerAudio::parseVBR",Log::DEBUG,"garbage when searching VBR header at pos %d",i); - return -1; - } - } - if ( strncmp((char *)&data[vbridpos],VBRHDR,strlen(VBRHDR)) != 0) { - Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"no VBR header at pos %d",vbridpos); - return -1; - } - int framedata=vbridpos+strlen(VBRHDR); - int expectedLen=0; - //OK we should now have a valid vbr header - bool hasFramenum=data[framedata+3] & 1; - bool hasBytes=data[framedata+3] & 0x2; - bool hasTOC=data[framedata+3] & 0x4; - expectedLen+=8; - if (hasTOC) expectedLen+=100; - if (framedata+expectedLen > len) { - Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"frame to short for VBR header data %d, expected %d", - len,framedata+expectedLen); - return -1; - } - if (!hasFramenum || ! hasBytes || ! hasTOC) { - //not usefull for us.. - Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"not all parts in VBR header - ignore"); - return -1; - } - framedata+=4; - if (vbr) delete vbr; - vbr=new vbrInfo(); - vbr->numFrames=data[framedata] << 24 | data[framedata+1]<<16| - data[framedata+2]<<8 |data[framedata+3]; - framedata+=4; - vbr->numBytes=data[framedata] << 24 | data[framedata+1]<<16| - data[framedata+2]<<8 |data[framedata+3]; - framedata+=4; - for (int ti=0;ti<100;ti++) { - vbr->table[ti]=data[framedata+ti]; - } - //compute file size in seconds - //should be (#of frames -1) *samplesPerFrame / sampleRate - //TODO: difference for Mono? - ULONG samplesPerFrame=384; //layer1 - if (layer != 3) samplesPerFrame=1152; - vbr->fileSeconds=(vbr->numFrames-1)*samplesPerFrame/hdrSamplingRate; - Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"successfully read VBR %ldbytes, %ld frames, %ldsec", - vbr->numBytes,vbr->numFrames,vbr->fileSeconds); - return hdrFramelen; -} - - - - - - - -UCHAR * DemuxerAudio::findHeader(UCHAR *buf, int len, bool writeInfo) { - while (len >= 3) //assume hdr+crc - { - UCHAR pattern=*buf; - buf++; len--; - if (pattern != HDRBYTE1 ) continue; - if ((*buf & HDRBYTE2MASK) != HDRBYTE2) continue; - if (readHeader((buf-1),4,writeInfo) != 0) continue; - return buf-1; - } - return NULL; -} - - - -int DemuxerAudio::readHeader(UCHAR * hbuf,int len,bool writeInfo) { - int curFramelen=0; - int curBitrate=0; - int curSamplingRate=0; - if (*hbuf != HDRBYTE1) return -1; - hbuf++; - if ((*hbuf & HDRBYTE2MASK) != HDRBYTE2) return -1; - UCHAR mpgtype=(*hbuf & 0x18)>>3; - if (mpgtype == 1) { - log->log("DemuxerAudio",Log::DEBUG,"header invalid mpgtype %s %i %i %i", - mpegString(mpgtype),*hbuf,*(hbuf+1),*(hbuf+2)); - return 1; - } - UCHAR layer=(*hbuf & 0x06) >>1; - //bool hasCRC=!(*hbuf & 1); - hbuf++; - UCHAR bitrateCode=(*hbuf & 0xf0) >>4; - UCHAR samplingCode=(*hbuf & 0x0c) >> 2; - bool padding=*hbuf & 0x02; - hbuf++; - //0 Stereo, 1 JointStereo, 2 Dual, 3 Mono - UCHAR chmode=(*hbuf & 0xc0) >> 6; - //UCHAR extension=(*hbuf & 0x30) >> 4; - - //layercode: 1-L3, 2-L2, 3-L1 - //columns 0,1,2 for MPEG1 - UCHAR bitrateColumn=3-layer; - if (bitrateColumn > 2) { - log->log("DemuxerAudio",Log::DEBUG,"header invalid layer %s %i %i %i", - layerString(layer),*(hbuf-2),*(hbuf-1),*hbuf); - return 1; - } - if (mpgtype != 3) bitrateColumn+=3; - if (bitrateColumn>4) bitrateColumn=4; - curBitrate=1000*bitrateTable[bitrateCode][bitrateColumn]; - UCHAR sampleRateColumn=0; - if (mpgtype == 10) sampleRateColumn=1; - if (mpgtype == 0) sampleRateColumn=2; - curSamplingRate=samplingRateTable[samplingCode][sampleRateColumn]; - if (curSamplingRate < 0 || curBitrate < 0) { - log->log("DemuxerAudio",Log::DEBUG,"header invalid rates br=%d sr=%d %i %i %i", - curBitrate,curSamplingRate,*(hbuf-2),*(hbuf-1),*hbuf); - return 1; - } - int padbytes=0; - if (padding) { - if (layer == 3) padbytes=4; - else padbytes=1; - } - if (layer == 3) { - //Layer 1 - //FrameLengthInBytes = (12 * BitRate / SampleRate + Padding) * 4 - curFramelen=(12*curBitrate/curSamplingRate+padbytes) * 4; - } - else { - //Layer 2/3 - //FrameLengthInBytes = 144 * BitRate / SampleRate + Padding - curFramelen=144*curBitrate/curSamplingRate+padbytes; - } - //the header itself - if (curFramelen < 32) { - log->log("DemuxerAudio",Log::DEBUG,"read header %ld mpgv=%s lc=%s br=%d sr=%d, fl=%d-invalid %i %i %i", - readHeaders,mpegString(mpgtype),layerString(layer), - curBitrate,curSamplingRate,curFramelen,*(hbuf-2),*(hbuf-1),*hbuf); - return 1; - } - if (writeInfo || isStarting){ - log->log("DemuxerAudio",Log::DEBUG,"read header %ld mpgv=%s lc=%s br=%d sr=%d, fl=%d %i %i %i", - readHeaders,mpegString(mpgtype),layerString(layer), - curBitrate,curSamplingRate,curFramelen,*(hbuf-2),*(hbuf-1),*hbuf); - if (info) delete info; - info=new mpegInfo(); - strcpy(info->mpegVersion,mpegString(mpgtype)); - info->mpegLayer=4-layer; - info->bitRate=curBitrate; - info->avrBitrate=curBitrate; - info->sampleRate=curSamplingRate; - const char *chmodStr=tr("Stereo"); - switch (chmode) { - case 1: chmodStr=tr("JointStereo");break; - case 2: chmodStr=tr("Dual");break; - case 3: chmodStr=tr("Mono");break; - } - SNPRINTF(info->info,sizeof(info->info)-1,"%s",chmodStr); - } - if (isStarting) avrBitrate=curBitrate; - isStarting=false; - readHeaders++; - //moving average F=0.005 - avrBitrate=avrBitrate+((5*(curBitrate-avrBitrate))/1024); - hdrBitrate=curBitrate; - hdrFramelen=curFramelen; - hdrSamplingRate=curSamplingRate; - hasHdrInfo=true; - return 0; -} - -int DemuxerAudio::findPTS(UCHAR* buf, int len, ULLONG* dest) -{ - //we have no PTS number ... - *dest=0; - return (findHeader(buf,len) != NULL)?1:0; -} - -bool PacketBuffer::doSkip() { - if (!bufferFull()) return false; - if (skipfactor == 0) return false; - numskip++; - if (numskip >= skipfactor) { - //sent at least always 2 packets - if (numskip > skipfactor) numskip=0; - return false; - } - packetWritten(); - return true; -} - -// just handle the real stream without dealing with the header -int PacketBuffer::putInternal(UCHAR * wbuf, int len,unsigned int &packetnum) -{ - /* Important, the type passed to stream must be a mediapacket type as defined in - Draintarget.h and not the device setting of the mvp, so we have to translate it here, - in order to get it working on windows - --MR */ - UCHAR mptype=0; - switch (streamtype) { - case Audio::MPEG1_PES: //? - case Audio::MPEG2_PES: //Important, this must be a PES ! - mptype=MPTYPE_MPEG_AUDIO; break; - default: - case Audio::MP3: //this can be any Mpeg1 Audio not only layer 3 not packed into PES - mptype=MPTYPE_MPEG_AUDIO_LAYER3;break; - }; - if (bufferFull()) { -#ifndef WIN32 - if (doSkip()) return 0;//NoSkip on Windows -#endif - //we are still full - so try to write - int sent=audio->put(store+bytesWritten,framelen-bytesWritten,/*streamtype*/mptype,packetnum);packetnum++; - //log->log("DemuxerAudio::PacketBuffer",Log::DEBUG,"written %d bytes to stream (still full) pp=%d, framelen=%d, written=%d",sent,partPacket,framelen, bytesWritten ); - if (sent < (framelen - bytesWritten)) { - //packet still not written - bytesWritten+=sent; - return 0; - } - packetWritten(); - //let the demuxer come back with the rest - need to check header first - return 0; - } - if (partPacket+len >= framelen) { - //now we have at least a complete packet - int bytesConsumed=framelen-partPacket; - memcpy(store+partPacket,wbuf,bytesConsumed); - partPacket=framelen; - //log->log("DemuxerAudio::PacketBuffer",Log::DEBUG,"stored packet %ld, length %d (last %d) for stream %p",numpackets,framelen,bytesConsumed,audio ); -#ifndef WIN32 //No Skip on Windows - if (doSkip()) return bytesConsumed; -#endif - int sent=audio->put(store,framelen,mptype,packetnum);packetnum++; - bytesWritten+=sent; - //log->log("DemuxerAudio::PacketBuffer",Log::DEBUG,"written %d bytes to stream",sent ); - if (bytesWritten < framelen) { - //still not completely written - return bytesConsumed; - } - packetWritten(); - //let the player come back... - return bytesConsumed; - } - //OK packet still not complete - if (len == 0) return 0; - int bytesConsumed=len; - memcpy(store+partPacket,wbuf,bytesConsumed); - partPacket+=bytesConsumed; - return bytesConsumed; -} - -/** - major entry for data from a player - the demuxer is either in the state headerSearch (packet written or - just at the beginning), writing garbage (inSync=false) or - handling data (none set) - A header is expected at the first byte after the previous packet - - otherwise we switch to garbage where we always search for a header - (but anyway provide the data to the underlying device - it's probably - more intelligent then we are... - We only loose a correct position display. - */ -int DemuxerAudio::put(UCHAR* wbuf, int len) -{ - //return audiostream.put(wbuf,len,streamtype); - int framelen=PACKET_SIZE; - UCHAR *hdr; - int bytesConsumed=0; - int oldBytes=0; - if (tmpFill != 0 || (buffer->bufferEmpty() && len < HDRLEN)) { - //OK we have to copy everything to the tmp buffer - int cp=(UINT)len<(PACKET_SIZE-tmpFill)?(UINT)len:(PACKET_SIZE-tmpFill); - memcpy(&tmpBuffer[tmpFill],wbuf,cp); - oldBytes=tmpFill; - tmpFill+=cp; - wbuf=tmpBuffer; - len=tmpFill; - //if there is no header here and our buffer - //is empty - just wait for the next header - if (len < HDRLEN && buffer->bufferEmpty()) { - log->log("DemuxerAudio",Log::DEBUG,"len to small for header %d at bytes %ld",len,globalBytesWritten); - return cp; - } - } - while (bytesConsumed < len ) { - if (buffer->bufferFull()) { - //if this is the first part of the loop, try to write to the stream - if (bytesConsumed == 0) buffer->putInternal(wbuf,0,packetnum); - //if the buffer is full, no need to continue - if (buffer->bufferFull()) break; - } - //either we are in a packet (buffer != full && buffer != empty) - //or we are searching a header - if (buffer->bufferEmpty()) { - if (len-bytesConsumed < HDRLEN) { - // we cannot still search - if (tmpFill != 0) { - //we are already working at the buffer - break; - } - memcpy(tmpBuffer,wbuf,len-bytesConsumed); - tmpFill=len-bytesConsumed; - log->log("DemuxerAudio",Log::DEBUG,"len to small for header %d at bytes %ld",len,globalBytesWritten); - return len; - } - - int lastFramelen=hdrFramelen; - //if the header has been valid before and we are searching - //it should be here - int maxsearch=((len-bytesConsumed) < (int)PACKET_SIZE)?len-bytesConsumed:(int)PACKET_SIZE; - hdr=findHeader(wbuf,maxsearch); - if (hdr != NULL) { - //log->log("DemuxerAudio",Log::DEBUG,"header found at offset %d",hdr-wbuf); - } - //hdr now points to the new header - if (hdr != wbuf) { - //still at least bytes before the header - inSync=false; - outOfSync++; - int garbageBytes=(hdr!=NULL)?(hdr-wbuf):maxsearch; - //we consider the garbage now as an own packet - //we can consume at most our buffer - //basically the buffer should be empty here (we are - //in header search - and we fill up each garbage - - int nframesize=garbageBytes; - if (nframesize > (int)PACKET_SIZE) { - nframesize=(int)PACKET_SIZE; - } - if (! buffer->bufferEmpty()) { - log->log("DemuxerAudio",Log::WARN,"buffer not empty when having garbage to store"); - //at the end no big problem, we only write the remaining bytes that we have... - } - else { - buffer->setFramelen(nframesize); - } - log->log("DemuxerAudio",Log::DEBUG,"garbage found at packet %ld (bytes %ld) of length %d, " - "framelen set to %d (last fl=%d)", - readHeaders,globalBytesWritten,garbageBytes,buffer->getFramelen(),lastFramelen); -#ifndef WIN32 - //hmm - we assume that he low level driver is more intelligent - //and give him the data "as is" - int written=buffer->putInternal(wbuf,garbageBytes,packetnum); - globalBytesWritten+=written; - bytesConsumed+=written; - if (written != garbageBytes || hdr == NULL ) { - break; - } -#else //DS is not intelligent - globalBytesWritten+=garbageBytes; - bytesConsumed+=garbageBytes; -#endif - //OK either all the "garbage" is written or - //we found the next header as expected - //continue with the next package - wbuf=hdr; - } - //we have to wait now until the buffer is - //free for the next package - if ( ! buffer->bufferEmpty()) return bytesConsumed; - //this is the place where we start a new packet - framelen=hdrFramelen; - if ( !buffer->setFramelen(framelen) ) { - log->log("DemuxerAudio",Log::DEBUG,"unable to set framelen should=%d, current=%d", - framelen,buffer->getFramelen()); - } - inSync=true; - } - //now we are surely within a packet - int written=buffer->putInternal(wbuf,len-bytesConsumed,packetnum); - //update the status - globalBytesWritten+=written; - wbuf+=written; - bytesConsumed+=written; - if (written == 0) { - //stream is full - break; - } - //OK - handle the rest - } - if (bytesConsumed >= oldBytes) { - //if we consumed more than the old bytes - OK the buffer - //can be thrown away - tmpFill=0; - return bytesConsumed-oldBytes; - } - else { - //only consumed bytes from the buffer - if (bytesConsumed == 0) { - //restore the buffer - tmpFill=oldBytes; - return 0; - } - //shift bytes in buffer - for (int i=0;iavrBitrate=avrBitrate; - info->bitRate=hdrBitrate; - return info; -} - -const DemuxerAudio::vbrInfo * DemuxerAudio::getVBRINfo() { - return vbr; -} - -int DemuxerAudio::checkStart(UCHAR *b, int len) { - UCHAR *start=b; - int id3len=parseID3V2(b,len); - if (id3len > 0) { - char * str=id3->toString(); - log->log("DemuxerAudio",Log::DEBUG,"parseID3V2 %d bytes of %d",id3len,len); - log->log("DemuxerAudio",Log::DEBUG,"parseID3V2 found:%s",str); - delete str; - b+=id3len; - len-=id3len; - } - int vbrlen=parseVBR(b,len); - if (vbrlen > 0 ) { - hasVBRInfo=true; - b+=vbrlen; - len-=vbrlen; - } - UCHAR * hdr=findHeader(b,len,true); - if (hdr == NULL) return -1; - return hdr-start; -} - -int DemuxerAudio::checkID3(UCHAR *b, int len) { - if (len != 128) return -1; - int rt=parseID3V1(b,len); - if (rt >= 0) { - char * str=id3->toString(); - log->log("DemuxerAudio",Log::DEBUG,"parseID3V1 found:%s",str); - delete str; - } - return rt; -} - -bool DemuxerAudio::isSync() { - return inSync; -} - -UINT DemuxerAudio::getSyncErrors() { - return outOfSync; -} - -ULONG DemuxerAudio::getBytesPerSecond() -{ - ULONG bps=hdrBitrate; - if (! hasHdrInfo) return 0; - if (hdrBitrate != avrBitrate) { - //we seem to have vbr - if (hasVBRInfo) { - //vbr stuff TODO - bps=avrBitrate; - } - else { - //some guessing - bps=avrBitrate; - } - } - bps=bps/8; - return bps; -} - -ULONG DemuxerAudio::getSecondsFromLen(ULONG len) { - if (! hasHdrInfo) return 0; - if (vbr) { - //first find the index where we are between - //rough starting point: - ULONG idx=100*len/vbr->numBytes; - if (idx >= 100) idx=99; - ULONG idxPos=(vbr->table[idx]) * vbr->numBytes/256; - ULONG pbefore=idxPos; - ULONG pafter=idxPos; - //OK now we know whether we have to go up or down - if (idxPos > len) { - //down - while (idxPos > len && idx > 0) { - pafter=idxPos; - idx--; - idxPos=(vbr->table[idx]) * vbr->numBytes/256; - pbefore=idxPos; - } - //OK we are now before our postion - } - else { - //up - while (idxPos < len && idx < 100 ) { - pbefore=idxPos; - idx++; - idxPos=(vbr->table[idx]) * vbr->numBytes/256; - pafter=idxPos; - } - //after our position - if (idx > 0) idx --; - } - //idx is now the index before our position - //approximate between the 2 points - ULONG idxTime=idx * vbr->fileSeconds/100; - if (pafter == pbefore) return idxTime; - ULONG rt=idxTime+ (len-pbefore)* vbr->fileSeconds/(100 * (pafter-pbefore)) ; - if (rt > vbr -> fileSeconds) return vbr->fileSeconds; - if (rt < idxTime) return idxTime; - return rt; - } - else { - ULONG bps=getBytesPerSecond(); - if (bps == 0) return 0; - return len/bps; - } -} - -ULONG DemuxerAudio::positionFromSeconds(ULONG seconds) { - if (! hasHdrInfo) return 0; - if (vbr) { - ULONG idx=seconds*100/vbr->fileSeconds; - if (idx > 99) idx=99; - ULONG idxPos=vbr->table[idx] * vbr->numBytes/256; - ULONG idx2=idx; - if (idx < 99) idx2++; - ULONG nextPos=vbr->table[idx] * vbr->numBytes/256; - ULONG rt=idxPos+(nextPos-idxPos) * (seconds - idx * vbr->fileSeconds /256); - if (rt < idxPos) return idxPos; - if ( rt > vbr->numBytes) return vbr->numBytes; - return rt; - } - else { - ULONG bps=getBytesPerSecond(); - return bps*seconds; - } -} - -int id3_tag::stringlen(bool withTitle) const { - if (!withTitle) - return strlen(artist)+strlen(genre)+strlen(year)+strlen(album)+ - strlen(track)+strlen(composer)+strlen(comment)+8+3+ - 90; //30 chars for each name... - else - return strlen(title)+strlen(artist)+strlen(genre)+strlen(year)+strlen(album)+ - strlen(track)+strlen(composer)+strlen(comment)+8+3+ - 120; //30 chars for each name... -} -//create a string out of the id3 tag info -//delete this string afterwards if you did not provide the buffer -char * id3_tag::toString(char *b, int len, bool withTitle) const { - const char *ta=tr("Artist"); -//char *tg=tr("Genre"); -//char *ty=tr("Year"); - const char *tx=tr("Album"); -//char *to=tr("Composer"); -//char *tc=tr("Comment"); - const char *tn=tr("Track"); - /* in the moment: - Title: - Artist: - Album: year - name - Track: - */ - if (b == NULL) { - len=stringlen(withTitle); - b=new char[len]; - } - const char * del=" - "; - if (strlen(year) == 0) del=""; - if (withTitle){ - const char *tt=tr("Title"); - SNPRINTF(b,len-1,"%s: %s\n%s: %s\n%s: %s%s%s\n%s: %s", - tt,title,ta,artist,tx,year,del,album,tn,track); - } - else { -SNPRINTF(b,len-1,"%s: %s\n%s: %s%s%s\n%s: %s", - ta,artist,tx,year,del,album,tn,track); - } - b[len-1]=0; - return b; -} - -void DemuxerAudio::setSkipFactor(int factor) { - Log::getInstance()->log("DemuxerAudio",Log::DEBUG,"set skipfactor %d\n",factor); - buffer->setSkipFactor(factor); -} - +/* + Copyright 2006 Mark Calderbank, Andreas Vogel + + 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 "demuxeraudio.h" +#include "audio.h" +#include "i18n.h" +#include "log.h" + +#define HDRBYTE1 0xff +#define HDRBYTE2 0xe0 +#define HDRBYTE2MASK 0xe0 + + + +class PacketBuffer { + + public: + PacketBuffer(Stream *as,UCHAR strtype) { + log=Log::getInstance(); + audio=as; + streamtype=strtype; + newStream(); + } + //just handle the data (do not deal with headers) + int putInternal(UCHAR* buf,int len,unsigned int &packetnum); + void reset(){ + partPacket=0; + bytesWritten=0; + framelen=DemuxerAudio::PACKET_SIZE; + } + void newStream() { + reset(); + numpackets=0; + numbytes=0; + skipfactor=0; + numskip=0; + } + bool bufferFull() { + return (partPacket>=framelen); + } + //can we write a new packet? + bool bufferEmpty() { + return partPacket==0; + } + //only set this, if buffer empty + //otherwise ignored! + bool setFramelen(int len) { + if (! bufferEmpty() ) return false; + if (len > (int)DemuxerAudio::PACKET_SIZE) return false; + framelen=len; + return true; + } + //how much bytes do we need to fill the packet? + int bytesMissing() { + return framelen-partPacket; + } + int getFramelen() { + return framelen; + } + void setSkipFactor(int factor) { + skipfactor=factor; + numskip=0; + } + private: + void packetWritten() { + numbytes+=framelen; + //log->log("DemuxerAudio::PacketBuffer",Log::DEBUG,"written packet %ld l=%d, bytes %ld",numpackets,framelen,numbytes); + numpackets++; + reset(); + } + bool doSkip(); + UCHAR store[DemuxerAudio::PACKET_SIZE]; // Storage for partial packets + int partPacket; // Length of partial packet stored from previous put() + int bytesWritten; //if they are !=0 and != framelength the stream is full... + int framelen; + Log * log; + Stream * audio; + UCHAR streamtype; + //global counters + ULONG numpackets; + ULONG numbytes; + int skipfactor; + int numskip; +}; + + +DemuxerAudio::DemuxerAudio(int p_vID, int p_aID) +{ + inSync=false; + isStarting=true; + log=Log::getInstance(); + readHeaders=0; + streamtype=Audio::MP3; + buffer=new PacketBuffer(&audiostream,streamtype); +// buffer=new PacketBuffer(&teststream,streamtype); + globalBytesWritten=0; + packetnum=0; + id3=NULL; + info=NULL; + vbr=NULL; + reset(); +} + +DemuxerAudio::~DemuxerAudio() { + delete buffer; + if(info) delete info; + if(id3) delete id3; + if (vbr) delete vbr; +} + +void DemuxerAudio::flush() +{ + Demuxer::flushAudio(); + buffer->newStream(); + tmpFill=0; +} + +void DemuxerAudio::reset() { + buffer->newStream(); + tmpFill=0; + readHeaders=0; + packetnum=0; + outOfSync=0; + globalBytesWritten=0; + if (id3) delete id3; + id3=NULL; + if (info) delete info; + info=NULL; + if (vbr) delete vbr; + vbr=NULL; + inSync=false; + hasHdrInfo=false; + hasVBRInfo=false; + isStarting=true; + hdrBitrate=128000; + hdrSamplingRate=44100; + avrBitrate=0; + hdrFramelen=0; + isStarting=true; +} + +int DemuxerAudio::scan(UCHAR *buf, int len) +{ + //no differend pids here + return 0; +} + +void DemuxerAudio::setVID(int p_vID) +{ +} + +void DemuxerAudio::setAID(int p_aID,int type) +{ +} + +static const char * id3_1_genre[] = { + "Blueshhh", + "Classic Rock", + "Country", + "Dance", + "Disco", + "Funk", + "Grunge", + "Hip-Hop", + "Jazz", + "Metal", + "New Age", + "Oldies", + "Other", + "Pop", + "R&B", + "Rap", + "Reggae", + "Rock", + "Techno", + "Industrial", + "Alternative", + "Ska", + "Death Metal", + "Pranks", + "Soundtrack", + "Euro-Techno", + "Ambient", + "Trip-Hop", + "Vocal", + "Jazz+Funk", + "Fusion", + "Trance", + "Classical", + "Instrumental", + "Acid", + "House", + "Game", + "Sound Clip", + "Gospel", + "Noise", + "AlternRock", + "Bass", + "Soul", + "Punk", + "Space", + "Meditative", + "Instrumental Pop", + "Instrumental Rock", + "Ethnic", + "Gothic", + "Darkwave", + "Techno-Industrial", + "Electronic", + "Pop-Folk", + "Eurodance", + "Dream", + "Southern Rock", + "Comedy", + "Cult", + "Gangsta", + "Top 40", + "Christian Rap", + "Pop/Funk", + "Jungle", + "Native American", + "Cabaret", + "New Wave", + "Psychadelic", + "Rave", + "Showtunes", + "Trailer", + "Lo-Fi", + "Tribal", + "Acid Punk", + "Acid Jazz", + "Polka", + "Retro", + "Musical", + "Rock & Roll", + "Hard Rock" +}; + + + +static int bitrateTable[16][5]={ +/* L1,L2,L3,2L1,2L2 */ +/*0000*/ {-1,-1,-1,-1,-1}, +/*0001*/ {32,32,32,32,8}, +/*0010*/ {64,48,40,48,16}, +/*0011*/ {96,56,48,56,24}, +/*0100*/ {128,64,56,64,32}, +/*0101*/ {160,80,64,80,40}, +/*0110*/ {192,96,80,96,48}, +/*0111*/ {224,112,96,112,56}, +/*1000*/ {256,128,112,128,64}, +/*1001*/ {288,160,128,144,80}, +/*1010*/ {320,192,160,160,96}, +/*1011*/ {352,224,192,176,112}, +/*1100*/ {384,256,224,192,128}, +/*1101*/ {416,320,256,224,144}, +/*1110*/ {448,384,320,256,160}, +/*1111*/ {-1,-1,-1,-1,-1} }; + +static int samplingRateTable[4][3]={ +/*00*/ {44100,22050,11025}, +/*01*/ {48000,24000,12000}, +/*10*/ {32000,16000,8000}, +/*11*/ {-1,-1,-1}}; + +//max 7 char! +static const char * mpegString(UCHAR code) { + switch(code) { + case 0: + return "MPEG2.5"; + case 1: + return "RESERV"; + case 2: + return "MPEG 2"; + case 3: + return "MPEG 1"; + } + return "UNKNOWN"; +} + +static const char * layerString(UCHAR code) { + switch(code) { + case 0: + return "Layer reserved"; + case 1: + return "Layer III"; + case 2: + return "Layer II"; + case 3: + return "Layer I"; + } + return "Layer UNKNOWN"; +} +/** + * parse an id3 Header + * provided by Brian Walton + * @returns -1 of nothing found + */ + +int DemuxerAudio::id3_2_3_FrameParse(unsigned char buf[], id3_frame *frame) +{ + if (buf[0] < 0x20 || buf[1] < 0x20 || buf [2] < 0x20 ) return -1; + frame->size = (buf[4] & 0x7F) << 21 | (buf[5] & 0x7F) << 14 | (buf[6] & 0x7F) << 7 | (buf[7] & 0x7F); + if (frame->size == 0) return -1; + //TODO. clearify flags against: + //http://id3.org/id3v2.3.0#head-697d09c50ed7fa96fb66c6b0a9d93585e2652b0b + frame->flags.tagAlterPreserv = (buf[8] & 0x80) >> 7; + frame->flags.filelterPreserv = (buf[8] & 0x40) >> 6; + frame->flags.readOnly = (buf[8] & 0x20) >> 5; + frame->flags.groupId = (buf[9] & 0x20) >> 5; + frame->flags.compression = (buf[9] & 0x80) >> 7; + frame->flags.encryption = (buf[9] & 0x40) >> 6; + frame->flags.unsync = 0; + frame->flags.dataLen = 0; + return 0; +} + + /** + * parse an id3 Header + * provided by Brian Walton + * @returns -1 of nothing found + */ + +int DemuxerAudio::id3_2_2_FrameParse(unsigned char buf[], id3_frame *frame) +{ + if (buf[0] < 0x20 || buf[1] < 0x20 || buf[2] < 0x20) return -1; + frame->size = (buf[3] & 0x7F) << 14 | (buf[4] & 0x7F) << 7 | (buf[5] & 0x7F); + if (frame->size == 0) return -1; + return 0; +} + + +//fill an id3tag from a frame payload +//http://id3.org/id3v2.3.0#head-697d09c50ed7fa96fb66c6b0a9d93585e2652b0b +//http://id3.org/id3v2-00 +static struct tagid { + const char * bytes; + int index; +} knownFrames[]= { + //ID3V2.3 + {"TIT2",1}, //title + {"TPE1",2}, //artist + {"TCON",3}, //genre + {"TRCK",6}, //track + {"TYER",4}, //year + {"TALB",5}, //album + {"TCOM",7}, //composer + {"COMM",8}, //comment + //Text encoding $xx + //Language $xx xx xx + //Short content descrip. $00 (00) + //The actual text + //ID3V2.0 + {"TT2",1 }, + {"TP1",2 }, + {"TCM",7 }, + {"TCO",3 }, //(genreNumber) + {"TAL",5 }, + {"TRK",6 }, + {"TYE",4 }, + {"COM",8 } +}; +#define NUMKNOWN (sizeof(knownFrames)/sizeof(knownFrames[0])) + +/*fill in infos + from an ID3 V2.x, V2.3 frame into the tags structure + frameData must point to the header + framelen is the len without header (10 Bytes for V23, 6 Bytes for v2x) + */ + +#define MAXLEN(tagtype) ((UINT)frameLentagtype)-1?(UINT)frameLen:sizeof(tag->tagtype)-1) +bool DemuxerAudio::fillId3Tag(id3_tag * tag,UCHAR * frameData, int frameLen, int dataOffset, bool v23) { + int tl=v23?4:3; + int tagIndex=-1; + if (tag == NULL) return false; + if (frameLen < 2) return false; + for (UINT i=0;i< NUMKNOWN;i++) { + if(strncmp((char *)frameData,knownFrames[i].bytes,tl) == 0) { + tagIndex=knownFrames[i].index; + break; + } + } + if (tagIndex < 0) return false; + UCHAR encoding=*(frameData+dataOffset); + dataOffset++; + frameLen--; + if (encoding != 0) { + log->log("DemuxerAudio",Log::DEBUG,"unknown encoding for tag %d, tagid %s",encoding, + knownFrames[tagIndex].bytes); + return false; + } + switch(tagIndex) { + case 1: //title + strncpy(tag->title,(char*)(frameData+dataOffset),MAXLEN(title)); + tag->title[MAXLEN(title)]=0; + break; + case 2: //artist + strncpy(tag->artist,(char*)(frameData+dataOffset),MAXLEN(artist)); + tag->artist[MAXLEN(artist)]=0; + break; + case 3: //genre + { + UCHAR * st=frameData+dataOffset; + int genre=0; + if (*st=='(') { + genre=atoi((const char *)(st+1)) && 31; + st=(UCHAR *)id3_1_genre[genre]; + } + strncpy(tag->genre,(char*)st,MAXLEN(genre)); + tag->genre[MAXLEN(genre)]=0; + break; + } + case 4: //year + strncpy(tag->year,(char *)(frameData+dataOffset),MAXLEN(year)); + tag->year[MAXLEN(year)]=0; + break; + case 5: //album + strncpy(tag->album,(char *)(frameData+dataOffset),MAXLEN(album)); + tag->album[MAXLEN(album)]=0; + break; + case 6: //track + strncpy(tag->track,(char *)(frameData+dataOffset),MAXLEN(track)); + tag->track[MAXLEN(track)]=0; + break; + case 7: //composer + strncpy(tag->composer,(char *)(frameData+dataOffset),MAXLEN(composer)); + tag->composer[MAXLEN(composer)]=0; + break; + case 8: //comment + strncpy(tag->comment,(char *)(frameData+dataOffset),MAXLEN(comment)); + tag->comment[MAXLEN(comment)]=0; + break; + default: + return false; + } + + return true; +} + +/** + * parse an id3 Header + * based on code provided by Brian Walton + * @returns -1 of nothing found + * otherwise the id3 info is filled + */ + +int DemuxerAudio::parseID3V2(UCHAR *data, int len) { + int debug=0; + UCHAR * start=data; + id3_header id3header; + id3_frame id3frame; + id3_tag * id3tag=NULL; + //len = read(fd, data, 10); + if (len < 10) { + delete id3tag; + return -1; + } + len-=10; + if(data[0]=='I' && data[1]=='D' && data[2]=='3') + { + id3tag=new id3_tag(); + id3header.major = data[3]; + id3header.minor = data[4]; + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"ID3 V2.%d.%d found\n", id3header.major, id3header.minor); + id3header.flags.unsynchronisation = (data[5] & 0x80)>>7; + id3header.flags.extended_header = (data[5] & 0x40)>>6; + id3header.flags.experimental = (data[5] & 0x20)>>5; + id3header.flags.footer = (data[5] & 0x10)>>4; + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Unsynchronisation flag: %d\n", id3header.flags.unsynchronisation); + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Extended header flag: %d\n", id3header.flags.extended_header); + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Experimental indicator flag: %d\n", id3header.flags.experimental); + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Footer present flag: %d\n", id3header.flags.footer); + id3header.size = (data[6] & 0x7F) << 21 | (data[7] & 0x7F) << 14 | (data[8] & 0x7F) << 7 | (data[9] & 0x7F); + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"ID3 Size: %d\n", id3header.size); + data=start+10; + if (len <= id3header.size) { + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"header size to big %d, only %d bytes available\n",id3header.size,len); + delete id3tag; + return -1; + } + if (id3header.flags.extended_header) + { + int extended_hdr_hdr=4; //still to be discussed (id3.org...) + //read extended header size + if (len < extended_hdr_hdr) { + if (debug) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"extended header found but cannot read\n"); + delete id3tag; + return -1; + } + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"remaining %d chars after extended hdr hdr\n", len); + id3header.extended_header.size = (data[0] & 0x7F) << 21 | (data[1] & 0x7F) << 14 | (data[2] & 0x7F) << 7 | (data[3] & 0x7F); + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Extended header size: %d\n", id3header.extended_header.size); + if (len <= id3header.extended_header.size+extended_hdr_hdr) { + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"extended Header to big, only %d bytes available\n",len); + delete id3tag; + return -1; + } + //lseek(fd, id3header.extended_header.size - 6, SEEK_CUR); + data+=id3header.extended_header.size+extended_hdr_hdr; + len-=id3header.extended_header.size+extended_hdr_hdr; + } + //set the end of the header + UCHAR * eob=start+id3header.size+10; + bool readNext=true; + while (data < eob && readNext) + { + //skip over some padding - found this in lame MCDI tag... + if (*data == 0) { + data++; + continue; + } + readNext=false; + switch(id3header.major) + { + case 2: //ID3 V2.2.x + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"version 2.2 frame, %d : %c %c %c\n", data-start,*data,*(data+1),*(data+2)); + if (data + 6 >= eob) + { + break; + } + if (id3_2_2_FrameParse(data, &id3frame) < 0) + { + break; + } + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"frame size: %d\n", id3frame.size); + fillId3Tag(id3tag,data,id3frame.size,6,false); + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"frame payload: %s\n", data + 6 +1); + data+=6+id3frame.size; + readNext=true; + break; + case 3: //ID3 V2.3.x + { + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"version 2.3 frame, %d : %c %c %c %c\n", data-start, + *data,*(data+1),*(data+2),*(data+3)); + if (data + 10 >= eob) + { + break; + } + if (id3_2_3_FrameParse(data, &id3frame) <0) + { + break; + } + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Frame size: %d\n", id3frame.size); + int dataOffset=10; + if (id3frame.flags.groupId) + { + dataOffset++; + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Frame group: %d\n", data[dataOffset]); + } + if (id3frame.flags.compression) + { + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Frame compressed: %d\n", id3frame.flags.compression); + } + if (id3frame.flags.encryption) + { + dataOffset+=1; + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Frame encryption method: %d\n", data[dataOffset]); + } + fillId3Tag(id3tag,data,id3frame.size-dataOffset+10,dataOffset,true); + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"frame payload: %s\n", data + dataOffset +1); + data+=10+id3frame.size; + readNext=true; + break; + } + default: + //don't support this version + delete id3tag; + return -1; + } + } + + data=eob; + //store the found tag + if (id3) delete id3; + id3=id3tag; + return data-start; + } + return -1; +} + +/** + * parse an id3v1 Header + * based on code provided by Brian Walton + * @returns -1 of nothing found + * otherwise the id3 info is filled + */ +#define MEMCPY(type,len,offset) {int type##max=sizeof(tag->type)-1type)-1:len;strncpy(tag->type,(char*)&data[offset],type##max);tag->type[type##max]=0;} + +int DemuxerAudio::parseID3V1(UCHAR *data, int len) { + int debug=1; + if (len < 128) return -1; + if(data[0]=='T' && data[1]=='A' && data[2]=='G') + { + id3_tag * tag=new id3_tag(); + if (debug != 0)log->log("DemuxerAudio::parseID3V1",Log::DEBUG,"ID3 V1 tag found\n"); + MEMCPY(title,30,3); + MEMCPY(artist,30,33); + MEMCPY(album,30,63); + MEMCPY(year,4,93); + if (data[125]==0 && data[126]!=0) + { //ID3 V1.1 + if (debug != 0)log->log("DemuxerAudio::parseID3V1",Log::DEBUG,"ID3 V1.1 tag\n"); + MEMCPY(comment,29,97); + sprintf(tag->track, "%d", data[126]); + } else { + if (debug != 0)log->log("DemuxerAudio::parseID3V1",Log::DEBUG,"ID3 V1.0 tag\n"); + MEMCPY(comment,30,97); + } + if (data[127] < sizeof(id3_1_genre)/sizeof(id3_1_genre[0])) + { + sprintf(tag->genre, id3_1_genre[data[127]]); + } + if (id3) delete id3; + id3=tag; + return 0; + } + return -1; +} + +//infos from http://www.multiweb.cz/twoinches/MP3inside.htm +int DemuxerAudio::parseVBR(UCHAR *data, int len) { + UCHAR *hdr=findHeader(data,len); + //we expect the header exactly here + if (hdr != data) return -1; + const static char * VBRHDR="Xing"; + int vbridpos=36; + UCHAR mpgtype=(data[1] & 0x18)>>3; + UCHAR chmode=(data[2] & 0xc0) >> 6; + UCHAR layer=(data[2] & 0x06) >>1; + if ( mpgtype == 3 && chmode == 11) vbridpos=21; + if ( mpgtype != 3 && chmode != 11) vbridpos=21; + if ( mpgtype != 3 && chmode == 11) vbridpos=13; + //check for the header ID + if (vbridpos+(int)strlen(VBRHDR)+4 >= len) { + Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"frame to short for VBR header %d",len); + return -1; + } + for (int i=4;ilog("DemuxerAudio::parseVBR",Log::DEBUG,"garbage when searching VBR header at pos %d",i); + return -1; + } + } + if ( strncmp((char *)&data[vbridpos],VBRHDR,strlen(VBRHDR)) != 0) { + Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"no VBR header at pos %d",vbridpos); + return -1; + } + int framedata=vbridpos+strlen(VBRHDR); + int expectedLen=0; + //OK we should now have a valid vbr header + bool hasFramenum=data[framedata+3] & 1; + bool hasBytes=data[framedata+3] & 0x2; + bool hasTOC=data[framedata+3] & 0x4; + expectedLen+=8; + if (hasTOC) expectedLen+=100; + if (framedata+expectedLen > len) { + Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"frame to short for VBR header data %d, expected %d", + len,framedata+expectedLen); + return -1; + } + if (!hasFramenum || ! hasBytes || ! hasTOC) { + //not usefull for us.. + Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"not all parts in VBR header - ignore"); + return -1; + } + framedata+=4; + if (vbr) delete vbr; + vbr=new vbrInfo(); + vbr->numFrames=data[framedata] << 24 | data[framedata+1]<<16| + data[framedata+2]<<8 |data[framedata+3]; + framedata+=4; + vbr->numBytes=data[framedata] << 24 | data[framedata+1]<<16| + data[framedata+2]<<8 |data[framedata+3]; + framedata+=4; + for (int ti=0;ti<100;ti++) { + vbr->table[ti]=data[framedata+ti]; + } + //compute file size in seconds + //should be (#of frames -1) *samplesPerFrame / sampleRate + //TODO: difference for Mono? + ULONG samplesPerFrame=384; //layer1 + if (layer != 3) samplesPerFrame=1152; + vbr->fileSeconds=(vbr->numFrames-1)*samplesPerFrame/hdrSamplingRate; + Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"successfully read VBR %ldbytes, %ld frames, %ldsec", + vbr->numBytes,vbr->numFrames,vbr->fileSeconds); + return hdrFramelen; +} + + + + + + + +UCHAR * DemuxerAudio::findHeader(UCHAR *buf, int len, bool writeInfo) { + while (len >= 3) //assume hdr+crc + { + UCHAR pattern=*buf; + buf++; len--; + if (pattern != HDRBYTE1 ) continue; + if ((*buf & HDRBYTE2MASK) != HDRBYTE2) continue; + if (readHeader((buf-1),4,writeInfo) != 0) continue; + return buf-1; + } + return NULL; +} + + + +int DemuxerAudio::readHeader(UCHAR * hbuf,int len,bool writeInfo) { + int curFramelen=0; + int curBitrate=0; + int curSamplingRate=0; + if (*hbuf != HDRBYTE1) return -1; + hbuf++; + if ((*hbuf & HDRBYTE2MASK) != HDRBYTE2) return -1; + UCHAR mpgtype=(*hbuf & 0x18)>>3; + if (mpgtype == 1) { + log->log("DemuxerAudio",Log::DEBUG,"header invalid mpgtype %s %i %i %i", + mpegString(mpgtype),*hbuf,*(hbuf+1),*(hbuf+2)); + return 1; + } + UCHAR layer=(*hbuf & 0x06) >>1; + //bool hasCRC=!(*hbuf & 1); + hbuf++; + UCHAR bitrateCode=(*hbuf & 0xf0) >>4; + UCHAR samplingCode=(*hbuf & 0x0c) >> 2; + bool padding=*hbuf & 0x02; + hbuf++; + //0 Stereo, 1 JointStereo, 2 Dual, 3 Mono + UCHAR chmode=(*hbuf & 0xc0) >> 6; + //UCHAR extension=(*hbuf & 0x30) >> 4; + + //layercode: 1-L3, 2-L2, 3-L1 + //columns 0,1,2 for MPEG1 + UCHAR bitrateColumn=3-layer; + if (bitrateColumn > 2) { + log->log("DemuxerAudio",Log::DEBUG,"header invalid layer %s %i %i %i", + layerString(layer),*(hbuf-2),*(hbuf-1),*hbuf); + return 1; + } + if (mpgtype != 3) bitrateColumn+=3; + if (bitrateColumn>4) bitrateColumn=4; + curBitrate=1000*bitrateTable[bitrateCode][bitrateColumn]; + UCHAR sampleRateColumn=0; + if (mpgtype == 10) sampleRateColumn=1; + if (mpgtype == 0) sampleRateColumn=2; + curSamplingRate=samplingRateTable[samplingCode][sampleRateColumn]; + if (curSamplingRate < 0 || curBitrate < 0) { + log->log("DemuxerAudio",Log::DEBUG,"header invalid rates br=%d sr=%d %i %i %i", + curBitrate,curSamplingRate,*(hbuf-2),*(hbuf-1),*hbuf); + return 1; + } + int padbytes=0; + if (padding) { + if (layer == 3) padbytes=4; + else padbytes=1; + } + if (layer == 3) { + //Layer 1 + //FrameLengthInBytes = (12 * BitRate / SampleRate + Padding) * 4 + curFramelen=(12*curBitrate/curSamplingRate+padbytes) * 4; + } + else { + //Layer 2/3 + //FrameLengthInBytes = 144 * BitRate / SampleRate + Padding + curFramelen=144*curBitrate/curSamplingRate+padbytes; + } + //the header itself + if (curFramelen < 32) { + log->log("DemuxerAudio",Log::DEBUG,"read header %ld mpgv=%s lc=%s br=%d sr=%d, fl=%d-invalid %i %i %i", + readHeaders,mpegString(mpgtype),layerString(layer), + curBitrate,curSamplingRate,curFramelen,*(hbuf-2),*(hbuf-1),*hbuf); + return 1; + } + if (writeInfo || isStarting){ + log->log("DemuxerAudio",Log::DEBUG,"read header %ld mpgv=%s lc=%s br=%d sr=%d, fl=%d %i %i %i", + readHeaders,mpegString(mpgtype),layerString(layer), + curBitrate,curSamplingRate,curFramelen,*(hbuf-2),*(hbuf-1),*hbuf); + if (info) delete info; + info=new mpegInfo(); + strcpy(info->mpegVersion,mpegString(mpgtype)); + info->mpegLayer=4-layer; + info->bitRate=curBitrate; + info->avrBitrate=curBitrate; + info->sampleRate=curSamplingRate; + const char *chmodStr=tr("Stereo"); + switch (chmode) { + case 1: chmodStr=tr("JointStereo");break; + case 2: chmodStr=tr("Dual");break; + case 3: chmodStr=tr("Mono");break; + } + SNPRINTF(info->info,sizeof(info->info)-1,"%s",chmodStr); + } + if (isStarting) avrBitrate=curBitrate; + isStarting=false; + readHeaders++; + //moving average F=0.005 + avrBitrate=avrBitrate+((5*(curBitrate-avrBitrate))/1024); + hdrBitrate=curBitrate; + hdrFramelen=curFramelen; + hdrSamplingRate=curSamplingRate; + hasHdrInfo=true; + return 0; +} + +int DemuxerAudio::findPTS(UCHAR* buf, int len, ULLONG* dest) +{ + //we have no PTS number ... + *dest=0; + return (findHeader(buf,len) != NULL)?1:0; +} + +bool PacketBuffer::doSkip() { + if (!bufferFull()) return false; + if (skipfactor == 0) return false; + numskip++; + if (numskip >= skipfactor) { + //sent at least always 2 packets + if (numskip > skipfactor) numskip=0; + return false; + } + packetWritten(); + return true; +} + +// just handle the real stream without dealing with the header +int PacketBuffer::putInternal(UCHAR * wbuf, int len,unsigned int &packetnum) +{ + /* Important, the type passed to stream must be a mediapacket type as defined in + Draintarget.h and not the device setting of the mvp, so we have to translate it here, + in order to get it working on windows + --MR */ + UCHAR mptype=0; + switch (streamtype) { + case Audio::MPEG1_PES: //? + case Audio::MPEG2_PES: //Important, this must be a PES ! + mptype=MPTYPE_MPEG_AUDIO; break; + default: + case Audio::MP3: //this can be any Mpeg1 Audio not only layer 3 not packed into PES + mptype=MPTYPE_MPEG_AUDIO_LAYER3;break; + }; + if (bufferFull()) { +#ifndef WIN32 + if (doSkip()) return 0;//NoSkip on Windows +#endif + //we are still full - so try to write + int sent=audio->put(store+bytesWritten,framelen-bytesWritten,/*streamtype*/mptype,packetnum);packetnum++; + //log->log("DemuxerAudio::PacketBuffer",Log::DEBUG,"written %d bytes to stream (still full) pp=%d, framelen=%d, written=%d",sent,partPacket,framelen, bytesWritten ); + if (sent < (framelen - bytesWritten)) { + //packet still not written + bytesWritten+=sent; + return 0; + } + packetWritten(); + //let the demuxer come back with the rest - need to check header first + return 0; + } + if (partPacket+len >= framelen) { + //now we have at least a complete packet + int bytesConsumed=framelen-partPacket; + memcpy(store+partPacket,wbuf,bytesConsumed); + partPacket=framelen; + //log->log("DemuxerAudio::PacketBuffer",Log::DEBUG,"stored packet %ld, length %d (last %d) for stream %p",numpackets,framelen,bytesConsumed,audio ); +#ifndef WIN32 //No Skip on Windows + if (doSkip()) return bytesConsumed; +#endif + int sent=audio->put(store,framelen,mptype,packetnum);packetnum++; + bytesWritten+=sent; + //log->log("DemuxerAudio::PacketBuffer",Log::DEBUG,"written %d bytes to stream",sent ); + if (bytesWritten < framelen) { + //still not completely written + return bytesConsumed; + } + packetWritten(); + //let the player come back... + return bytesConsumed; + } + //OK packet still not complete + if (len == 0) return 0; + int bytesConsumed=len; + memcpy(store+partPacket,wbuf,bytesConsumed); + partPacket+=bytesConsumed; + return bytesConsumed; +} + +/** + major entry for data from a player + the demuxer is either in the state headerSearch (packet written or + just at the beginning), writing garbage (inSync=false) or + handling data (none set) + A header is expected at the first byte after the previous packet - + otherwise we switch to garbage where we always search for a header + (but anyway provide the data to the underlying device - it's probably + more intelligent then we are... + We only loose a correct position display. + */ +int DemuxerAudio::put(UCHAR* wbuf, int len) +{ + //return audiostream.put(wbuf,len,streamtype); + int framelen=PACKET_SIZE; + UCHAR *hdr; + int bytesConsumed=0; + int oldBytes=0; + if (tmpFill != 0 || (buffer->bufferEmpty() && len < HDRLEN)) { + //OK we have to copy everything to the tmp buffer + int cp=(UINT)len<(PACKET_SIZE-tmpFill)?(UINT)len:(PACKET_SIZE-tmpFill); + memcpy(&tmpBuffer[tmpFill],wbuf,cp); + oldBytes=tmpFill; + tmpFill+=cp; + wbuf=tmpBuffer; + len=tmpFill; + //if there is no header here and our buffer + //is empty - just wait for the next header + if (len < HDRLEN && buffer->bufferEmpty()) { + log->log("DemuxerAudio",Log::DEBUG,"len to small for header %d at bytes %ld",len,globalBytesWritten); + return cp; + } + } + while (bytesConsumed < len ) { + if (buffer->bufferFull()) { + //if this is the first part of the loop, try to write to the stream + if (bytesConsumed == 0) buffer->putInternal(wbuf,0,packetnum); + //if the buffer is full, no need to continue + if (buffer->bufferFull()) break; + } + //either we are in a packet (buffer != full && buffer != empty) + //or we are searching a header + if (buffer->bufferEmpty()) { + if (len-bytesConsumed < HDRLEN) { + // we cannot still search + if (tmpFill != 0) { + //we are already working at the buffer + break; + } + memcpy(tmpBuffer,wbuf,len-bytesConsumed); + tmpFill=len-bytesConsumed; + log->log("DemuxerAudio",Log::DEBUG,"len to small for header %d at bytes %ld",len,globalBytesWritten); + return len; + } + + int lastFramelen=hdrFramelen; + //if the header has been valid before and we are searching + //it should be here + int maxsearch=((len-bytesConsumed) < (int)PACKET_SIZE)?len-bytesConsumed:(int)PACKET_SIZE; + hdr=findHeader(wbuf,maxsearch); + if (hdr != NULL) { + //log->log("DemuxerAudio",Log::DEBUG,"header found at offset %d",hdr-wbuf); + } + //hdr now points to the new header + if (hdr != wbuf) { + //still at least bytes before the header + inSync=false; + outOfSync++; + int garbageBytes=(hdr!=NULL)?(hdr-wbuf):maxsearch; + //we consider the garbage now as an own packet + //we can consume at most our buffer + //basically the buffer should be empty here (we are + //in header search - and we fill up each garbage + + int nframesize=garbageBytes; + if (nframesize > (int)PACKET_SIZE) { + nframesize=(int)PACKET_SIZE; + } + if (! buffer->bufferEmpty()) { + log->log("DemuxerAudio",Log::WARN,"buffer not empty when having garbage to store"); + //at the end no big problem, we only write the remaining bytes that we have... + } + else { + buffer->setFramelen(nframesize); + } + log->log("DemuxerAudio",Log::DEBUG,"garbage found at packet %ld (bytes %ld) of length %d, " + "framelen set to %d (last fl=%d)", + readHeaders,globalBytesWritten,garbageBytes,buffer->getFramelen(),lastFramelen); +#ifndef WIN32 + //hmm - we assume that he low level driver is more intelligent + //and give him the data "as is" + int written=buffer->putInternal(wbuf,garbageBytes,packetnum); + globalBytesWritten+=written; + bytesConsumed+=written; + if (written != garbageBytes || hdr == NULL ) { + break; + } +#else //DS is not intelligent + globalBytesWritten+=garbageBytes; + bytesConsumed+=garbageBytes; +#endif + //OK either all the "garbage" is written or + //we found the next header as expected + //continue with the next package + wbuf=hdr; + } + //we have to wait now until the buffer is + //free for the next package + if ( ! buffer->bufferEmpty()) return bytesConsumed; + //this is the place where we start a new packet + framelen=hdrFramelen; + if ( !buffer->setFramelen(framelen) ) { + log->log("DemuxerAudio",Log::DEBUG,"unable to set framelen should=%d, current=%d", + framelen,buffer->getFramelen()); + } + inSync=true; + } + //now we are surely within a packet + int written=buffer->putInternal(wbuf,len-bytesConsumed,packetnum); + //update the status + globalBytesWritten+=written; + wbuf+=written; + bytesConsumed+=written; + if (written == 0) { + //stream is full + break; + } + //OK - handle the rest + } + if (bytesConsumed >= oldBytes) { + //if we consumed more than the old bytes - OK the buffer + //can be thrown away + tmpFill=0; + return bytesConsumed-oldBytes; + } + else { + //only consumed bytes from the buffer + if (bytesConsumed == 0) { + //restore the buffer + tmpFill=oldBytes; + return 0; + } + //shift bytes in buffer + for (int i=0;iavrBitrate=avrBitrate; + info->bitRate=hdrBitrate; + return info; +} + +const DemuxerAudio::vbrInfo * DemuxerAudio::getVBRINfo() { + return vbr; +} + +int DemuxerAudio::checkStart(UCHAR *b, int len) { + UCHAR *start=b; + int id3len=parseID3V2(b,len); + if (id3len > 0) { + char * str=id3->toString(); + log->log("DemuxerAudio",Log::DEBUG,"parseID3V2 %d bytes of %d",id3len,len); + log->log("DemuxerAudio",Log::DEBUG,"parseID3V2 found:%s",str); + delete str; + b+=id3len; + len-=id3len; + } + int vbrlen=parseVBR(b,len); + if (vbrlen > 0 ) { + hasVBRInfo=true; + b+=vbrlen; + len-=vbrlen; + } + UCHAR * hdr=findHeader(b,len,true); + if (hdr == NULL) return -1; + return hdr-start; +} + +int DemuxerAudio::checkID3(UCHAR *b, int len) { + if (len != 128) return -1; + int rt=parseID3V1(b,len); + if (rt >= 0) { + char * str=id3->toString(); + log->log("DemuxerAudio",Log::DEBUG,"parseID3V1 found:%s",str); + delete str; + } + return rt; +} + +bool DemuxerAudio::isSync() { + return inSync; +} + +UINT DemuxerAudio::getSyncErrors() { + return outOfSync; +} + +ULONG DemuxerAudio::getBytesPerSecond() +{ + ULONG bps=hdrBitrate; + if (! hasHdrInfo) return 0; + if (hdrBitrate != avrBitrate) { + //we seem to have vbr + if (hasVBRInfo) { + //vbr stuff TODO + bps=avrBitrate; + } + else { + //some guessing + bps=avrBitrate; + } + } + bps=bps/8; + return bps; +} + +ULONG DemuxerAudio::getSecondsFromLen(ULONG len) { + if (! hasHdrInfo) return 0; + if (vbr) { + //first find the index where we are between + //rough starting point: + ULONG idx=100*len/vbr->numBytes; + if (idx >= 100) idx=99; + ULONG idxPos=(vbr->table[idx]) * vbr->numBytes/256; + ULONG pbefore=idxPos; + ULONG pafter=idxPos; + //OK now we know whether we have to go up or down + if (idxPos > len) { + //down + while (idxPos > len && idx > 0) { + pafter=idxPos; + idx--; + idxPos=(vbr->table[idx]) * vbr->numBytes/256; + pbefore=idxPos; + } + //OK we are now before our postion + } + else { + //up + while (idxPos < len && idx < 100 ) { + pbefore=idxPos; + idx++; + idxPos=(vbr->table[idx]) * vbr->numBytes/256; + pafter=idxPos; + } + //after our position + if (idx > 0) idx --; + } + //idx is now the index before our position + //approximate between the 2 points + ULONG idxTime=idx * vbr->fileSeconds/100; + if (pafter == pbefore) return idxTime; + ULONG rt=idxTime+ (len-pbefore)* vbr->fileSeconds/(100 * (pafter-pbefore)) ; + if (rt > vbr -> fileSeconds) return vbr->fileSeconds; + if (rt < idxTime) return idxTime; + return rt; + } + else { + ULONG bps=getBytesPerSecond(); + if (bps == 0) return 0; + return len/bps; + } +} + +ULONG DemuxerAudio::positionFromSeconds(ULONG seconds) { + if (! hasHdrInfo) return 0; + if (vbr) { + ULONG idx=seconds*100/vbr->fileSeconds; + if (idx > 99) idx=99; + ULONG idxPos=vbr->table[idx] * vbr->numBytes/256; + ULONG idx2=idx; + if (idx < 99) idx2++; + ULONG nextPos=vbr->table[idx] * vbr->numBytes/256; + ULONG rt=idxPos+(nextPos-idxPos) * (seconds - idx * vbr->fileSeconds /256); + if (rt < idxPos) return idxPos; + if ( rt > vbr->numBytes) return vbr->numBytes; + return rt; + } + else { + ULONG bps=getBytesPerSecond(); + return bps*seconds; + } +} + +int id3_tag::stringlen(bool withTitle) const { + if (!withTitle) + return strlen(artist)+strlen(genre)+strlen(year)+strlen(album)+ + strlen(track)+strlen(composer)+strlen(comment)+8+3+ + 90; //30 chars for each name... + else + return strlen(title)+strlen(artist)+strlen(genre)+strlen(year)+strlen(album)+ + strlen(track)+strlen(composer)+strlen(comment)+8+3+ + 120; //30 chars for each name... +} +//create a string out of the id3 tag info +//delete this string afterwards if you did not provide the buffer +char * id3_tag::toString(char *b, int len, bool withTitle) const { + const char *ta=tr("Artist"); +//char *tg=tr("Genre"); +//char *ty=tr("Year"); + const char *tx=tr("Album"); +//char *to=tr("Composer"); +//char *tc=tr("Comment"); + const char *tn=tr("Track"); + /* in the moment: + Title: + Artist: + Album: year - name + Track: + */ + if (b == NULL) { + len=stringlen(withTitle); + b=new char[len]; + } + const char * del=" - "; + if (strlen(year) == 0) del=""; + if (withTitle){ + const char *tt=tr("Title"); + SNPRINTF(b,len-1,"%s: %s\n%s: %s\n%s: %s%s%s\n%s: %s", + tt,title,ta,artist,tx,year,del,album,tn,track); + } + else { +SNPRINTF(b,len-1,"%s: %s\n%s: %s%s%s\n%s: %s", + ta,artist,tx,year,del,album,tn,track); + } + b[len-1]=0; + return b; +} + +void DemuxerAudio::setSkipFactor(int factor) { + Log::getInstance()->log("DemuxerAudio",Log::DEBUG,"set skipfactor %d\n",factor); + buffer->setSkipFactor(factor); +} + diff --git a/demuxerts.cc b/demuxerts.cc index 9d0cf32..def7222 100644 --- a/demuxerts.cc +++ b/demuxerts.cc @@ -1,871 +1,883 @@ -/* - Copyright 2006-2008 Mark Calderbank - - 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 "demuxerts.h" -#include "log.h" -#include "video.h" -#include "vdr.h" - -#define PTS_JUMP_MARGIN 10000 -#define PTS_ALLOWANCE 90000 - -// TODO: PTS class to handle wrapping arithmetic & comparisons? -static ULLONG PTSDistance(ULLONG pts1, ULLONG pts2) -{ - // Assume pts1, pts2 < 2^33; calculate shortest distance between - ULLONG ret = (pts1 > pts2) ? pts1 - pts2 : pts2 - pts1; - if (ret > (1LL<<32)) ret = (1LL<<33) - ret; - return ret; -} - -static ULLONG PTSDifference(ULLONG pts1, ULLONG pts2) -{ - // Assume pts1, pts2 < 2^33; calculate pts1 - pts2 - if (pts1 > pts2) - return pts1 - pts2; - else - return (1LL<<33) + pts1 - pts2; -} - -DemuxerTS::DemuxerTS(int p_vID, int p_aID, int p_subID, int p_tID) -{ - vID = p_vID; vActive = false; - aID = p_aID; aActive = false; - subID = p_subID; subActive = false; - atype = 0; - subLength = 0; - tID = p_tID; - havechannelinfo=false; - doubledframerate=false; - framereserve=0; -} - -void DemuxerTS::flush() -{ - partPacket = 0; - parsed = false; - havechannelinfo=false; - Demuxer::flush(); - vPacket.init(PESTYPE_VID0); - switch (atype) - { - case 1: - aPacket.init(PESTYPE_PRIVATE_1, PESTYPE_SUBSTREAM_AC30); - break; - default: - case 0: - aPacket.init(PESTYPE_AUD0); - break; - } - subPacket.init(PESTYPE_PRIVATE_1); -tPacket.init(PESTYPE_PRIVATE_1,PESTYPE_SUBSTREAM_TELETEXTMAX); - - vActive = false; - aActive = false; - subActive = false; - subLength = 0; - tActive = false; - doubledframerate=false; - framereserve=0; -} - -int DemuxerTS::scan(UCHAR *buf, int len) -{ - switch (atype) - { - case 1: - return PESTYPE_PRIVATE_1; - default: - case 0: - return PESTYPE_AUD0; - } -} - -void DemuxerTS::setVID(int p_vID) -{ - vID = p_vID; - vPacket.init(PESTYPE_VID0); - vActive = false; -} - -void DemuxerTS::setAID(int p_aID, int type) -{ - aID = p_aID; - atype = type; - switch (atype) - { - case 1: - aPacket.init(PESTYPE_PRIVATE_1, PESTYPE_SUBSTREAM_AC30); - setAudioStream(PESTYPE_SUBSTREAM_AC30); - break; - default: - case 0: - aPacket.init(PESTYPE_AUD0); - setAudioStream(PESTYPE_AUD0); - break; - } - aActive = false; -} - -void DemuxerTS::setSubID(int p_subID) -{ - subID = p_subID; - subPacket.init(PESTYPE_PRIVATE_1); - subActive = false; - subLength = 0; -} - -void DemuxerTS::setTID(int p_tID) -{ - tID = p_tID; - tPacket.init(PESTYPE_PRIVATE_1,PESTYPE_SUBSTREAM_TELETEXTMAX); - tActive = false; - -} - - - - -int DemuxerTS::findPTS(UCHAR* buf, int len, ULLONG* dest) -{ - int scanaid=0; - - while (len >= TS_SIZE) - { - if (*buf != TS_SIG) {buf++;len--; continue;} - - //Pattern scanning won't work for ts - - - int datalen = TS_SIZE - 4; - int pid = ( (buf[1] & 0x1F) << 8 ) | buf[2]; - UCHAR payload = buf[1] & 0x40; - - if (buf[3] & 0x20) // Adaptation field is present - datalen -= (buf[4] + 1); - - UCHAR* curbuf =buf+ (TS_SIZE - datalen); - - - if (payload) { - if (pid == 0x00) {//PAT, only take first program number, ignore the rest - int pmtpid = (*(curbuf+11)<< 8) | *(curbuf+12); - if ((pmtpid >> 13) != 0x07) - { - Log::getInstance()->log("findPTS", Log::DEBUG, "PMTPID=%02x %02x TRAILING 111 not set but %x", *(curbuf+11),*(curbuf+12), (pmtpid >> 13)); - } - else - { - pmtpid = pmtpid & 0x1FFF; //clear upper 3 bits - PMTPID = pmtpid; - } - - } else if (pid == PMTPID) { //PMT - int sectionlength = ((*(curbuf+2) << 8) & 0x0F ) | *(buf+3); - //sectionlength += 4; //include header but subtract crc in the end... - int p = 13; //skip fixed part of pmt - while ( p < sectionlength) { - int streamtype = *(curbuf+p); - p++; - int foundpid = (*(curbuf+p)<< 8) | *(curbuf+p+1); - p += 2; //skip ES Pid - int eslength = ((*(curbuf+p) << 8) & 0x0F ) | *(curbuf+p+1); - p += 2; //skip ES length - if ((foundpid >> 13) != 0x07) - { - Log::getInstance()->log("findPTS", Log::DEBUG, "FOUNDPID=%02x %02x TRAILING 111 not set but %x", *(buf+p),*(buf+p+1), (foundpid >> 13)); - } - else - { - foundpid = foundpid & 0x1FFF; //clear upper 3 bits - //int pos=0; UNUSED? - if (streamtype==3 || streamtype ==4) { - scanaid=foundpid; - } - } - p += eslength; //skip es descriptor - } - } else if (pid == scanaid) { - // UINT framelength = ((UINT)curbuf[4] << 8) | curbuf[5]; UNUSED? - - if ( curbuf[7] & 0x80 ) // PTS_DTS_flags indicate that PTS is present - { - *dest = ( (ULLONG)(curbuf[9] & 0x0E) << 29 ) | - ( (ULLONG)(curbuf[10]) << 22 ) | - ( (ULLONG)(curbuf[11] & 0xFE) << 14 ) | - ( (ULLONG)(curbuf[12]) << 7 ) | - ( (ULLONG)(curbuf[13] & 0xFE) >> 1 ); - return 1; - } - } - } - len-=TS_SIZE; - buf+=TS_SIZE; - } - // No PTS found. - return 0; -} - -void DemuxerTS::setFrameNum(ULONG frame) -{ - frameCounting = true; - frameNumber = frame; - framereserve=0; - Log::getInstance()->log("DemuxerTS", Log::DEBUG, "setFrameNum %d", frame); -} - -void DemuxerTS::setPacketNum(ULONG npacket) -{ - packetCounting = true; - packetNumber = npacket; - Log::getInstance()->log("DemuxerTS", Log::DEBUG, "setPacketNum %d", npacket); -} - - -int DemuxerTS::put(UCHAR* buf, int len) -{ - int ret = 0; // return number of bytes consumed - - while (partPacket) - { - if (len >= TS_SIZE + 1 - partPacket) - { // Remainder of partial packet is available, plus one - memcpy(store+partPacket, buf, TS_SIZE - partPacket); - ret += TS_SIZE - partPacket; - buf += TS_SIZE - partPacket; - len -= TS_SIZE - partPacket; - partPacket = TS_SIZE; - if (*buf == TS_SIG) - { // Packet is properly terminated - int rc = processTS(store); - if (rc) - partPacket = 0; // Successfully processed - else - return ret; // Try again later. - } - else - { // Packet not terminated. Find another candidate, and shift store - Log::getInstance()->log("TS Demuxer", Log::ERR, "TS Misaligned!"); - int search = 1; - while (search < partPacket && store[search] != TS_SIG) - search++; - partPacket -= search; - if (partPacket) memcpy(store, store+search, partPacket); - } - } - else - { // Still don't have complete packet. Consume what we do have. - memcpy(store+partPacket, buf, len); - partPacket += len; - ret += len; - return ret; - } - } - - // Position ourselves at a candidate TS packet - while (len > 0 && *buf != TS_SIG) - { - Log::getInstance()->log("TS Demuxer", Log::ERR, "TS Misaligned!"); - buf++; ret++; len--; - } - - while (len) - { - if (len < TS_SIZE + 1) - { // Not enough data. Store what we have. - memcpy(store, buf, len); - partPacket = len; - ret += len; - return ret; - } - - if (buf[TS_SIZE] != TS_SIG) - { // Not terminated correctly. - buf++; ret++; len--; - while (len > 0 && *buf != TS_SIG) - { - buf++; ret++; len--; - } - } - else - { - int rc = processTS(buf); - if (rc) - { // Successfully processed - buf += TS_SIZE; ret += TS_SIZE; len -= TS_SIZE; - } - else - { // Processing failed. - return ret; - } - } - } - return ret; -} - -int DemuxerTS::processTS(UCHAR* buf) -{ - int datalen = TS_SIZE - 4; - - int pid = ( (buf[1] & 0x1F) << 8 ) | buf[2]; - UCHAR payload = buf[1] & 0x40; - - if (buf[3] & 0x20) // Adaptation field is present - datalen -= (buf[4] + 1); - if (datalen < 0) // Error in stream TODO log this - return 1; - if (datalen == 0) // Null packet - return 1; - buf += (TS_SIZE - datalen); - - if (payload) - { - int rc = 1; - if (pid == 0x00) {//PAT, only take first program number, ignore the rest - int pmtpid = (*(buf+11)<< 8) | *(buf+12); - if ((pmtpid >> 13) != 0x07) - { - Log::getInstance()->log("ProcessTS", Log::DEBUG, "PMTPID=%02x %02x TRAILING 111 not set but %x", *(buf+11),*(buf+12), (pmtpid >> 13)); - } - else - { - pmtpid = pmtpid & 0x1FFF; //clear upper 3 bits - PMTPID = pmtpid; - } - return 1; - } - if (pid == PMTPID) - { //PMT - int sectionlength = ((*(buf+2) << 8) & 0x0F ) | *(buf+3); - //sectionlength += 4; //include header but subtract crc in the end... - int p = 13; //skip fixed part of pmt - Channel new_channelinfo; - new_channelinfo.numAPids=0; - new_channelinfo.numDPids=0; - new_channelinfo.numSPids=0; - new_channelinfo.number=0; - new_channelinfo.type=VDR::RADIO; - new_channelinfo.name=NULL; - new_channelinfo.tpid=0xFFFFF; //unused, check this - new_channelinfo.vpid=0xFFFFF; //unused, check this - new_channelinfo.index=0; - - new_channelinfo.apids.clear(); - new_channelinfo.dpids.clear(); - new_channelinfo.spids.clear(); - - while ( p < sectionlength) { - int streamtype = *(buf+p); - p++; - int foundpid = (*(buf+p)<< 8) | *(buf+p+1); - p += 2; //skip ES Pid - int eslength = ((*(buf+p) << 8) & 0x0F ) | *(buf+p+1); - p += 2; //skip ES length - if ((foundpid >> 13) != 0x07) - { - Log::getInstance()->log("ProcessTS", Log::DEBUG, "FOUNDPID=%02x %02x TRAILING 111 not set but %x", *(buf+p),*(buf+p+1), (foundpid >> 13)); - } - else - { - foundpid = foundpid & 0x1FFF; //clear upper 3 bits - bool notfound=false; - int pos=0; - // Log::getInstance()->log("ProcessTS", Log::DEBUG, "FOUNDPID is %x", foundpid); - switch (streamtype) - { - case 0x1B: //MPEG 4 for future use - case 1: - case 2: { //video - if (foundpid != getVID()) - setVID(foundpid); - new_channelinfo.type=VDR::VIDEO; - new_channelinfo.vstreamtype=streamtype; - new_channelinfo.vpid=foundpid; - if (streamtype==0x1b) h264=true; - else h264=false; - - // Log::getInstance()->log("ProcessTS", Log::DEBUG, "Set video PID to %x", foundpid); - }break; - case 3: - case 4: { //audio - apid newapid; - newapid.pid = foundpid; - newapid.name = NULL; //set it in player - new_channelinfo.apids.push_back(newapid); - new_channelinfo.numAPids++; - if (getAID() == 0) { //set unset AID to first audio pid that reports itself - setAID(foundpid,0); - Log::getInstance()->log("ProcessTS", Log::DEBUG, "Set audio PID to %x", foundpid); - } - } break; - case 5: - case 6: { //Private Data - apid newapid; - newapid.pid = foundpid; - newapid.name = NULL; //set it in player - pos=0; - notfound=true; - - while (pos< eslength && notfound) { - switch (buf[p+pos]) { - case 0x6A: {//Ac3 descriptor - new_channelinfo.dpids.push_back(newapid); - new_channelinfo.numDPids++; - notfound=false; - } break; - case 0x59: {//SubtitlingDescriptor - new_channelinfo.spids.push_back(newapid); - new_channelinfo.numSPids++; - notfound=false; - } break; - case 0x56: { - new_channelinfo.tpid=foundpid; - notfound=false; - } break; - }; - pos+=2+buf[p+pos+1]; - } - - } break; - default://TODO how about subtitles and second audio pids - break; - } - } - - p += eslength; //skip es descriptor - - - } - bool audioPIDpresent=false; //Check if pids chnages - ULONG i; - for (i=0;i0) { - setAID(channelinfo.apids[0].pid,0); - } else if (channelinfo.numDPids>0) { - setAID(channelinfo.dpids[0].pid,1); - } - } - - channelinfo=new_channelinfo; - havechannelinfo=true; - - return 1; - } - - - - - if (pid == vID) - { - if (vActive) - { - if (!parsed) - { - parseTSPacketDetails(vPacket); - parsed = true; - } - rc = submitPacket(vPacket); - } - vActive = true; - } - if (pid == aID) - { - if (aActive) - { - if (!parsed) - { - parseTSPacketDetails(aPacket); - parsed = true; - } - rc = submitPacket(aPacket); - } - aActive = true; - } - if (pid == subID) - { - if (subActive) - { - if (!parsed) - { - parseTSPacketDetails(subPacket); - parsed = true; - } - rc = submitPacket(subPacket); - } - subActive = true; - } - if (isteletextdecoded && pid == tID) - { - if (tActive) - { - if (!parsed) - { - parseTSPacketDetails(tPacket); - parsed = true; - } - rc = submitPacket(tPacket); - } - tActive = true; - } - if (rc == 0) return 0; - - parsed = false; - if (pid == vID) - { - vPacket.init(PESTYPE_VID0); - buf += 6; datalen -= 6; - } - if (pid == aID) - { - switch (atype) - { - case 1: - aPacket.init(PESTYPE_PRIVATE_1, PESTYPE_SUBSTREAM_AC30); - break; - default: - case 0: - aPacket.init(PESTYPE_AUD0); - break; - } - buf += 6; datalen -= 6; - } - if (pid == subID) - { - subPacket.init(PESTYPE_PRIVATE_1); - subLength = (buf[4] << 8) + buf[5]; - buf += 6; datalen -= 6; - } - if (isteletextdecoded && pid == tID) - { - tPacket.init(PESTYPE_PRIVATE_1,PESTYPE_SUBSTREAM_TELETEXTMAX); - buf += 6; datalen -= 6; - } - } - - if ( (pid == vID && vActive) || - (pid == aID && aActive) || - (pid == tID && tActive) || - (pid == subID && subActive) ) - { - PESPacket* packet = NULL; - if (pid == vID) packet = &vPacket; - if (pid == aID) packet = &aPacket; - if (pid == subID) { - packet = &subPacket; - } - if (pid == tID) packet = &tPacket; - if (packet != NULL) - { - if (packet->write(buf, datalen) == 0) - { // Writing to packet failed. It has overflowed. - if (!parsed) - { - parseTSPacketDetails(*packet); - parsed = true; - } - if (submitPacket(*packet) == 0) return 0; - parsed = false; - packet->truncate(); - packet->write((UCHAR*)"\200\000\000", 3); - packet->write(buf, datalen); - } - } - } - - if (pid == subID && subActive && subPacket.getLength() == subLength) - { - parsePacketDetails(subPacket); -Log::getInstance()->log("DEMUXERTS", Log::DEBUG, "SUBMITTING A SUBTITLE PACKET %d %x", subLength, subPacket.getSubstream()); - submitPacket(subPacket); - subActive = false; - } - - return 1; -} - -ULONG DemuxerTS::getPacketNum() -{ - return packetNumber; -} - -ULONG DemuxerTS::getFrameNumFromPTS(ULLONG pts) -{ - ULLONG difference = (1LL<<33); - ULONG ref_frame = 0; - int total = 0, actual = 0; - if (pts==0) return 0; //we are in startup - pts_map_mutex.Lock(); - PTSMap::iterator iter = pts_map.begin(); - while (iter != pts_map.end()) - { - ++total; - //Log::getInstance()->log("DemuxerTS", Log::DEBUG, "getFrameNumfromPTS pts1 %lld pts2 %lld", pts, iter->pts); - if (PTSDifference(iter->pts, pts) < PTS_ALLOWANCE) - { - difference = 0; - ref_frame = iter->frame; - actual = total; - break; - } - ULLONG newdiff = PTSDifference(pts, iter->pts); - if (newdiff < difference) - { - difference = newdiff; - ref_frame = iter->frame; - actual = total; - } - ++iter; - } - if (total > 1 && actual == 1) // We are using the most recent PTS ref. - { // Delete the rest. - iter = pts_map.begin(); iter++; - pts_map.erase(iter, pts_map.end()); - } - pts_map_mutex.Unlock(); - - //Log::getInstance()->log("DemuxerTS", Log::DEBUG, "getFrameNumfromPTS pts %lld deleted %d difference %lld", pts, total,difference); - - if (difference == (1LL<<33)) - return 0; // We cannot make sense of the pts - else - return ref_frame + difference * fps / 90000; -} - - -void DemuxerTS::parseTSPacketDetails(PESPacket &packet) // Only important stuff for paket counting reminas -{ - parsePacketDetails(packet); - if (packetCounting && packet.getPacketType() >= PESTYPE_AUD0 && - packet.getPacketType() <= PESTYPE_AUDMAX) - { - packetNumber++; - } - UINT pictsinpacket=packet.countPictureHeaders(h264); - - UINT numpicts=0; - if (!doubledframerate) - { - numpicts=pictsinpacket; - } - else - { - numpicts=(pictsinpacket+framereserve)>>1; - framereserve=(pictsinpacket+framereserve)%2; - } - - - if (frameCounting && numpicts && - packet.getPacketType() >= PESTYPE_VID0 && - packet.getPacketType() <= PESTYPE_VIDMAX) - { - frameNumber+=numpicts; - ULONG frame_num = frameNumber; - if ((h264 || packet.findSeqHeader(h264) > 1) && packet.hasPTS()) - { - PTSMapEntry me; - pts_map_mutex.Lock(); - if (pts_map.empty()) - { - me.pts = packet.getPTS(); - me.frame = frame_num; - pts_map_mutex.Unlock(); - pts_map_mutex.Lock(); - pts_map.push_front(me); - } - me = pts_map.front(); - pts_map_mutex.Unlock(); - - //UINT fps = Video::getInstance()->getFPS(); - double tfps=fps; - if (doubledframerate) tfps*=2.; - ULLONG pts_expected = me.pts + 90000*((int)(((double)(frame_num - me.frame)) / tfps)); - while (pts_expected > (1LL<<33)) pts_expected -= (1LL<<33); - - if (!doubledframerate - &&(abs((long long)PTSDistance(pts_expected, packet.getPTS())-1800) <= 1 - || abs((long long)PTSDistance(pts_expected, packet.getPTS())-1501) <= 1)) { - doubledframerate=true; //Detected p50 or p60 - } - - if (PTSDistance(pts_expected, packet.getPTS()) > PTS_JUMP_MARGIN) // PTS jump! - { - me.pts = packet.getPTS(); - me.frame = frame_num; - pts_map_mutex.Lock(); - pts_map.push_front(me); - pts_map_mutex.Unlock(); - } - } - } -} - - -bool DemuxerTS::scanForVideo(UCHAR* buf, UINT len, bool &ish264) -{ - int pmtpidy=0; - - while (len >= TS_SIZE) - { - if (*buf != TS_SIG) {buf++;len--; continue;} - - //Pattern scanning won't work for ts - - - int datalen = TS_SIZE - 4; - int pid = ( (buf[1] & 0x1F) << 8 ) | buf[2]; - UCHAR payload = buf[1] & 0x40; - - if (buf[3] & 0x20) // Adaptation field is present - datalen -= (buf[4] + 1); - - UCHAR* curbuf =buf+ (TS_SIZE - datalen); - - if (payload) { - if (pid == 0x00) {//PAT, only take first program number, ignore the rest - int pmtpid = (*(curbuf+11)<< 8) | *(curbuf+12); - if ((pmtpid >> 13) != 0x07) - { - Log::getInstance()->log("DemuxerTS", Log::DEBUG, "PMTPID=%02x %02x TRAILING 111 not set but %x", *(curbuf+11),*(curbuf+12), (pmtpid >> 13)); - } - else - { - pmtpid = pmtpid & 0x1FFF; //clear upper 3 bits - pmtpidy = pmtpid; - Log::getInstance()->log("DemuxerTS", Log::DEBUG, "PMT pid%02x",pmtpid ); - } - - } else if (pid == pmtpidy) { //PMT - int sectionlength = ((*(curbuf+2) << 8) & 0x0F ) | *(buf+3); - //sectionlength += 4; //include header but subtract crc in the end... - int p = 13; //skip fixed part of pmt - while ( p < sectionlength) { - int streamtype = *(curbuf+p); - p++; - int foundpid = (*(curbuf+p)<< 8) | *(curbuf+p+1); - p += 2; //skip ES Pid - int eslength = ((*(curbuf+p) << 8) & 0x0F ) | *(curbuf+p+1); - p += 2; //skip ES length - if ((foundpid >> 13) != 0x07) - { - Log::getInstance()->log("DemuxerTS", Log::DEBUG, "FOUNDPID=%02x %02x TRAILING 111 not set but %x", *(buf+p),*(buf+p+1), (foundpid >> 13)); - } - else - { - foundpid = foundpid & 0x1FFF; //clear upper 3 bits - // int pos=0; UNUSED? - Log::getInstance()->log("DemuxerTS", Log::DEBUG, "Pid found %02x type %02x",foundpid ,streamtype); - if (streamtype==1 || streamtype ==2) { - ish264=false; - Log::getInstance()->log("DemuxerTS", Log::DEBUG, "Found Mpeg2 Video"); - return true; - } - if (streamtype==0x1b) { - ish264=true; - Log::getInstance()->log("DemuxerTS", Log::DEBUG, "Found h264 Video"); - return true; - } - } - p += eslength; //skip es descriptor - } - ish264=false; - return false; - } - } - len-=TS_SIZE; - buf+=TS_SIZE; - } - ish264=false; - return false; -} - -UINT DemuxerTS::stripAudio(UCHAR* buf, UINT len) //it has to be adapted -{ - //This function strips all TS Headers and non video payload - UINT readpos=0; - UINT writepos=0; - PESPacket destpaket; - bool started=true; - while (readpos < len ) { - if (buf[readpos] != TS_SIG) {readpos++; continue;} - UINT oldreadpos=readpos; - - int datalen = TS_SIZE - 4; - int pid = ( (buf[readpos+1] & 0x1F) << 8 ) | buf[readpos+2]; - UCHAR payload = buf[readpos+1] & 0x40; - if (buf[readpos+3] & 0x20) { // Adaptation field is present - datalen -= (buf[readpos+4] + 1); - } - if (datalen < 0) {// Error in stream TODO log this - return 0; - } - if (datalen == 0) {// Null packet - readpos=oldreadpos+TS_SIZE; - continue; - } - readpos += (TS_SIZE - datalen); - UINT towrite=min(datalen,len-readpos); - if (pid == vID) { - if (payload) { - if (started) { - parsePacketDetails(destpaket); - memcpy(buf+writepos,destpaket.getData(),destpaket.getSize()); - writepos+=destpaket.getSize(); - } - destpaket.init(PESTYPE_VID0); - readpos += 6; towrite -= 6; - started=true; - } - - if (started) { - if (!destpaket.write(buf+readpos,towrite)) { - parsePacketDetails(destpaket); - memcpy(buf+writepos,destpaket.getData(),destpaket.getSize()); - writepos+=destpaket.getSize(); - destpaket.truncate(); - destpaket.write((UCHAR*)"\200\000\000", 3); - destpaket.write(buf+readpos,towrite); - - } - } - - - - } - readpos=oldreadpos+TS_SIZE; - } - parsePacketDetails(destpaket); - memcpy(buf+writepos,destpaket.getData(),destpaket.getSize()); - writepos+=destpaket.getSize(); - - return writepos; -} - - - +/* + Copyright 2006-2008 Mark Calderbank + + 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 "demuxerts.h" +#include "log.h" +#include "video.h" +#include "vdr.h" + +#define PTS_JUMP_MARGIN 10000 +#define PTS_ALLOWANCE 90000 + +// TODO: PTS class to handle wrapping arithmetic & comparisons? +static ULLONG PTSDistance(ULLONG pts1, ULLONG pts2) +{ + // Assume pts1, pts2 < 2^33; calculate shortest distance between + ULLONG ret = (pts1 > pts2) ? pts1 - pts2 : pts2 - pts1; + if (ret > (1LL<<32)) ret = (1LL<<33) - ret; + return ret; +} + +static ULLONG PTSDifference(ULLONG pts1, ULLONG pts2) +{ + // Assume pts1, pts2 < 2^33; calculate pts1 - pts2 + if (pts1 > pts2) + return pts1 - pts2; + else + return (1LL<<33) + pts1 - pts2; +} + +DemuxerTS::DemuxerTS(int p_vID, int p_aID, int p_subID, int p_tID) +{ + vID = p_vID; vActive = false; + aID = p_aID; aActive = false; + subID = p_subID; subActive = false; + atype = 0; + subLength = 0; + tID = p_tID; + havechannelinfo=false; + //doubledframerate=false; + framereserve=0; +} + +void DemuxerTS::flush() +{ + partPacket = 0; + parsed = false; + havechannelinfo=false; + Demuxer::flush(); + vPacket.init(PESTYPE_VID0); + switch (atype) + { + case 1: + aPacket.init(PESTYPE_PRIVATE_1, PESTYPE_SUBSTREAM_AC30); + break; + default: + case 0: + aPacket.init(PESTYPE_AUD0); + break; + } + subPacket.init(PESTYPE_PRIVATE_1); +tPacket.init(PESTYPE_PRIVATE_1,PESTYPE_SUBSTREAM_TELETEXTMAX); + + vActive = false; + aActive = false; + subActive = false; + subLength = 0; + tActive = false; + // doubledframerate=false; + framereserve=0; +} + +int DemuxerTS::scan(UCHAR *buf, int len) +{ + switch (atype) + { + case 1: + return PESTYPE_PRIVATE_1; + default: + case 0: + return PESTYPE_AUD0; + } +} + +void DemuxerTS::setVID(int p_vID) +{ + vID = p_vID; + vPacket.init(PESTYPE_VID0); + vActive = false; +} + +void DemuxerTS::setAID(int p_aID, int type) +{ + aID = p_aID; + atype = type; + switch (atype) + { + case 1: + aPacket.init(PESTYPE_PRIVATE_1, PESTYPE_SUBSTREAM_AC30); + setAudioStream(PESTYPE_SUBSTREAM_AC30); + break; + default: + case 0: + aPacket.init(PESTYPE_AUD0); + setAudioStream(PESTYPE_AUD0); + break; + } + aActive = false; +} + +void DemuxerTS::setSubID(int p_subID) +{ + subID = p_subID; + subPacket.init(PESTYPE_PRIVATE_1); + subActive = false; + subLength = 0; +} + +void DemuxerTS::setTID(int p_tID) +{ + tID = p_tID; + tPacket.init(PESTYPE_PRIVATE_1,PESTYPE_SUBSTREAM_TELETEXTMAX); + tActive = false; + +} + + + + +int DemuxerTS::findPTS(UCHAR* buf, int len, ULLONG* dest) +{ + int scanaid=0; + + while (len >= TS_SIZE) + { + if (*buf != TS_SIG) {buf++;len--; continue;} + + //Pattern scanning won't work for ts + + + int datalen = TS_SIZE - 4; + int pid = ( (buf[1] & 0x1F) << 8 ) | buf[2]; + UCHAR payload = buf[1] & 0x40; + + if (buf[3] & 0x20) // Adaptation field is present + datalen -= (buf[4] + 1); + + UCHAR* curbuf =buf+ (TS_SIZE - datalen); + + + if (payload) { + if (pid == 0x00) {//PAT, only take first program number, ignore the rest + int pmtpid = (*(curbuf+11)<< 8) | *(curbuf+12); + if ((pmtpid >> 13) != 0x07) + { + Log::getInstance()->log("findPTS", Log::DEBUG, "PMTPID=%02x %02x TRAILING 111 not set but %x", *(curbuf+11),*(curbuf+12), (pmtpid >> 13)); + } + else + { + pmtpid = pmtpid & 0x1FFF; //clear upper 3 bits + PMTPID = pmtpid; + } + + } else if (pid == PMTPID) { //PMT + int sectionlength = ((*(curbuf+2) << 8) & 0x0F ) | *(buf+3); + //sectionlength += 4; //include header but subtract crc in the end... + int p = 13; //skip fixed part of pmt + while ( p < sectionlength) { + int streamtype = *(curbuf+p); + p++; + int foundpid = (*(curbuf+p)<< 8) | *(curbuf+p+1); + p += 2; //skip ES Pid + int eslength = ((*(curbuf+p) << 8) & 0x0F ) | *(curbuf+p+1); + p += 2; //skip ES length + if ((foundpid >> 13) != 0x07) + { + Log::getInstance()->log("findPTS", Log::DEBUG, "FOUNDPID=%02x %02x TRAILING 111 not set but %x", *(buf+p),*(buf+p+1), (foundpid >> 13)); + } + else + { + foundpid = foundpid & 0x1FFF; //clear upper 3 bits + //int pos=0; UNUSED? + if (streamtype==3 || streamtype ==4) { + scanaid=foundpid; + } + } + p += eslength; //skip es descriptor + } + } else if (pid == scanaid) { + // UINT framelength = ((UINT)curbuf[4] << 8) | curbuf[5]; UNUSED? + + if ( curbuf[7] & 0x80 ) // PTS_DTS_flags indicate that PTS is present + { + *dest = ( (ULLONG)(curbuf[9] & 0x0E) << 29 ) | + ( (ULLONG)(curbuf[10]) << 22 ) | + ( (ULLONG)(curbuf[11] & 0xFE) << 14 ) | + ( (ULLONG)(curbuf[12]) << 7 ) | + ( (ULLONG)(curbuf[13] & 0xFE) >> 1 ); + return 1; + } + } + } + len-=TS_SIZE; + buf+=TS_SIZE; + } + // No PTS found. + return 0; +} + +void DemuxerTS::setFrameNum(ULONG frame) +{ + frameCounting = true; + frameNumber = frame; + framereserve=0; + Log::getInstance()->log("DemuxerTS", Log::DEBUG, "setFrameNum %d", frame); +} + +void DemuxerTS::setPacketNum(ULONG npacket) +{ + packetCounting = true; + packetNumber = npacket; + Log::getInstance()->log("DemuxerTS", Log::DEBUG, "setPacketNum %d", npacket); +} + +int DemuxerTS::put(UCHAR* buf, int len) +{ + int ret = 0; // return number of bytes consumed + + bool misaligned_mess=false; + while (partPacket) + { + if (len >= TS_SIZE + 1 - partPacket) + { // Remainder of partial packet is available, plus one + memcpy(store+partPacket, buf, TS_SIZE - partPacket); + ret += TS_SIZE - partPacket; + buf += TS_SIZE - partPacket; + len -= TS_SIZE - partPacket; + partPacket = TS_SIZE; + if (*buf == TS_SIG) + { // Packet is properly terminated + int rc = processTS(store); + if (rc) + partPacket = 0; // Successfully processed + else + return ret; // Try again later. + } + else + { // Packet not terminated. Find another candidate, and shift store + if (!misaligned_mess) { + Log::getInstance()->log("TS Demuxer", Log::ERR, "TS Misaligned!A"); + misaligned_mess=true; // do not alarm more than once + } + int search = 1; + while (search < partPacket && store[search] != TS_SIG) + search++; + partPacket -= search; + if (partPacket) memcpy(store, store+search, partPacket); + } + } + else + { // Still don't have complete packet. Consume what we do have. + memcpy(store+partPacket, buf, len); + partPacket += len; + ret += len; + return ret; + } + } + + // Position ourselves at a candidate TS packet + while (len > 0 && *buf != TS_SIG) + { + if (!misaligned_mess) { + Log::getInstance()->log("TS Demuxer", Log::ERR, "TS Misaligned!B"); + misaligned_mess=true; // do not alarm more than once + } + buf++; ret++; len--; + } + + while (len) + { + if (len < TS_SIZE + 1) + { // Not enough data. Store what we have. + memcpy(store, buf, len); + partPacket = len; + ret += len; + return ret; + } + + if (buf[TS_SIZE] != TS_SIG) + { // Not terminated correctly. + buf++; ret++; len--; + while (len > 0 && *buf != TS_SIG) + { + buf++; ret++; len--; + } + } + else + { + int rc = processTS(buf); + if (rc) + { // Successfully processed + buf += TS_SIZE; ret += TS_SIZE; len -= TS_SIZE; + } + else + { // Processing failed. + return ret; + } + } + } + return ret; +} + + +int DemuxerTS::processTS(UCHAR* buf) +{ + int datalen = TS_SIZE - 4; + + int pid = ( (buf[1] & 0x1F) << 8 ) | buf[2]; + UCHAR payload = buf[1] & 0x40; + + + if (buf[3] & 0x20) // Adaptation field is present + datalen -= (buf[4] + 1); + if (datalen < 0) // Error in stream TODO log this + return 1; + if (datalen == 0) // Null packet + return 1; + // if (!(buf[3] &0x10)) return 1; // no payload + buf += (TS_SIZE - datalen); + + if (payload) + { + int rc = 1; + if (pid == 0x00) {//PAT, only take first program number, ignore the rest + int pmtpid = (*(buf+11)<< 8) | *(buf+12); + if ((pmtpid >> 13) != 0x07) + { + Log::getInstance()->log("ProcessTS", Log::DEBUG, "PMTPID=%02x %02x TRAILING 111 not set but %x", *(buf+11),*(buf+12), (pmtpid >> 13)); + } + else + { + pmtpid = pmtpid & 0x1FFF; //clear upper 3 bits + PMTPID = pmtpid; + } + return 1; + } + if (pid == PMTPID) + { //PMT + int sectionlength = ((*(buf+2) << 8) & 0x0F ) | *(buf+3); + //sectionlength += 4; //include header but subtract crc in the end... + int p = 13; //skip fixed part of pmt + Channel new_channelinfo; + new_channelinfo.numAPids=0; + new_channelinfo.numDPids=0; + new_channelinfo.numSPids=0; + new_channelinfo.number=0; + new_channelinfo.type=VDR::RADIO; + new_channelinfo.name=NULL; + new_channelinfo.tpid=0xFFFFF; //unused, check this + new_channelinfo.vpid=0xFFFFF; //unused, check this + new_channelinfo.index=0; + + new_channelinfo.apids.clear(); + new_channelinfo.dpids.clear(); + new_channelinfo.spids.clear(); + + while ( p < sectionlength) { + int streamtype = *(buf+p); + p++; + int foundpid = (*(buf+p)<< 8) | *(buf+p+1); + p += 2; //skip ES Pid + int eslength = ((*(buf+p) << 8) & 0x0F ) | *(buf+p+1); + p += 2; //skip ES length + if ((foundpid >> 13) != 0x07) + { + Log::getInstance()->log("ProcessTS", Log::DEBUG, "FOUNDPID=%02x %02x TRAILING 111 not set but %x", *(buf+p),*(buf+p+1), (foundpid >> 13)); + } + else + { + foundpid = foundpid & 0x1FFF; //clear upper 3 bits + bool notfound=false; + int pos=0; + // Log::getInstance()->log("ProcessTS", Log::DEBUG, "FOUNDPID is %x", foundpid); + switch (streamtype) + { + case 0x1B: //MPEG 4 for future use + case 1: + case 2: { //video + if (foundpid != getVID()) + setVID(foundpid); + new_channelinfo.type=VDR::VIDEO; + new_channelinfo.vstreamtype=streamtype; + new_channelinfo.vpid=foundpid; + if (streamtype==0x1b) h264=true; + else h264=false; + + // Log::getInstance()->log("ProcessTS", Log::DEBUG, "Set video PID to %x", foundpid); + }break; + case 3: + case 4: { //audio + apid newapid; + newapid.pid = foundpid; + newapid.name = NULL; //set it in player + new_channelinfo.apids.push_back(newapid); + new_channelinfo.numAPids++; + if (getAID() == 0) { //set unset AID to first audio pid that reports itself + setAID(foundpid,0); + Log::getInstance()->log("ProcessTS", Log::DEBUG, "Set audio PID to %x", foundpid); + } + } break; + case 5: + case 6: { //Private Data + apid newapid; + newapid.pid = foundpid; + newapid.name = NULL; //set it in player + pos=0; + notfound=true; + + while (pos< eslength && notfound) { + switch (buf[p+pos]) { + case 0x6A: {//Ac3 descriptor + new_channelinfo.dpids.push_back(newapid); + new_channelinfo.numDPids++; + notfound=false; + } break; + case 0x59: {//SubtitlingDescriptor + new_channelinfo.spids.push_back(newapid); + new_channelinfo.numSPids++; + notfound=false; + } break; + case 0x56: { + new_channelinfo.tpid=foundpid; + notfound=false; + } break; + }; + pos+=2+buf[p+pos+1]; + } + + } break; + default://TODO how about subtitles and second audio pids + break; + } + } + + p += eslength; //skip es descriptor + + + } + bool audioPIDpresent=false; //Check if pids chnages + ULONG i; + for (i=0;i0) { + setAID(channelinfo.apids[0].pid,0); + } else if (channelinfo.numDPids>0) { + setAID(channelinfo.dpids[0].pid,1); + } + } + + channelinfo=new_channelinfo; + havechannelinfo=true; + + return 1; + } + + + + + if (pid == vID) + { + if (vActive) + { + if (!parsed) + { + parseTSPacketDetails(vPacket); + parsed = true; + } + rc = submitPacket(vPacket); + } + vActive = true; + } + if (pid == aID) + { + if (aActive) + { + if (!parsed) + { + parseTSPacketDetails(aPacket); + parsed = true; + } + rc = submitPacket(aPacket); + } + aActive = true; + } + if (pid == subID) + { + if (subActive) + { + if (!parsed) + { + parseTSPacketDetails(subPacket); + parsed = true; + } + rc = submitPacket(subPacket); + } + subActive = true; + } + if (isteletextdecoded && pid == tID) + { + if (tActive) + { + if (!parsed) + { + parseTSPacketDetails(tPacket); + parsed = true; + } + rc = submitPacket(tPacket); + } + tActive = true; + } + if (rc == 0) return 0; + + parsed = false; + if (pid == vID) + { + vPacket.init(PESTYPE_VID0); + buf += 6; datalen -= 6; + } + if (pid == aID) + { + switch (atype) + { + case 1: + aPacket.init(PESTYPE_PRIVATE_1, PESTYPE_SUBSTREAM_AC30); + break; + default: + case 0: + aPacket.init(PESTYPE_AUD0); + break; + } + buf += 6; datalen -= 6; + } + if (pid == subID) + { + subPacket.init(PESTYPE_PRIVATE_1); + subLength = (buf[4] << 8) + buf[5]; + buf += 6; datalen -= 6; + } + if (isteletextdecoded && pid == tID) + { + tPacket.init(PESTYPE_PRIVATE_1,PESTYPE_SUBSTREAM_TELETEXTMAX); + buf += 6; datalen -= 6; + } + } + + if ( (pid == vID && vActive) || + (pid == aID && aActive) || + (pid == tID && tActive) || + (pid == subID && subActive) ) + { + PESPacket* packet = NULL; + if (pid == vID) packet = &vPacket; + if (pid == aID) packet = &aPacket; + if (pid == subID) { + packet = &subPacket; + } + if (pid == tID) packet = &tPacket; + if (packet != NULL) + { + if (packet->write(buf, datalen) == 0) + { // Writing to packet failed. It has overflowed. + if (!parsed) + { + parseTSPacketDetails(*packet); + parsed = true; + } + if (submitPacket(*packet) == 0) return 0; + parsed = false; + packet->truncate(); + packet->write((UCHAR*)"\200\000\000", 3); + packet->write(buf, datalen); + } + } + } + + if (pid == subID && subActive && subPacket.getLength() == subLength) + { + parsePacketDetails(subPacket); +Log::getInstance()->log("DEMUXERTS", Log::DEBUG, "SUBMITTING A SUBTITLE PACKET %d %x", subLength, subPacket.getSubstream()); + submitPacket(subPacket); + subActive = false; + } + + return 1; +} + +ULONG DemuxerTS::getPacketNum() +{ + return packetNumber; +} + +ULONG DemuxerTS::getFrameNumFromPTS(ULLONG pts) +{ + ULLONG difference = (1LL<<33); + ULONG ref_frame = 0; + int total = 0, actual = 0; + if (pts==0) return 0; //we are in startup + pts_map_mutex.Lock(); + PTSMap::iterator iter = pts_map.begin(); + while (iter != pts_map.end()) + { + ++total; + //Log::getInstance()->log("DemuxerTS", Log::DEBUG, "getFrameNumfromPTS pts1 %lld pts2 %lld", pts, iter->pts); + if (PTSDifference(iter->pts, pts) < PTS_ALLOWANCE) + { + difference = 0; + ref_frame = iter->frame; + actual = total; + break; + } + ULLONG newdiff = PTSDifference(pts, iter->pts); + if (newdiff < difference) + { + difference = newdiff; + ref_frame = iter->frame; + actual = total; + } + ++iter; + } + if (total > 1 && actual == 1) // We are using the most recent PTS ref. + { // Delete the rest. + iter = pts_map.begin(); iter++; + pts_map.erase(iter, pts_map.end()); + } + pts_map_mutex.Unlock(); + + Log::getInstance()->log("DemuxerTS", Log::DEBUG, "getFrameNumfromPTS pts %lld deleted %d difference %lld", pts, total,difference); + + if (difference == (1LL<<33)) + return 0; // We cannot make sense of the pts + else + return ref_frame + difference * fps / 90000; +} + + +void DemuxerTS::parseTSPacketDetails(PESPacket &packet) // Only important stuff for paket counting reminas +{ + parsePacketDetails(packet); + if (packetCounting && packet.getPacketType() >= PESTYPE_AUD0 && + packet.getPacketType() <= PESTYPE_AUDMAX) + { + packetNumber++; + } + UINT pictsinpacket=packet.countPictureHeaders(h264); + + UINT numpicts=0; + /* if (!doubledframerate) + {*/ + numpicts=pictsinpacket; + /* } + else + { + numpicts=(pictsinpacket+framereserve)>>1; + framereserve=(pictsinpacket+framereserve)%2; + }*/ + + + if (frameCounting && numpicts && + packet.getPacketType() >= PESTYPE_VID0 && + packet.getPacketType() <= PESTYPE_VIDMAX) + { + frameNumber+=numpicts; + ULONG frame_num = frameNumber; + if ((h264 || packet.findSeqHeader(h264) > 1) && packet.hasPTS()) + { + PTSMapEntry me; + pts_map_mutex.Lock(); + if (pts_map.empty()) + { + me.pts = packet.getPTS(); + me.frame = frame_num; + pts_map_mutex.Unlock(); + pts_map_mutex.Lock(); + pts_map.push_front(me); + } + me = pts_map.front(); + pts_map_mutex.Unlock(); + + //UINT fps = Video::getInstance()->getFPS(); + double tfps=fps; + // if (doubledframerate) tfps*=2.; + ULLONG pts_expected = me.pts + 90000*((int)(((double)(frame_num - me.frame)) / tfps)); + while (pts_expected > (1LL<<33)) pts_expected -= (1LL<<33); + + // this was a workaround for a vdr bug, now fixed, for older recordings deleter the index file + + /* + if (!doubledframerate + &&(abs((long long)PTSDistance(pts_expected, packet.getPTS())-1800) <= 1 + || abs((long long)PTSDistance(pts_expected, packet.getPTS())-1501) <= 1)) { + doubledframerate=true; //Detected p50 or p60 + }*/ + + if (PTSDistance(pts_expected, packet.getPTS()) > PTS_JUMP_MARGIN) // PTS jump! + { + me.pts = packet.getPTS(); + me.frame = frame_num; + pts_map_mutex.Lock(); + pts_map.push_front(me); + pts_map_mutex.Unlock(); + } + } + } +} + + +bool DemuxerTS::scanForVideo(UCHAR* buf, UINT len, bool &ish264) +{ + int pmtpidy=0; + + while (len >= TS_SIZE) + { + if (*buf != TS_SIG) {buf++;len--; continue;} + + //Pattern scanning won't work for ts + + + int datalen = TS_SIZE - 4; + int pid = ( (buf[1] & 0x1F) << 8 ) | buf[2]; + UCHAR payload = buf[1] & 0x40; + + if (buf[3] & 0x20) // Adaptation field is present + datalen -= (buf[4] + 1); + + UCHAR* curbuf =buf+ (TS_SIZE - datalen); + + if (payload) { + if (pid == 0x00) {//PAT, only take first program number, ignore the rest + int pmtpid = (*(curbuf+11)<< 8) | *(curbuf+12); + if ((pmtpid >> 13) != 0x07) + { + Log::getInstance()->log("DemuxerTS", Log::DEBUG, "PMTPID=%02x %02x TRAILING 111 not set but %x", *(curbuf+11),*(curbuf+12), (pmtpid >> 13)); + } + else + { + pmtpid = pmtpid & 0x1FFF; //clear upper 3 bits + pmtpidy = pmtpid; + Log::getInstance()->log("DemuxerTS", Log::DEBUG, "PMT pid%02x",pmtpid ); + } + + } else if (pid == pmtpidy) { //PMT + int sectionlength = ((*(curbuf+2) << 8) & 0x0F ) | *(buf+3); + //sectionlength += 4; //include header but subtract crc in the end... + int p = 13; //skip fixed part of pmt + while ( p < sectionlength) { + int streamtype = *(curbuf+p); + p++; + int foundpid = (*(curbuf+p)<< 8) | *(curbuf+p+1); + p += 2; //skip ES Pid + int eslength = ((*(curbuf+p) << 8) & 0x0F ) | *(curbuf+p+1); + p += 2; //skip ES length + if ((foundpid >> 13) != 0x07) + { + Log::getInstance()->log("DemuxerTS", Log::DEBUG, "FOUNDPID=%02x %02x TRAILING 111 not set but %x", *(buf+p),*(buf+p+1), (foundpid >> 13)); + } + else + { + foundpid = foundpid & 0x1FFF; //clear upper 3 bits + // int pos=0; UNUSED? + Log::getInstance()->log("DemuxerTS", Log::DEBUG, "Pid found %02x type %02x",foundpid ,streamtype); + if (streamtype==1 || streamtype ==2) { + ish264=false; + Log::getInstance()->log("DemuxerTS", Log::DEBUG, "Found Mpeg2 Video"); + return true; + } + if (streamtype==0x1b) { + ish264=true; + Log::getInstance()->log("DemuxerTS", Log::DEBUG, "Found h264 Video"); + return true; + } + } + p += eslength; //skip es descriptor + } + ish264=false; + return false; + } + } + len-=TS_SIZE; + buf+=TS_SIZE; + } + ish264=false; + return false; +} + +UINT DemuxerTS::stripAudio(UCHAR* buf, UINT len) //it has to be adapted +{ + //This function strips all TS Headers and non video payload + UINT readpos=0; + UINT writepos=0; + PESPacket destpaket; + bool started=true; + while (readpos < len ) { + if (buf[readpos] != TS_SIG) {readpos++; continue;} + UINT oldreadpos=readpos; + + int datalen = TS_SIZE - 4; + int pid = ( (buf[readpos+1] & 0x1F) << 8 ) | buf[readpos+2]; + UCHAR payload = buf[readpos+1] & 0x40; + if (buf[readpos+3] & 0x20) { // Adaptation field is present + datalen -= (buf[readpos+4] + 1); + } + if (datalen < 0) {// Error in stream TODO log this + return 0; + } + if (datalen == 0) {// Null packet + readpos=oldreadpos+TS_SIZE; + continue; + } + readpos += (TS_SIZE - datalen); + UINT towrite=min(datalen,len-readpos); + if (pid == vID) { + if (payload) { + if (started) { + parsePacketDetails(destpaket); + memcpy(buf+writepos,destpaket.getData(),destpaket.getSize()); + writepos+=destpaket.getSize(); + } + destpaket.init(PESTYPE_VID0); + readpos += 6; towrite -= 6; + started=true; + } + + if (started) { + if (!destpaket.write(buf+readpos,towrite)) { + parsePacketDetails(destpaket); + memcpy(buf+writepos,destpaket.getData(),destpaket.getSize()); + writepos+=destpaket.getSize(); + destpaket.truncate(); + destpaket.write((UCHAR*)"\200\000\000", 3); + destpaket.write(buf+readpos,towrite); + + } + } + + + + } + readpos=oldreadpos+TS_SIZE; + } + parsePacketDetails(destpaket); + memcpy(buf+writepos,destpaket.getData(),destpaket.getSize()); + writepos+=destpaket.getSize(); + + return writepos; +} + + + diff --git a/demuxerts.h b/demuxerts.h index 9023d3c..24e5b4d 100644 --- a/demuxerts.h +++ b/demuxerts.h @@ -1,96 +1,96 @@ -/* - Copyright 2006-2007 Mark Calderbank - - 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 DEMUXERTS_H -#define DEMUXERTS_H - -#include "mutex.h" -#include - -#include "demuxer.h" -#include "defines.h" -#include "channel.h" - -#define TS_SIZE 188 -#define TS_SIG 0x47 - -class DemuxerTS : public Demuxer -{ - public: - DemuxerTS(int p_vID = 0, int p_aID = 0, int p_subID = 0, int p_tID = 0); - void flush(); - int scan(UCHAR* buf, int len); - int findPTS(UCHAR* buf, int len, ULLONG* dest); - void setVID(int p_vID); - void setTID(int p_tID); - void setAID(int p_aID, int type); - void setSubID(int p_subID); - int getVID() { return vID; } - int getTID() { return tID; } - int getAID() { return aID; } - int getSubID() { return subID; } - int put(UCHAR* buf, int len); - - void setFrameNum(ULONG frame); - void setPacketNum(ULONG npacket); - ULONG getFrameNumFromPTS(ULLONG pts); - ULONG getPacketNum(); - UINT stripAudio(UCHAR* buf, UINT len); - static bool scanForVideo(UCHAR* buf, UINT len, bool &ish264); - Channel getChannelInfo() {return channelinfo;}; - - - private: - int processTS(UCHAR* buf); - - UCHAR store[TS_SIZE]; // Storage for partial packets - int partPacket; // Length of partial packet stored from previous put() - bool parsed; // Whether PES packet to be submitted has been parsed yet - PESPacket vPacket; // Video PES packet under construction - PESPacket aPacket; // Audio PES packet under construction - PESPacket subPacket; // Subtitles PES packet under construction - PESPacket tPacket; // Teletext PES packet under construction - int vID, aID, subID,tID; // TS IDs for video/audio/subtitles - int PMTPID; //TODO HANS which of these do I Need: - - int atype; - bool subActive; // Same for subtitles - UINT subLength; // Expected length of subtitle packet - bool vActive, aActive, tActive; // Whether video/audio is actively being captured - - bool havechannelinfo; - Channel channelinfo; - - - //TODO HANS which of next do I need - ULONG frameNumber, packetNumber; - bool frameCounting, packetCounting; - bool doubledframerate; - int framereserve; - typedef struct { ULLONG pts; ULONG frame; } PTSMapEntry; - typedef std::deque PTSMap; - PTSMap pts_map; - Mutex pts_map_mutex; - void parseTSPacketDetails(PESPacket &packet); - - -}; - -#endif +/* + Copyright 2006-2007 Mark Calderbank + + 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 DEMUXERTS_H +#define DEMUXERTS_H + +#include "mutex.h" +#include + +#include "demuxer.h" +#include "defines.h" +#include "channel.h" + +#define TS_SIZE 188 +#define TS_SIG 0x47 + +class DemuxerTS : public Demuxer +{ + public: + DemuxerTS(int p_vID = 0, int p_aID = 0, int p_subID = 0, int p_tID = 0); + void flush(); + int scan(UCHAR* buf, int len); + int findPTS(UCHAR* buf, int len, ULLONG* dest); + void setVID(int p_vID); + void setTID(int p_tID); + void setAID(int p_aID, int type); + void setSubID(int p_subID); + int getVID() { return vID; } + int getTID() { return tID; } + int getAID() { return aID; } + int getSubID() { return subID; } + int put(UCHAR* buf, int len); + + void setFrameNum(ULONG frame); + void setPacketNum(ULONG npacket); + ULONG getFrameNumFromPTS(ULLONG pts); + ULONG getPacketNum(); + UINT stripAudio(UCHAR* buf, UINT len); + static bool scanForVideo(UCHAR* buf, UINT len, bool &ish264); + Channel getChannelInfo() {return channelinfo;}; + + + private: + int processTS(UCHAR* buf); + + UCHAR store[TS_SIZE]; // Storage for partial packets + int partPacket; // Length of partial packet stored from previous put() + bool parsed; // Whether PES packet to be submitted has been parsed yet + PESPacket vPacket; // Video PES packet under construction + PESPacket aPacket; // Audio PES packet under construction + PESPacket subPacket; // Subtitles PES packet under construction + PESPacket tPacket; // Teletext PES packet under construction + int vID, aID, subID,tID; // TS IDs for video/audio/subtitles + int PMTPID; //TODO HANS which of these do I Need: + + int atype; + bool subActive; // Same for subtitles + UINT subLength; // Expected length of subtitle packet + bool vActive, aActive, tActive; // Whether video/audio is actively being captured + + bool havechannelinfo; + Channel channelinfo; + + + //TODO HANS which of next do I need + ULONG frameNumber, packetNumber; + bool frameCounting, packetCounting; + // bool doubledframerate; + int framereserve; + typedef struct { ULLONG pts; ULONG frame; } PTSMapEntry; + typedef std::deque PTSMap; + PTSMap pts_map; + Mutex pts_map_mutex; + void parseTSPacketDetails(PESPacket &packet); + + +}; + +#endif diff --git a/demuxervdr.cc b/demuxervdr.cc index abcb921..c3ddfa5 100644 --- a/demuxervdr.cc +++ b/demuxervdr.cc @@ -1,405 +1,396 @@ -/* - Copyright 2005-2008 Mark Calderbank - - 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 "demuxervdr.h" -#include "video.h" -#include "dvbsubtitles.h" -#include "log.h" - -#ifndef WIN32 -#include -#else -#define __LITTLE_ENDIAN 1234 -#define __BIG_ENDIAN 4321 -#define __BYTE_ORDER __LITTLE_ENDIAN -#endif - -#define PTS_JUMP_MARGIN 10000 -#define PTS_ALLOWANCE 90000 - -// TODO: PTS class to handle wrapping arithmetic & comparisons? -static ULLONG PTSDistance(ULLONG pts1, ULLONG pts2) -{ - // Assume pts1, pts2 < 2^33; calculate shortest distance between - ULLONG ret = (pts1 > pts2) ? pts1 - pts2 : pts2 - pts1; - if (ret > (1LL<<32)) ret = (1LL<<33) - ret; - return ret; -} - -static ULLONG PTSDifference(ULLONG pts1, ULLONG pts2) -{ - // Assume pts1, pts2 < 2^33; calculate pts1 - pts2 - if (pts1 > pts2) - return pts1 - pts2; - else - return (1LL<<33) + pts1 - pts2; -} - -DemuxerVDR::DemuxerVDR() -{ - frameCounting = false; - packetCounting = false; - subtitlePacketPosition = 0; -} - -void DemuxerVDR::reset() -{ - frameCounting = false; - packetCounting = false; - pts_map.clear(); - Demuxer::reset(); -} - -void DemuxerVDR::flush() -{ - state = 0; - submitting = false; - subtitlePacketPosition = 0; - Demuxer::flush(); -} - -int DemuxerVDR::scan(UCHAR *buf, int len) -{ - // Temporarily, just look for the lowest audio stream and return it - UCHAR HiByte = PESTYPE_AUDMAX; - int ret = 0; - - while (len >= 4) - { - UINT pattern = *(UINT*)buf; - buf++; len--; - -#if __BYTE_ORDER == __BIG_ENDIAN - if (pattern < (UINT)(0x100 | PESTYPE_AUD0) || - pattern > (UINT)(0x100 | HiByte)) continue; - HiByte = pattern & 0xFF; -#else - if ((pattern & 0xFFFFFF) != 0x010000 || - pattern < ((UINT)PESTYPE_AUD0 << 24) || - pattern > ((UINT)HiByte << 24)) continue; - HiByte = pattern >> 24; -#endif - ret = HiByte; - if (HiByte == PESTYPE_AUD0) break; - } - return ret; -} - -int DemuxerVDR::findPTS(UCHAR* buf, int len, ULLONG* dest) -{ - while (len >= 14) - { - UINT pattern = *(UINT*)buf; - buf++; len--; -#if __BYTE_ORDER == __BIG_ENDIAN - if (pattern < (0x100 | PESTYPE_AUD0) || - pattern > (0x100 | PESTYPE_VIDMAX)) continue; -#else - if ((pattern & 0xFFFFFF) != 0x010000 || - pattern < ((UINT)PESTYPE_AUD0 << 24) || - pattern > ((UINT)PESTYPE_VIDMAX << 24)) continue; -#endif - if ((buf[5] & 0xC0) != 0x80) continue; - - UINT packetlength = ((UINT)buf[3] << 8) | buf[4]; - - if ( buf[6] & 0x80 ) // PTS_DTS_flags indicate that PTS is present - { - if ( (buf[8] & 0x01) != 0x01 || - (buf[10] & 0x01) != 0x01 || - (buf[12] & 0x01) != 0x01) continue; - - *dest = ( (ULLONG)(buf[8] & 0x0E) << 29 ) | - ( (ULLONG)(buf[9]) << 22 ) | - ( (ULLONG)(buf[10] & 0xFE) << 14 ) | - ( (ULLONG)(buf[11]) << 7 ) | - ( (ULLONG)(buf[12] & 0xFE) >> 1 ); - return 1; - } - - buf += 5; len -= 5; - buf += packetlength; len -= packetlength; - } - // No PTS found. - return 0; -} - -void DemuxerVDR::setFrameNum(ULONG frame) -{ - frameCounting = true; - frameNumber = frame; - Log::getInstance()->log("Demuxer", Log::DEBUG, "setFrameNum %d", frame); -} - -void DemuxerVDR::setPacketNum(ULONG npacket) -{ - packetCounting = true; - packetNumber = npacket; - Log::getInstance()->log("Demuxer", Log::DEBUG, "setPacketNum %d", npacket); -} - -int DemuxerVDR::put(UCHAR* buf, int len) -{ - int ret = 0; // return number of bytes consumed - if (submitting) - { - if (submitPacket(packet) == 0) // Still full! - return ret; - else - submitting = false; - } - - if (state > 0) // We are half way through a PES packet. - { - if (len >= state) // The remainder of the packet is available. - { - packet.write(buf, state); - buf += state; len -= state; ret += state; - state = 0; - parseVDRPacketDetails(); - if (submitPacket(packet) == 0) // Stream is full - { - submitting = true; - return ret; - } - } - else // Write what we have, then exit. - { - packet.write(buf, len); - state -= len; - return len; - } - } - - while (len > 0) - { - switch (state) - { - case 0: - case -1: - if (*buf == 0x00) state--; else state = 0; - buf++; len--; ret++; - break; - case -2: - if (*buf == 0x01) state--; else if (*buf != 0x00) state = 0; - buf++; len--; ret++; - break; - case -3: - if ((*buf >= PESTYPE_VID0 && *buf <= PESTYPE_VIDMAX) || - (*buf >= PESTYPE_AUD0 && *buf <= PESTYPE_AUDMAX) || - (*buf == PESTYPE_PRIVATE_1)) - { - packet.init(*buf); - state--; - } - else if (*buf == 0x00) - state = -1; - else - state = 0; - buf++; len--; ret++; - break; - case -4: - packetLength = ((UINT)*buf) << 8; - state--; - buf++; len--; ret++; - break; - case -5: - packetLength += *buf; - state--; - buf++; len--; ret++; - break; - } - - if (state == -6) // Packet header complete - { - if (len >= packetLength) // The entire packet is available. - { - packet.write(buf, packetLength); - buf += packetLength; len -= packetLength; ret += packetLength; - state = 0; - parseVDRPacketDetails(); - if (submitPacket(packet) == 0) // Stream is full - { - submitting = true; - return ret; - } - } - else // Write what we have. - { - packet.write(buf, len); - state = packetLength - len; - ret += len; - len = 0; - } - } - } - return ret; -} - -ULONG DemuxerVDR::getPacketNum() -{ - return packetNumber; -} - -ULONG DemuxerVDR::getFrameNumFromPTS(ULLONG pts) -{ - ULLONG difference = (1LL<<33); - ULONG ref_frame = 0; - int total = 0, actual = 0; - pts_map_mutex.Lock(); - PTSMap::iterator iter = pts_map.begin(); - while (iter != pts_map.end()) - { - ++total; - if (PTSDifference(iter->pts, pts) < PTS_ALLOWANCE) - { - difference = 0; - ref_frame = iter->frame; - actual = total; - break; - } - ULLONG newdiff = PTSDifference(pts, iter->pts); - if (newdiff < difference) - { - difference = newdiff; - ref_frame = iter->frame; - actual = total; - } - ++iter; - } - if (total > 1 && actual == 1) // We are using the most recent PTS ref. - { // Delete the rest. - iter = pts_map.begin(); iter++; - pts_map.erase(iter, pts_map.end()); - } - pts_map_mutex.Unlock(); - - if (difference == (1LL<<33)) - return 0; // We cannot make sense of the pts - else - return ref_frame + (ULONG)((double) (difference / 90000) *fps); -} - -void DemuxerVDR::dealWithSubtitlePacket() -{ - const UCHAR* data = packet.getData(); - UINT packetSize = packet.getSize(); - if (packetSize < 9) return; - UINT payloadOffset = data[8] + 9; - if (packetSize < payloadOffset + 5) return; - if (data[payloadOffset + 3] == 0x00) - { // Not a continuation packet - if (packetSize < payloadOffset + 7) return; - subtitlePacket.init(data[3]); - subtitlePacket.write(&data[6], payloadOffset - 6); - subtitlePacket.write(&data[payloadOffset+4], packetSize-payloadOffset-4); - subtitlePacketPosition = payloadOffset + 2; - } - else - { // Continuation packet - if (subtitlePacketPosition == 0) return; - subtitlePacket.write(&data[payloadOffset+4], packetSize-payloadOffset-4); - } - - const UCHAR* sub_data = subtitlePacket.getData(); - UINT subSize = subtitlePacket.getSize(); - while (subtitlePacketPosition < subSize) - { - if (sub_data[subtitlePacketPosition] == 0xFF) - { // Packet is complete. Switch it into the "real" packet to be submitted. - packet = subtitlePacket; - parsePacketDetails(packet); - subtitlePacketPosition = 0; // Wait for next non-continuation packet - break; - } - if (sub_data[subtitlePacketPosition] != 0x0F) - { - subtitlePacketPosition = 0; // Wait for next non-continuation packet - break; - } - if (subSize < subtitlePacketPosition + 6) break; - UINT segmentLength = (sub_data[subtitlePacketPosition + 4] << 8) - + sub_data[subtitlePacketPosition + 5]; - subtitlePacketPosition += segmentLength + 6; - } -} - -void DemuxerVDR::parseVDRPacketDetails() -{ - if (packet.getPacketType() == PESTYPE_PRIVATE_1 && packet.getSize() > 8) - { - UINT payload_begin = packet[8] + 9; - if (packet.getSize() > payload_begin) - { - UCHAR substream_type = packet[payload_begin] & 0xF0; - if (substream_type == 0x20) // Subtitles - { - dealWithSubtitlePacket(); - } - else // Not subtitles - parsePacketDetails(packet); - } - } - else // Not a private packet*/ - parsePacketDetails(packet); - - if (packetCounting && packet.getPacketType() >= PESTYPE_AUD0 && - packet.getPacketType() <= PESTYPE_AUDMAX) - { - packetNumber++; - } - - if (frameCounting && packet.findPictureHeader(h264) && - packet.getPacketType() >= PESTYPE_VID0 && - packet.getPacketType() <= PESTYPE_VIDMAX) - { - ULONG frame_num = (frameNumber)++; - if (packet.findSeqHeader(h264) > 1 && packet.hasPTS()) - { - PTSMapEntry me; - pts_map_mutex.Lock(); - if (pts_map.empty()) - { - me.pts = packet.getPTS(); - me.frame = frame_num; - pts_map_mutex.Unlock(); -Log::getInstance()->log("Demuxer", Log::DEBUG, "+* PTS INIT *+ %llu %u", me.pts, me.frame); - pts_map_mutex.Lock(); - pts_map.push_front(me); - } - me = pts_map.front(); - pts_map_mutex.Unlock(); - -// UINT fps = Video::getInstance()->getFPS(); - ULLONG pts_expected = me.pts + 90000*((ULONG)((double)(frame_num - me.frame)) / fps); - while (pts_expected > (1LL<<33)) pts_expected -= (1LL<<33); - - if (PTSDistance(pts_expected, packet.getPTS()) > PTS_JUMP_MARGIN) // PTS jump! - { -Log::getInstance()->log("Demuxer", Log::DEBUG, "+* PTS JUMP *+ %llu %u", packet.getPTS(), frame_num); - me.pts = packet.getPTS(); - me.frame = frame_num; - pts_map_mutex.Lock(); - pts_map.push_front(me); - pts_map_mutex.Unlock(); - } - } - } -} +/* + Copyright 2005-2008 Mark Calderbank + + 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 "demuxervdr.h" +#include "video.h" +#include "dvbsubtitles.h" +#include "log.h" + +#ifndef WIN32 +#include +#else +#define __LITTLE_ENDIAN 1234 +#define __BIG_ENDIAN 4321 +#define __BYTE_ORDER __LITTLE_ENDIAN +#endif + +#define PTS_JUMP_MARGIN 10000 +#define PTS_ALLOWANCE 90000 + +// TODO: PTS class to handle wrapping arithmetic & comparisons? +static ULLONG PTSDistance(ULLONG pts1, ULLONG pts2) +{ + // Assume pts1, pts2 < 2^33; calculate shortest distance between + ULLONG ret = (pts1 > pts2) ? pts1 - pts2 : pts2 - pts1; + if (ret > (1LL<<32)) ret = (1LL<<33) - ret; + return ret; +} + +static ULLONG PTSDifference(ULLONG pts1, ULLONG pts2) +{ + // Assume pts1, pts2 < 2^33; calculate pts1 - pts2 + if (pts1 > pts2) + return pts1 - pts2; + else + return (1LL<<33) + pts1 - pts2; +} + +DemuxerVDR::DemuxerVDR() +{ + frameCounting = false; + packetCounting = false; + subtitlePacketPosition = 0; +} + +void DemuxerVDR::reset() +{ + frameCounting = false; + packetCounting = false; + pts_map.clear(); + Demuxer::reset(); +} + +void DemuxerVDR::flush() +{ + state = 0; + submitting = false; + subtitlePacketPosition = 0; + Demuxer::flush(); +} + +int DemuxerVDR::scan(UCHAR *buf, int len) { + // Temporarily, just look for the lowest audio stream and return it + UCHAR HiByte = PESTYPE_AUDMAX; + int ret = 0; + + while (len >= 4) { + UINT pattern = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; + buf++; + len--; + + if (pattern < (UINT)(0x100 | PESTYPE_AUD0) || pattern > (UINT)( + 0x100 | HiByte)) + continue; + HiByte = pattern & 0xFF; + + ret = HiByte; + if (HiByte == PESTYPE_AUD0) + break; + } + return ret; +} + +int DemuxerVDR::findPTS(UCHAR* buf, int len, ULLONG* dest) { + while (len >= 14) { + UINT pattern = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; + buf++; + len--; + if (pattern < (0x100 | PESTYPE_AUD0) || pattern > (0x100 + | PESTYPE_VIDMAX)) + continue; + + if ((buf[5] & 0xC0) != 0x80) + continue; + + UINT packetlength = ((UINT) buf[3] << 8) | buf[4]; + + if (buf[6] & 0x80) // PTS_DTS_flags indicate that PTS is present + { + if ((buf[8] & 0x01) != 0x01 || (buf[10] & 0x01) != 0x01 || (buf[12] + & 0x01) != 0x01) + continue; + + *dest = ((ULLONG)(buf[8] & 0x0E) << 29) | ((ULLONG)(buf[9]) << 22) + | ((ULLONG)(buf[10] & 0xFE) << 14) | ((ULLONG)(buf[11]) + << 7) | ((ULLONG)(buf[12] & 0xFE) >> 1); + return 1; + } + + buf += 5; + len -= 5; + buf += packetlength; + len -= packetlength; + } + // No PTS found. + return 0; +} + +void DemuxerVDR::setFrameNum(ULONG frame) +{ + frameCounting = true; + frameNumber = frame; + Log::getInstance()->log("Demuxer", Log::DEBUG, "setFrameNum %d", frame); +} + +void DemuxerVDR::setPacketNum(ULONG npacket) +{ + packetCounting = true; + packetNumber = npacket; + Log::getInstance()->log("Demuxer", Log::DEBUG, "setPacketNum %d", npacket); +} + +int DemuxerVDR::put(UCHAR* buf, int len) +{ + int ret = 0; // return number of bytes consumed + if (submitting) + { + if (submitPacket(packet) == 0) // Still full! + return ret; + else + submitting = false; + } + + if (state > 0) // We are half way through a PES packet. + { + if (len >= state) // The remainder of the packet is available. + { + packet.write(buf, state); + buf += state; len -= state; ret += state; + state = 0; + parseVDRPacketDetails(); + if (submitPacket(packet) == 0) // Stream is full + { + submitting = true; + return ret; + } + } + else // Write what we have, then exit. + { + packet.write(buf, len); + state -= len; + return len; + } + } + + while (len > 0) + { + switch (state) + { + case 0: + case -1: + if (*buf == 0x00) state--; else state = 0; + buf++; len--; ret++; + break; + case -2: + if (*buf == 0x01) state--; else if (*buf != 0x00) state = 0; + buf++; len--; ret++; + break; + case -3: + if ((*buf >= PESTYPE_VID0 && *buf <= PESTYPE_VIDMAX) || + (*buf >= PESTYPE_AUD0 && *buf <= PESTYPE_AUDMAX) || + (*buf == PESTYPE_PRIVATE_1)) + { + packet.init(*buf); + state--; + } + else if (*buf == 0x00) + state = -1; + else + state = 0; + buf++; len--; ret++; + break; + case -4: + packetLength = ((UINT)*buf) << 8; + state--; + buf++; len--; ret++; + break; + case -5: + packetLength += *buf; + state--; + buf++; len--; ret++; + break; + } + + if (state == -6) // Packet header complete + { + if (len >= packetLength) // The entire packet is available. + { + packet.write(buf, packetLength); + buf += packetLength; len -= packetLength; ret += packetLength; + state = 0; + parseVDRPacketDetails(); + if (submitPacket(packet) == 0) // Stream is full + { + submitting = true; + return ret; + } + } + else // Write what we have. + { + packet.write(buf, len); + state = packetLength - len; + ret += len; + len = 0; + } + } + } + return ret; +} + +ULONG DemuxerVDR::getPacketNum() +{ + return packetNumber; +} + +ULONG DemuxerVDR::getFrameNumFromPTS(ULLONG pts) +{ + ULLONG difference = (1LL<<33); + ULONG ref_frame = 0; + int total = 0, actual = 0; + pts_map_mutex.Lock(); + PTSMap::iterator iter = pts_map.begin(); + while (iter != pts_map.end()) + { + ++total; + if (PTSDifference(iter->pts, pts) < PTS_ALLOWANCE) + { + difference = 0; + ref_frame = iter->frame; + actual = total; + break; + } + ULLONG newdiff = PTSDifference(pts, iter->pts); + if (newdiff < difference) + { + difference = newdiff; + ref_frame = iter->frame; + actual = total; + } + ++iter; + } + if (total > 1 && actual == 1) // We are using the most recent PTS ref. + { // Delete the rest. + iter = pts_map.begin(); iter++; + pts_map.erase(iter, pts_map.end()); + } + pts_map_mutex.Unlock(); + + if (difference == (1LL<<33)) + return 0; // We cannot make sense of the pts + else + return ref_frame + (ULONG)((double) (difference / 90000) *fps); +} + +void DemuxerVDR::dealWithSubtitlePacket() +{ + const UCHAR* data = packet.getData(); + UINT packetSize = packet.getSize(); + if (packetSize < 9) return; + UINT payloadOffset = data[8] + 9; + if (packetSize < payloadOffset + 5) return; + if (data[payloadOffset + 3] == 0x00) + { // Not a continuation packet + if (packetSize < payloadOffset + 7) return; + subtitlePacket.init(data[3]); + subtitlePacket.write(&data[6], payloadOffset - 6); + subtitlePacket.write(&data[payloadOffset+4], packetSize-payloadOffset-4); + subtitlePacketPosition = payloadOffset + 2; + } + else + { // Continuation packet + if (subtitlePacketPosition == 0) return; + subtitlePacket.write(&data[payloadOffset+4], packetSize-payloadOffset-4); + } + + const UCHAR* sub_data = subtitlePacket.getData(); + UINT subSize = subtitlePacket.getSize(); + while (subtitlePacketPosition < subSize) + { + if (sub_data[subtitlePacketPosition] == 0xFF) + { // Packet is complete. Switch it into the "real" packet to be submitted. + packet = subtitlePacket; + parsePacketDetails(packet); + subtitlePacketPosition = 0; // Wait for next non-continuation packet + break; + } + if (sub_data[subtitlePacketPosition] != 0x0F) + { + subtitlePacketPosition = 0; // Wait for next non-continuation packet + break; + } + if (subSize < subtitlePacketPosition + 6) break; + UINT segmentLength = (sub_data[subtitlePacketPosition + 4] << 8) + + sub_data[subtitlePacketPosition + 5]; + subtitlePacketPosition += segmentLength + 6; + } +} + +void DemuxerVDR::parseVDRPacketDetails() +{ + if (packet.getPacketType() == PESTYPE_PRIVATE_1 && packet.getSize() > 8) + { + UINT payload_begin = packet[8] + 9; + if (packet.getSize() > payload_begin) + { + UCHAR substream_type = packet[payload_begin] & 0xF0; + if (substream_type == 0x20) // Subtitles + { + dealWithSubtitlePacket(); + } + else // Not subtitles + parsePacketDetails(packet); + } + } + else // Not a private packet*/ + parsePacketDetails(packet); + + if (packetCounting && packet.getPacketType() >= PESTYPE_AUD0 && + packet.getPacketType() <= PESTYPE_AUDMAX) + { + packetNumber++; + } + + if (frameCounting && packet.findPictureHeader(h264) && + packet.getPacketType() >= PESTYPE_VID0 && + packet.getPacketType() <= PESTYPE_VIDMAX) + { + ULONG frame_num = (frameNumber)++; + if (packet.findSeqHeader(h264) > 1 && packet.hasPTS()) + { + PTSMapEntry me; + pts_map_mutex.Lock(); + if (pts_map.empty()) + { + me.pts = packet.getPTS(); + me.frame = frame_num; + pts_map_mutex.Unlock(); +Log::getInstance()->log("Demuxer", Log::DEBUG, "+* PTS INIT *+ %llu %u", me.pts, me.frame); + pts_map_mutex.Lock(); + pts_map.push_front(me); + } + me = pts_map.front(); + pts_map_mutex.Unlock(); + +// UINT fps = Video::getInstance()->getFPS(); + ULLONG pts_expected = me.pts + 90000*((ULONG)((double)(frame_num - me.frame)) / fps); + while (pts_expected > (1LL<<33)) pts_expected -= (1LL<<33); + + if (PTSDistance(pts_expected, packet.getPTS()) > PTS_JUMP_MARGIN) // PTS jump! + { +Log::getInstance()->log("Demuxer", Log::DEBUG, "+* PTS JUMP *+ %llu %u", packet.getPTS(), frame_num); + me.pts = packet.getPTS(); + me.frame = frame_num; + pts_map_mutex.Lock(); + pts_map.push_front(me); + pts_map_mutex.Unlock(); + } + } + } +} diff --git a/directory.cc b/directory.cc index dec62ff..e158790 100644 --- a/directory.cc +++ b/directory.cc @@ -20,7 +20,7 @@ #include "directory.h" -Directory::Directory(char* newName) +Directory::Directory(const char* newName) { index = -1; diff --git a/directory.h b/directory.h index 7cc49b7..1ad4aa8 100644 --- a/directory.h +++ b/directory.h @@ -36,7 +36,7 @@ typedef vector RecordingList; class Directory { public: - Directory(char* newName); + Directory(const char* newName); ~Directory(); Directory* getDirByName(char* dirName); diff --git a/draintarget.cc b/draintarget.cc deleted file mode 100644 index fddd58b..0000000 --- a/draintarget.cc +++ /dev/null @@ -1,28 +0,0 @@ -/* - 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. -*/ - -#include "draintarget.h" - -DrainTarget::DrainTarget() -{ -} -DrainTarget::~DrainTarget() -{ -} diff --git a/draintarget.h b/draintarget.h index 772b606..fc19a29 100644 --- a/draintarget.h +++ b/draintarget.h @@ -1,105 +1,114 @@ -/* - 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 DRAINTARGET_H -#define DRAINTARGET_H - -#include "defines.h" -#include - -#define MPTYPE_VIDEO_MPEG2 0x00 -#define MPTYPE_MPEG_AUDIO 0x01 -#define MPTYPE_AC3 0x02 -#define MPTYPE_AC3_PRE13 0x03 //old vdr recording compatmode -#define MPTYPE_MPEG_AUDIO_LAYER3 0x04 //for media mp3 playback -#define MPTYPE_TELETEXT 0x05 //for EBU VBI teletext -#define MPTYPE_VIDEO_H264 0x06 - - - -struct MediaPacket -{ - ULONG pos_buffer; //position in stream buffer - ULONG length; //length of the packet - // The fields below are not needed by the MVP - UCHAR type; - ULLONG pts; - ULLONG dts; - bool synched; - int index; -#ifdef WIN32 - long long presentation_time;/* in 100 ns units*/ - bool disconti; -#endif -}; - -using namespace std; -typedef list MediaPacketList; - -class DrainTarget -{ - public: - DrainTarget(); - virtual ~DrainTarget(); - - virtual long long SetStartOffset(long long curreftime, bool *rsync)=0; - virtual void ResetTimeOffsets()=0; - - virtual bool dtsTimefix(){return false;} //determines if the draintargets needs a mixure of pts and dts or not - -// The following two functions are used by the Stream -// to deliver media packets to the front end (DrainTarget). -// -// First, the Stream calls PrepareMediaSample, which gives the front end -// read-only access to the Stream's MediaPacketList. PrepareMediaSample should -// examine the list to determine how much it wishes to consume, and -// should copy any data it requires from the MediaPacket objects into -// local storage. -// This function call takes place under a mutex lock to ensure integrity -// of the list structure. It should be fast and must not contain any -// cancellation points, such as I/O calls for logging. -// -// Second, the Stream releases the mutex and calls DeliverMediaSample. -// This function delivers data from the Stream buffer to the presentation -// device and returns information to the Stream regarding how many MediaPackets -// were consumed. Any data copied from the MediaPackets objects during -// PrepareMediaSample is guaranteed by the Stream still to be valid -// during the following DeliverMediaSample call. - - // samplepos is equal to the number of bytes from the first MediaPacket - // in the list that have already been consumed in a previous call. - virtual void PrepareMediaSample(const MediaPacketList&, UINT samplepos)=0; - - // The Stream guarantees that the value of *samplepos passed to - // DeliverMediaSample will be equal to the value of samplepos passed to - // PrepareMediaSample in the previous call. - // This function should consume data from the buffer according to the - // decisions made in PrepareMediaSample. Its return value and *samplepos - // tell the Stream how much data it consumed. - // If DeliverMediaSample returns X, the Stream will remove packets 0 to X-1 - // (inclusive) from the list before the next call. - // DeliverMediaSample must also set *samplepos equal to the number of bytes - // processed from packet X (usually zero). - // It is allowed, that the draintarget modifies the part of the buffer, which belongs - // to the mediapackets it is processing. - virtual UINT DeliverMediaSample(UCHAR* buffer, UINT *samplepos)=0; -}; - -#endif +/* + 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 DRAINTARGET_H +#define DRAINTARGET_H + +#include "defines.h" +#include + +#define MPTYPE_VIDEO_MPEG2 0x00 +#define MPTYPE_MPEG_AUDIO 0x01 +#define MPTYPE_AC3 0x02 +#define MPTYPE_AC3_PRE13 0x03 //old vdr recording compatmode +#define MPTYPE_MPEG_AUDIO_LAYER3 0x04 //for media mp3 playback +#define MPTYPE_TELETEXT 0x05 //for EBU VBI teletext +#define MPTYPE_VIDEO_H264 0x06 + + + +struct MediaPacket +{ + ULONG pos_buffer; //position in stream buffer + ULONG length; //length of the packet + // The fields below are not needed by the MVP + UCHAR type; + ULLONG pts; + ULLONG dts; + bool synched; + int index; +#if defined(WIN32) || defined(__ANDROID__) + long long presentation_time;/* in 100 ns units*/ + bool disconti; +#endif +}; + +using namespace std; +typedef list MediaPacketList; + + + +class DrainTarget +{ + public: + DrainTarget() { + + } + virtual ~DrainTarget(){ + + } + + virtual long long SetStartOffset(long long curreftime, bool *rsync)=0; + virtual void ResetTimeOffsets()=0; + + virtual bool dtsTimefix(){return false;} //determines if the draintargets needs a mixure of pts and dts or not + +// The following two functions are used by the Stream +// to deliver media packets to the front end (DrainTarget). +// +// First, the Stream calls PrepareMediaSample, which gives the front end +// read-only access to the Stream's MediaPacketList. PrepareMediaSample should +// examine the list to determine how much it wishes to consume, and +// should copy any data it requires from the MediaPacket objects into +// local storage. +// This function call takes place under a mutex lock to ensure integrity +// of the list structure. It should be fast and must not contain any +// cancellation points, such as I/O calls for logging. +// +// Second, the Stream releases the mutex and calls DeliverMediaSample. +// This function delivers data from the Stream buffer to the presentation +// device and returns information to the Stream regarding how many MediaPackets +// were consumed. Any data copied from the MediaPackets objects during +// PrepareMediaSample is guaranteed by the Stream still to be valid +// during the following DeliverMediaSample call. + + // samplepos is equal to the number of bytes from the first MediaPacket + // in the list that have already been consumed in a previous call. + virtual void PrepareMediaSample(const MediaPacketList&, UINT samplepos)=0; + + // The Stream guarantees that the value of *samplepos passed to + // DeliverMediaSample will be equal to the value of samplepos passed to + // PrepareMediaSample in the previous call. + // This function should consume data from the buffer according to the + // decisions made in PrepareMediaSample. Its return value and *samplepos + // tell the Stream how much data it consumed. + // If DeliverMediaSample returns X, the Stream will remove packets 0 to X-1 + // (inclusive) from the list before the next call. + // DeliverMediaSample must also set *samplepos equal to the number of bytes + // processed from packet X (usually zero). + // It is allowed, that the draintarget modifies the part of the buffer, which belongs + // to the mediapackets it is processing. + virtual UINT DeliverMediaSample(UCHAR* buffer, UINT *samplepos)=0; + // The drain target might advice the feeder about free buffers with threadSignal + + +}; + +#endif diff --git a/dvbsubtitles.h b/dvbsubtitles.h index 71531c7..d720396 100644 --- a/dvbsubtitles.h +++ b/dvbsubtitles.h @@ -21,11 +21,8 @@ #ifndef DVBSUBTITLES_H #define DVBSUBTITLES_H -#ifdef WIN32 -#include "threadwin.h" -#else -#include "threadp.h" -#endif +#include "threadsystem.h" + #include "bitmap.h" #include "demuxer.h" #include diff --git a/i18n.cc b/i18n.cc index 5d07f75..091701b 100644 --- a/i18n.cc +++ b/i18n.cc @@ -22,9 +22,11 @@ #include #include -#ifndef WIN32 + +//Nothing is using this ? +/*#ifndef WIN32 #include -#endif +#endif*/ #include "vdr.h" #include "log.h" diff --git a/imagereader.h b/imagereader.h index 3d2bb18..533a057 100644 --- a/imagereader.h +++ b/imagereader.h @@ -1,120 +1,116 @@ -/* - Copyright 2004-2006 Chris Tallon, Andreas Vogel - - 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 IMAGEREADER_H -#define IMAGEREADER_H - -#include -#include -#ifndef WIN32 -#include -#endif -#include - -#include "callback.h" -#include "thread.h" - -#ifdef WIN32 -#include "threadwin.h" -#else -#include "threadp.h" -#endif - - - -class MediaProvider; -class Log; - - -class ImageReader : public Thread_TYPE, public Callback -{ - public: - - //create an image reader for a media channel - //the channel must already been opened - ImageReader(int channel,MediaProvider *p); - //call shutdown before destroying the reader! - virtual ~ImageReader(); - //request the next image block - //will return the current block (if already read) and request the next from the server - //rsize will return the received len: 0 on EOF, -1 on error - //the returned buffer has to be freed outside (really use free!) - //if the buffer is not filled it will wait until the chunk is received! - int getImageChunk(ULLONG offset,UINT len, UINT * rsize,UCHAR **buffer); - - //is the reader still running? - bool isReaderRunning(); - - void shutdown(); - - //stop the reader (waits until the reader thread does not access anything) - void stop(); - - - virtual void call(void * caller); - - - protected: - void threadMethod(); - void threadPostStopCleanup(); - - private: - MediaProvider * provider; - int channel; - static const int MAXCHUNKS=2; - //start the player thread - void run(); - Log* logger; - - bool running; - bool readerRunning; - void waitTimed(int ms); - - typedef enum { - S_REQUEST=1, - S_FETCHING=2, - S_READY=3, - S_BREAK=4, - S_FREE=0 - } rstate; - - class Chunk{ - public: - UCHAR * buffer; //receive buffer (to be deallocated with free) - ULLONG offset; //offset within stream/file - UINT reqlen; //requested len - ULONG len; //received len - rstate state; //current state - Chunk() { - buffer=NULL; - offset=0; - reqlen=0; - len=0; - state=S_FREE; - } - }; - //received data - //setting state in this array requires the thread lock - Chunk data[MAXCHUNKS]; - -}; - -#endif - +/* + Copyright 2004-2006 Chris Tallon, Andreas Vogel + + 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 IMAGEREADER_H +#define IMAGEREADER_H + +#include +#include +#ifndef WIN32 +#include +#endif +#include + +#include "callback.h" +#include "thread.h" + +#include "threadsystem.h" + + + +class MediaProvider; +class Log; + + +class ImageReader : public Thread_TYPE, public Callback +{ + public: + + //create an image reader for a media channel + //the channel must already been opened + ImageReader(int channel,MediaProvider *p); + //call shutdown before destroying the reader! + virtual ~ImageReader(); + //request the next image block + //will return the current block (if already read) and request the next from the server + //rsize will return the received len: 0 on EOF, -1 on error + //the returned buffer has to be freed outside (really use free!) + //if the buffer is not filled it will wait until the chunk is received! + int getImageChunk(ULLONG offset,UINT len, UINT * rsize,UCHAR **buffer); + + //is the reader still running? + bool isReaderRunning(); + + void shutdown(); + + //stop the reader (waits until the reader thread does not access anything) + void stop(); + + + virtual void call(void * caller); + + + protected: + void threadMethod(); + void threadPostStopCleanup(); + + private: + MediaProvider * provider; + int channel; + static const int MAXCHUNKS=2; + //start the player thread + void run(); + Log* logger; + + bool running; + bool readerRunning; + void waitTimed(int ms); + + typedef enum { + S_REQUEST=1, + S_FETCHING=2, + S_READY=3, + S_BREAK=4, + S_FREE=0 + } rstate; + + class Chunk{ + public: + UCHAR * buffer; //receive buffer (to be deallocated with free) + ULLONG offset; //offset within stream/file + UINT reqlen; //requested len + ULONG len; //received len + rstate state; //current state + Chunk() { + buffer=NULL; + offset=0; + reqlen=0; + len=0; + state=S_FREE; + } + }; + //received data + //setting state in this array requires the thread lock + Chunk data[MAXCHUNKS]; + +}; + +#endif + diff --git a/ledmvp.h b/ledmvp.h index 5ae39f9..b502216 100644 --- a/ledmvp.h +++ b/ledmvp.h @@ -32,7 +32,7 @@ class LedMVP : public Led { public: LedMVP(); - ~LedMVP(); + virtual ~LedMVP(); int init(int device); int shutdown(); diff --git a/log.cc b/log.cc index 3aff96c..5ed65f6 100644 --- a/log.cc +++ b/log.cc @@ -1,188 +1,216 @@ -/* - Copyright 2004-2005 Chris Tallon - Copyright 2003-2004 University Of Bradford - - 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 "log.h" - -#include "vdr.h" - -Log* Log::instance = NULL; - -Log::Log() -{ - if (instance) return; - instance = this; - logfile = NULL; - initted = 0; - logLevel = 0; - netLog = false; -} - -Log::~Log() -{ - instance = NULL; -} - -Log* Log::getInstance() -{ - return instance; -} - -void Log::upLogLevel() -{ - if (logLevel == Log::DEBUG) - { - log("Log", logLevel, "Log level is at its highest already"); - return; - } - - logLevel++; - log("Log", logLevel, "Log level is now %i", logLevel); -} - -void Log::downLogLevel() -{ - if (logLevel == Log::CRAZY) - { - log("Log", logLevel, "Log level is at its lowest already"); - return; - } - - logLevel--; - log("Log", logLevel, "Log level is now %i", logLevel); -} - -int Log::init(int startLogLevel, char* fileName, int tenabled) -{ - initted = 1; - logLevel = startLogLevel; - enabled = tenabled; -// logfile = fopen(fileName, "a"); -// logfile = fopen(stdout, "a"); - logfile = stdout; -// logfile = fopen("/log", "a"); - - if (logfile) return 1; - else return 0; -} - -int Log::shutdown() -{ - if (!initted) return 1; - if (enabled) fclose(logfile); - return 1; -} - -int Log::log(char *fromModule, int level, char* message, ...) -{ - if (!instance || !logfile) return 0; - - if (!enabled && !netLog) return 1; - if (level > logLevel) return 1; - - char buffer[151]; - int spaceLeft = 150; - -#ifndef _MSC_VER - struct timeval tv; - gettimeofday(&tv, NULL); - struct tm* tms = localtime(&tv.tv_sec); -#else - struct _timeb tb; - _ftime(&tb); - struct tm* tms = localtime(&tb.time); -#endif - spaceLeft -= strftime(buffer, spaceLeft, "%H:%M:%S.", tms); -#ifndef _MSC_VER - spaceLeft -= SNPRINTF(&buffer[150-spaceLeft], spaceLeft, "%06lu ", (unsigned long)tv.tv_usec); -#else - spaceLeft -= SNPRINTF(&buffer[150-spaceLeft], spaceLeft, "%06lu ", (unsigned long)tb.millitm); -#endif - - char levelString[10]; - if (level == CRAZY) strcpy(levelString, "[CRAZY] "); - if (level == EMERG) strcpy(levelString, "[EMERG] "); - if (level == ALERT) strcpy(levelString, "[ALERT] "); - if (level == CRIT) strcpy(levelString, "[CRIT] "); - if (level == ERR) strcpy(levelString, "[ERR] "); - if (level == WARN) strcpy(levelString, "[WARN] "); - if (level == NOTICE) strcpy(levelString, "[notice]"); - if (level == INFO) strcpy(levelString, "[info] "); - if (level == DEBUG) strcpy(levelString, "[debug] "); - -#ifndef WIN32 - spaceLeft -= SNPRINTF(&buffer[150-spaceLeft], spaceLeft, "%s %d %s - ", levelString, getpid(), fromModule); -#else - spaceLeft -= SNPRINTF(&buffer[150-spaceLeft], spaceLeft, "%s %s - ", levelString, fromModule); -#endif - - va_list ap; - va_start(ap, message); - spaceLeft = VSNPRINTF(&buffer[150-spaceLeft], spaceLeft, message, ap); - va_end(ap); - - int messageLength = strlen(buffer); - if (messageLength < 150) - { - buffer[messageLength] = '\n'; - buffer[messageLength+1] = '\0'; - } - else - { - buffer[149] = '\n'; - buffer[150] = '\0'; - } - - int success = 1; - if (enabled) - { - success = fputs(buffer, logfile); - fflush(NULL); - } - - if (netLog) vdr->networkLog(buffer); - - if (success != EOF) - return 1; - else - return 0; - -} - -int Log::status() -{ - if (instance && logfile) return 1; - else return 0; -} - -void Log::netLogOn() -{ - vdr = VDR::getInstance(); - netLog = true; - log("Log", Log::DEBUG, "Network logging started"); -} - -void Log::netLogOff() -{ - netLog = false; - vdr = NULL; -} - - +/* + Copyright 2004-2005 Chris Tallon + Copyright 2003-2004 University Of Bradford + + 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 "log.h" + +#include "vdr.h" + +#ifdef __ANDROID__ +#include +#endif + +Log* Log::instance = NULL; + +Log::Log() +{ + if (instance) return; + instance = this; + logfile = NULL; + initted = 0; + logLevel = 0; + extlog = NULL; +} + +Log::~Log() +{ + instance = NULL; +} + +Log* Log::getInstance() +{ + return instance; +} + +void Log::upLogLevel() +{ + if (logLevel == Log::DEBUG) + { + log("Log", logLevel, "Log level is at its highest already"); + return; + } + + logLevel++; + log("Log", logLevel, "Log level is now %i", logLevel); +} + +void Log::downLogLevel() +{ + if (logLevel == Log::CRAZY) + { + log("Log", logLevel, "Log level is at its lowest already"); + return; + } + + logLevel--; + log("Log", logLevel, "Log level is now %i", logLevel); +} + +int Log::init(int startLogLevel,const char* fileName, int tenabled) +{ + initted = 1; + logLevel = startLogLevel; + enabled = tenabled; +// logfile = fopen(fileName, "a"); +// logfile = fopen(stdout, "a"); + logfile = stdout; +// logfile = fopen("/log", "a"); + + if (logfile) return 1; + else return 0; +} + +int Log::shutdown() +{ + if (!initted) return 1; + if (enabled) fclose(logfile); + return 1; +} + +int Log::log(const char *fromModule, int level,const char* message, ...) +{ + if (!instance || !logfile) return 0; + + if (!enabled && !extlog) return 1; + if (level > logLevel) return 1; + + char buffer[151]; + int spaceLeft = 150; + +#ifndef _MSC_VER + struct timeval tv; + gettimeofday(&tv, NULL); + struct tm* tms = localtime(&tv.tv_sec); +#else + struct _timeb tb; + _ftime(&tb); + struct tm* tms = localtime(&tb.time); +#endif + spaceLeft -= strftime(buffer, spaceLeft, "%H:%M:%S.", tms); +#ifndef _MSC_VER + spaceLeft -= SNPRINTF(&buffer[150-spaceLeft], spaceLeft, "%06lu ", (unsigned long)tv.tv_usec); +#else + spaceLeft -= SNPRINTF(&buffer[150-spaceLeft], spaceLeft, "%06lu ", (unsigned long)tb.millitm); +#endif + + char levelString[10]; + if (level == CRAZY) strcpy(levelString, "[CRAZY] "); + if (level == EMERG) strcpy(levelString, "[EMERG] "); + if (level == ALERT) strcpy(levelString, "[ALERT] "); + if (level == CRIT) strcpy(levelString, "[CRIT] "); + if (level == ERR) strcpy(levelString, "[ERR] "); + if (level == WARN) strcpy(levelString, "[WARN] "); + if (level == NOTICE) strcpy(levelString, "[notice]"); + if (level == INFO) strcpy(levelString, "[info] "); + if (level == DEBUG) strcpy(levelString, "[debug] "); + +#ifndef WIN32 + spaceLeft -= SNPRINTF(&buffer[150-spaceLeft], spaceLeft, "%s %d %s - ", levelString, getpid(), fromModule); +#else + spaceLeft -= SNPRINTF(&buffer[150-spaceLeft], spaceLeft, "%s %s - ", levelString, fromModule); +#endif + + va_list ap; + va_start(ap, message); + spaceLeft = VSNPRINTF(&buffer[150-spaceLeft], spaceLeft, message, ap); + va_end(ap); + + int messageLength = strlen(buffer); + if (messageLength < 150) + { + buffer[messageLength] = '\n'; + buffer[messageLength+1] = '\0'; + } + else + { + buffer[149] = '\n'; + buffer[150] = '\0'; + } + + int success = 1; + if (enabled) + { +#ifndef __ANDROID__ + success = fputs(buffer, logfile); + fflush(NULL); +#else + int and_level=0; + switch (level) { + case CRAZY: + case ALERT: + case EMERG : + case CRIT:{ + and_level=ANDROID_LOG_FATAL; + } break; + case ERR: { + and_level=ANDROID_LOG_ERROR; + } break; + case WARN: { + and_level=ANDROID_LOG_WARN; + } break; + case NOTICE: + case INFO: { + and_level=ANDROID_LOG_INFO; + } break; + case DEBUG :{ + and_level=ANDROID_LOG_DEBUG; + } break; + }; + __android_log_vprint(and_level, fromModule, + message, ap); +#endif + } + + if (extlog) extlog->LogExtern(buffer); //Replacement for network logging + + + if (success != EOF) + return 1; + else + return 0; + +} + + + +int Log::status() +{ + if (instance && logfile) return 1; + else return 0; +} + +void Log::setExternLogger(ExternLogger* login) { + extlog=login; + log("Log", Log::DEBUG, "Extern logging started"); +} + + + + diff --git a/log.h b/log.h index 5e5e11e..ed24b8b 100644 --- a/log.h +++ b/log.h @@ -1,104 +1,114 @@ -/* - Copyright 2004-2005 Chris Tallon - Copyright 2003-2004 University Of Bradford - - 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 LOG_H -#define LOG_H - -#include -#ifndef WIN32 - #include - #include -#else - #include -#endif - -#include -#include -#include -#include -#include "defines.h" - -class VDR; - -class Log -{ - public: - Log(); - ~Log(); - static Log* getInstance(); - - int init(int defaultLevel, char* fileName, int enabled); - int shutdown(); - int log(char *fromModule, int level, char *message, ...); - int status(); - void upLogLevel(); - void downLogLevel(); - void netLogOn(); - void netLogOff(); - - const static int CRAZY = 0; // mad crazy things that should never happen - const static int EMERG = 1; // human assist required NOW - const static int ALERT = 2; // system unusable, but happy to sit there - const static int CRIT = 3; // still working, but maybe about to die - const static int ERR = 4; // that response is not even listed... - const static int WARN = 5; // this could be a bad thing. still running tho - const static int NOTICE = 6; // significant good thing - const static int INFO = 7; // verbose good thing - const static int DEBUG = 8; // debug-level messages - - private: - static Log* instance; - int initted; - int logLevel; - int enabled; - - FILE *logfile; - - bool netLog; - VDR* vdr; -}; - -#endif - -/* - -Documentation -------------- - -This class is intended to be instatiated once by the core. -For a one off use: - -Log::getInstance()->log("", Log::, ""); - -Or, a pointer can be stored and used: - -Log *myptr = Log::getInstance(); - -myptr->log("", Log::, ""); -myptr->log("", Log::, ""); - -Level usages are above. - -The message parameter in the log function can be used in the same way as printf, eg. - -myptr->log("", Log::, "Success: %s %i", stringpointer, integer); - -*/ +/* + Copyright 2004-2005 Chris Tallon + Copyright 2003-2004 University Of Bradford + + 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 LOG_H +#define LOG_H + +#include +#ifndef WIN32 + #include + #include +#else + #include +#endif + +#include +#include +#include +#include +#include "defines.h" + + +class ExternLogger { +public: + virtual bool LogExtern(const char* message)=0; +}; + + +class Log +{ + public: + Log(); + ~Log(); + static Log* getInstance(); + + int init(int defaultLevel,const char* fileName, int enabled); + int shutdown(); + int log(const char *fromModule, int level,const char *message, ...); + int status(); + void upLogLevel(); + void downLogLevel(); + void setExternLogger(ExternLogger* log); + void unsetExternLogger() { + extlog=NULL; + } + + + + const static int CRAZY = 0; // mad crazy things that should never happen + const static int EMERG = 1; // human assist required NOW + const static int ALERT = 2; // system unusable, but happy to sit there + const static int CRIT = 3; // still working, but maybe about to die + const static int ERR = 4; // that response is not even listed... + const static int WARN = 5; // this could be a bad thing. still running tho + const static int NOTICE = 6; // significant good thing + const static int INFO = 7; // verbose good thing + const static int DEBUG = 8; // debug-level messages + + private: + static Log* instance; + int initted; + int logLevel; + int enabled; + + FILE *logfile; + + ExternLogger* extlog; + + +}; + +#endif + +/* + +Documentation +------------- + +This class is intended to be instatiated once by the core. +For a one off use: + +Log::getInstance()->log("", Log::, ""); + +Or, a pointer can be stored and used: + +Log *myptr = Log::getInstance(); + +myptr->log("", Log::, ""); +myptr->log("", Log::, ""); + +Level usages are above. + +The message parameter in the log function can be used in the same way as printf, eg. + +myptr->log("", Log::, "Success: %s %i", stringpointer, integer); + +*/ diff --git a/main.cc b/main.cc index 3d4ad20..226f3e2 100644 --- a/main.cc +++ b/main.cc @@ -1,573 +1,579 @@ -/* - 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. -*/ - -#include -#include -#include -#include -#ifndef WIN32 -#include -#include -#endif - -#include "defines.h" -#include "log.h" -#include "timers.h" -#include "vdr.h" -#include "boxstack.h" -#include "command.h" - -#ifndef _MIPS_ARCH - -#include "mtdmvp.h" -#include "remotemvp.h" -#include "ledmvp.h" -#include "osdmvp.h" -#include "audiomvp.h" -#include "videomvp.h" - -#else - -#include "mtdnmt.h" -#include "remotelirc.h" -#include "lednmt.h" -#include "osddirectfb.h" -#include "audionmt.h" -#include "videonmt.h" - -#endif - - -#include "wol.h" -#include "vsleeptimer.h" - - -#ifndef WIN32 -void sighandler(int signalReceived); -#endif - -void shutdown(int code); - -#ifndef _MIPS_ARCH - -extern "C" -{ - int ticonfig_main(int, char**); -} - -#endif - -// Global variables -------------------------------------------------------------------------------------------------- -Log* logger; -Remote* remote; -Mtd* mtd; -Led* led; -Osd* osd; -Timers* timers; -BoxStack* boxstack; -Command* command; -VDR* vdr; -Video* video; -Audio* audio; -Wol* wol; -Sleeptimer* sleeptimer; - -// Linux MVP main function and sighandler -#ifndef WIN32 -int main(int argc, char** argv) -{ -#ifndef _MIPS_ARCH - if (strstr(argv[0], "ticonfig")) return ticonfig_main(argc, argv); -#endif - - bool daemonize = true; - bool debugEnabled = false; - bool crashed = false; - char* setServer = NULL; - int c; - - while ((c = getopt(argc, argv, "cdns:")) != -1) - { - switch (c) - { - case 'c': - crashed = true; - break; - case 'd': - debugEnabled = true; // and... - case 'n': - daemonize = false; - break; - case 's': - setServer = optarg; - break; - case '?': - printf("Unknown option\n"); - return 1; - default: - printf("Option error\n"); - return 1; - } - } - - // Init global vars ------------------------------------------------------------------------------------------------ - logger = new Log(); - timers = new Timers(); - vdr = new VDR(); -#ifndef _MIPS_ARCH - mtd = new MtdMVP(); - remote = new RemoteMVP(); - led = new LedMVP(); - osd = new OsdMVP(); - audio = new AudioMVP(); - video = new VideoMVP(); -#else - mtd = new MtdNMT(); - remote = new RemoteLirc(); - led = new LedNMT(); - osd = new OsdDirectFB(); - audio = new AudioNMT(); - video = new VideoNMT(); -#endif - - - boxstack = new BoxStack(); - command = new Command(); - wol = new Wol(); - sleeptimer = new Sleeptimer(); - - if (!logger || !remote || !mtd || !led || !osd || !video || !audio || !boxstack || !command || !wol || !sleeptimer) - { - printf("Could not create objects. Memory problems?\n"); - shutdown(1); - } - - // Get logging module started -------------------------------------------------------------------------------------- - - if (!logger->init(Log::DEBUG, "dummy", debugEnabled ? 1 : 0)) - { - printf("Could not initialise log object. Aborting.\n"); - shutdown(1); - } - - logger->log("Core", Log::INFO, "Starting up..."); - - // Daemonize if not -d - - if (daemonize) - { - // Fork away - pid_t forkTest = fork(); - if (forkTest == -1) - { printf("Cannot fork (1).\n"); exit(1); } - if (forkTest != 0) _exit(0); // PID returned, I am the parent - // otherwise, I am the child - setsid(); - forkTest = fork(); - if (forkTest == -1) - { printf("Cannot fork (2).\n"); exit(1); } - if (forkTest != 0) _exit(0); // PID returned, I am the parent - // otherwise, I am the child - close(0); - close(1); - close(2); - } - - // Set up signal handling ------------------------------------------------------------------------------------------ - - sighandler_t sigtest; - - sigtest = signal(SIGPIPE, SIG_IGN); - if (sigtest == SIG_ERR) - { - logger->log("Core", Log::EMERG, "Could not set up signal handler for SIGPIPE. Aborting."); - shutdown(1); - } - sigtest = signal(SIGINT, sighandler); - if (sigtest == SIG_ERR) - { - logger->log("Core", Log::EMERG, "Could not set up signal handler for SIGINT. Aborting."); - shutdown(1); - } - sigtest = signal(SIGTERM, sighandler); - if (sigtest == SIG_ERR) - { - logger->log("Core", Log::EMERG, "Could not set up signal handler for SIGTERM. Aborting."); - shutdown(1); - } - sigtest = signal(SIGUSR1, sighandler); - if (sigtest == SIG_ERR) - { - logger->log("Core", Log::EMERG, "Could not set up signal handler for SIGUSR1. Aborting."); - shutdown(1); - } -/* - sigtest = signal(SIGUSR2, sighandler); - if (sigtest == SIG_ERR) - { - logger->log("Core", Log::EMERG, "Could not set up signal handler for SIGUSR2. Aborting."); - shutdown(1); - } -*/ - sigtest = signal(SIGURG, sighandler); - if (sigtest == SIG_ERR) - { - logger->log("Core", Log::EMERG, "Could not set up signal handler for SIGURG. Aborting."); - shutdown(1); - } - - logger->log("Core", Log::INFO, "Signal handlers set up successfully"); - - - // Init modules ---------------------------------------------------------------------------------------------------- - int success; -#ifndef _MIPS_ARCH - success = remote->init("/dev/rawir"); -#else - success = remote->init("/dev/lircd"); -#endif - if (success) - { - logger->log("Core", Log::INFO, "Remote module initialised"); - } - else - { - logger->log("Core", Log::EMERG, "Remote module failed to initialise"); - shutdown(1); - } -#ifndef _MIPS_ARCH - success = led->init(((RemoteMVP*)remote)->getDevice()); -#else - success = led->init(-1); -#endif - if (success) - { - logger->log("Core", Log::INFO, "LED module initialised"); - } - else - { - logger->log("Core", Log::EMERG, "LED module failed to initialise"); - shutdown(1); - } - - success = mtd->init(); - if (success) - { - logger->log("Core", Log::INFO, "Mtd module initialised"); - } - else - { - logger->log("Core", Log::EMERG, "Mtd module failed to initialise"); - shutdown(1); - } - - success = timers->init(); - if (success) - { - logger->log("Core", Log::INFO, "Timers module initialised"); - } - else - { - logger->log("Core", Log::EMERG, "Timers module failed to initialise"); - shutdown(1); - } - - UCHAR videoFormat = (UCHAR)mtd->getPALorNTSC(); - if (videoFormat == Video::PAL) logger->log("Core", Log::INFO, "Read from MTD: PAL 720x576"); - else if (videoFormat == Video::NTSC) logger->log("Core", Log::INFO, "Read from MTD: NTSC 720x480"); - else logger->log("Core", Log::INFO, "No help from MTD. Assuming NTSC 720x480"); - - success = video->init(videoFormat); - if (success) - { - logger->log("Core", Log::INFO, "Video module initialised"); - } - else - { - logger->log("Core", Log::EMERG, "Video module failed to initialise"); - shutdown(1); - } - - success = osd->init((void*)("/dev/stbgfx")); - if (success) - { - logger->log("Core", Log::INFO, "OSD module initialised"); - } - else - { - logger->log("Core", Log::EMERG, "OSD module failed to initialise"); - shutdown(1); - } - - success = audio->init(Audio::MPEG2_PES); - if (success) - { - logger->log("Core", Log::INFO, "Audio module initialised"); - } - else - { - logger->log("Core", Log::EMERG, "Audio module failed to initialise"); - shutdown(1); - } - - success = vdr->init(3024); - if (success) - { - logger->log("Core", Log::INFO, "VDR module initialised"); - } - else - { - logger->log("Core", Log::EMERG, "VDR module failed to initialise"); - shutdown(1); - } - - success = boxstack->init(); - if (success) - { - logger->log("Core", Log::INFO, "BoxStack module initialised"); - } - else - { - logger->log("Core", Log::EMERG, "BoxStack module failed to initialise"); - shutdown(1); - } - - success = command->init(crashed, setServer); - if (success) - { - logger->log("Core", Log::INFO, "Command module initialised"); - } - else - { - logger->log("Core", Log::EMERG, "Command module failed to initialise"); - shutdown(1); - } - - // Other init ------------------------------------------------------------------------------------------------------ - - logger->log("Core", Log::NOTICE, "Startup successful"); - - // Run main loop --------------------------------------------------------------------------------------------------- - - // Ok, all major device components and other bits are loaded and ready - command->run(); - - // When that returns quit ------------------------------------------------------------------------------------------ - - shutdown(0); - return 0; -} - -// ------------------------------------------------------------------------------------------------------------------- - -void sighandler(int signalReceived) -{ - logger->log("Core", Log::NOTICE, "Signal %i received", signalReceived); - - switch (signalReceived) - { - case SIGINT: - { - logger->log("Core", Log::NOTICE, "Interrupt signal, shutting down..."); - command->stop(); // FIXME this is probably not safe - use the messaging system / is that even safe? - break; - } - case SIGTERM: - { - logger->log("Core", Log::NOTICE, "Term signal, shutting down..."); - command->stop(); // FIXME this is probably not safe - use the messaging system / is that even safe? - break; - } - case SIGUSR1: - { - command->sig1(); - break; - } -/* - case SIGUSR1: - { - logger->log("Core", Log::DEBUG, "SIGUSR1 caught"); - logger->upLogLevel(); - break; - } - case SIGUSR2: - { - logger->log("Core", Log::DEBUG, "SIGUSR2 caught"); - logger->downLogLevel(); - break; - } -*/ - case SIGURG: - { - logger->log("Core", Log::DEBUG, "SIGURG caught"); - break; - } - } -} -#endif - -// ------------------------------------------------------------------------------------------------------------------- - -void shutdown(int code) -{ - if (boxstack) - { - boxstack->shutdown(); - delete boxstack; - logger->log("Core", Log::NOTICE, "BoxStack module shut down"); - } - - // FIXME, send a del all to boxstack first, then get rid of it after command? - if (command) // shut down command here in case views have posted messages - { - command->shutdown(); - delete command; - logger->log("Core", Log::NOTICE, "Command module shut down"); - } - - if (vdr) - { - vdr->shutdown(); - delete vdr; - logger->log("Core", Log::NOTICE, "VDR module shut down"); - } - - if (osd) - { - osd->shutdown(); - delete osd; - logger->log("Core", Log::NOTICE, "OSD module shut down"); - } - - if (audio) - { - audio->shutdown(); - delete audio; - logger->log("Core", Log::NOTICE, "Audio module shut down"); - } - - if (video) - { - video->shutdown(); - delete video; - logger->log("Core", Log::NOTICE, "Video module shut down"); - } - - if (timers) - { - timers->shutdown(); - delete timers; - logger->log("Core", Log::NOTICE, "Timers module shut down"); - } - - if (mtd) - { - mtd->shutdown(); - delete mtd; - logger->log("Core", Log::NOTICE, "MTD module shut down"); - } - - if (led) - { - led->shutdown(); - delete led; - logger->log("Core", Log::NOTICE, "LED module shut down"); - } - - if (remote) - { - remote->shutdown(); - delete remote; - logger->log("Core", Log::NOTICE, "Remote module shut down"); - } - - if (wol) - { - delete wol; - logger->log("Core", Log::NOTICE, "WOL module shut down"); - } - - if (sleeptimer) - { - delete sleeptimer; - logger->log("Core", Log::NOTICE, "Sleeptimer module shut down"); - } - - if (logger) - { - logger->log("Core", Log::NOTICE, "Log module shutting down... bye!\n\n"); - logger->shutdown(); - delete logger; - } - - exit(code); -} - -// ------------------------------------------------------------------------------------------------------------------- - -ULLONG ntohll(ULLONG a) -{ - return htonll(a); -} - -ULLONG htonll(ULLONG a) -{ - #if BYTE_ORDER == BIG_ENDIAN - return a; - #else - ULLONG b = 0; - - b = ((a << 56) & 0xFF00000000000000ULL) - | ((a << 40) & 0x00FF000000000000ULL) - | ((a << 24) & 0x0000FF0000000000ULL) - | ((a << 8) & 0x000000FF00000000ULL) - | ((a >> 8) & 0x00000000FF000000ULL) - | ((a >> 24) & 0x0000000000FF0000ULL) - | ((a >> 40) & 0x000000000000FF00ULL) - | ((a >> 56) & 0x00000000000000FFULL) ; - - return b; - #endif -} - -void MILLISLEEP(ULONG a) -{ -#ifndef WIN32 - struct timespec delayTime; - delayTime.tv_sec = a / 1000; - delayTime.tv_nsec = (a % 1000) * 1000000; - nanosleep(&delayTime, NULL); -#else - Sleep(a); -#endif -} - -int min(UINT a, int b) -{ - if (a > b) return b; - else return a; -} - -int max(int a, int b) -{ - if (a > b) return a; - else return b; -} - +/* + 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. +*/ + +#include +#include +#include +#include +#ifndef WIN32 +#include +#include +#endif + +#include "defines.h" +#include "log.h" +#include "timers.h" +#include "vdr.h" +#include "boxstack.h" +#include "command.h" + + +#ifdef VOMP_PLATTFORM_MVP + + +#include "mtdmvp.h" +#include "remotemvp.h" +#include "ledmvp.h" +#include "osdmvp.h" +#include "audiomvp.h" +#include "videomvp.h" + +extern "C" +{ + int ticonfig_main(int, char**); +} + +#endif + +#ifdef VOMP_PLATTFORM_NMT + +#include "mtdnmt.h" +#include "remotelirc.h" +#include "lednmt.h" +#include "osddirectfb.h" +#include "audionmt.h" +#include "videonmt.h" + +#endif + +#ifdef VOMP_PLATTFORM_RASPBERRY + +#include "mtdraspberry.h" +#include "remotelinux.h" +#include "ledraspberry.h" +#include "osdopengl.h" +#include "audiovpe.h" +#include "videovpeogl.h" + +#endif + + + + +#include "wol.h" +#include "vsleeptimer.h" + + +#ifndef WIN32 +void sighandler(int signalReceived); +#endif + +void shutdown(int code); + + + +// Global variables -------------------------------------------------------------------------------------------------- +Log* logger; +Remote* remote; +Mtd* mtd; +Led* led; +Osd* osd; +Timers* timers; +BoxStack* boxstack; +Command* command; +VDR* vdr; +Video* video; +Audio* audio; +Wol* wol; +Sleeptimer* sleeptimer; + +// Linux MVP main function and sighandler +#ifndef WIN32 +int main(int argc, char** argv) +{ +#ifdef VOMP_PLATTFORM_MVP + if (strstr(argv[0], "ticonfig")) return ticonfig_main(argc, argv); +#endif + + bool daemonize = true; + bool debugEnabled = false; + bool crashed = false; + char* setServer = NULL; + int c; + + while ((c = getopt(argc, argv, "cdns:")) != -1) + { + switch (c) + { + case 'c': + crashed = true; + break; + case 'd': + debugEnabled = true; // and... + case 'n': + daemonize = false; + break; + case 's': + setServer = optarg; + break; + case '?': + printf("Unknown option\n"); + return 1; + default: + printf("Option error\n"); + return 1; + } + } + + // Init global vars ------------------------------------------------------------------------------------------------ + logger = new Log(); + timers = new Timers(); + vdr = new VDR(); + + mtd = new Mtd_TYPE(); + remote = new Remote_TYPE(); + led = new Led_TYPE(); + osd = new Osd_TYPE(); + audio = new Audio_TYPE(); + video = new Video_TYPE(); + + + + boxstack = new BoxStack(); + command = new Command(); + wol = new Wol(); + sleeptimer = new Sleeptimer(); + + if (!logger || !remote || !mtd || !led || !osd || !video || !audio || !boxstack || !command || !wol || !sleeptimer) + { + printf("Could not create objects. Memory problems?\n"); + shutdown(1); + } + + // Get logging module started -------------------------------------------------------------------------------------- + + if (!logger->init(Log::DEBUG, "dummy", debugEnabled ? 1 : 0)) + { + printf("Could not initialise log object. Aborting.\n"); + shutdown(1); + } + + logger->log("Core", Log::INFO, "Starting up..."); + + // Daemonize if not -d + + if (daemonize) + { + // Fork away + pid_t forkTest = fork(); + if (forkTest == -1) + { printf("Cannot fork (1).\n"); exit(1); } + if (forkTest != 0) _exit(0); // PID returned, I am the parent + // otherwise, I am the child + setsid(); + forkTest = fork(); + if (forkTest == -1) + { printf("Cannot fork (2).\n"); exit(1); } + if (forkTest != 0) _exit(0); // PID returned, I am the parent + // otherwise, I am the child + close(0); + close(1); + close(2); + } + + // Set up signal handling ------------------------------------------------------------------------------------------ + + sighandler_t sigtest; + + sigtest = signal(SIGPIPE, SIG_IGN); + if (sigtest == SIG_ERR) + { + logger->log("Core", Log::EMERG, "Could not set up signal handler for SIGPIPE. Aborting."); + shutdown(1); + } + sigtest = signal(SIGINT, sighandler); + if (sigtest == SIG_ERR) + { + logger->log("Core", Log::EMERG, "Could not set up signal handler for SIGINT. Aborting."); + shutdown(1); + } + sigtest = signal(SIGTERM, sighandler); + if (sigtest == SIG_ERR) + { + logger->log("Core", Log::EMERG, "Could not set up signal handler for SIGTERM. Aborting."); + shutdown(1); + } + sigtest = signal(SIGUSR1, sighandler); + if (sigtest == SIG_ERR) + { + logger->log("Core", Log::EMERG, "Could not set up signal handler for SIGUSR1. Aborting."); + shutdown(1); + } +/* + sigtest = signal(SIGUSR2, sighandler); + if (sigtest == SIG_ERR) + { + logger->log("Core", Log::EMERG, "Could not set up signal handler for SIGUSR2. Aborting."); + shutdown(1); + } +*/ + sigtest = signal(SIGURG, sighandler); + if (sigtest == SIG_ERR) + { + logger->log("Core", Log::EMERG, "Could not set up signal handler for SIGURG. Aborting."); + shutdown(1); + } + + logger->log("Core", Log::INFO, "Signal handlers set up successfully"); + + + // Init modules ---------------------------------------------------------------------------------------------------- + int success; + + success = remote->init(RemoteStartDev); + + if (success) + { + logger->log("Core", Log::INFO, "Remote module initialised"); + } + else + { + logger->log("Core", Log::EMERG, "Remote module failed to initialise"); + shutdown(1); + } +#ifdef VOMP_PLATTFORM_MVP + success = led->init(((RemoteMVP*)remote)->getDevice()); +#else + success = led->init(-1); +#endif + if (success) + { + logger->log("Core", Log::INFO, "LED module initialised"); + } + else + { + logger->log("Core", Log::EMERG, "LED module failed to initialise"); + shutdown(1); + } + + success = mtd->init(); + if (success) + { + logger->log("Core", Log::INFO, "Mtd module initialised"); + } + else + { + logger->log("Core", Log::EMERG, "Mtd module failed to initialise"); + shutdown(1); + } + + success = timers->init(); + if (success) + { + logger->log("Core", Log::INFO, "Timers module initialised"); + } + else + { + logger->log("Core", Log::EMERG, "Timers module failed to initialise"); + shutdown(1); + } + + UCHAR videoFormat = (UCHAR)mtd->getPALorNTSC(); + if (videoFormat == Video::PAL) logger->log("Core", Log::INFO, "Read from MTD: PAL 720x576"); + else if (videoFormat == Video::NTSC) logger->log("Core", Log::INFO, "Read from MTD: NTSC 720x480"); + else logger->log("Core", Log::INFO, "No help from MTD. Assuming NTSC 720x480"); + + success = video->init(videoFormat); + if (success) + { + logger->log("Core", Log::INFO, "Video module initialised"); + } + else + { + logger->log("Core", Log::EMERG, "Video module failed to initialise"); + shutdown(1); + } + + success = osd->init((void*)OsdStartDev); + if (success) + { + logger->log("Core", Log::INFO, "OSD module initialised"); + } + else + { + logger->log("Core", Log::EMERG, "OSD module failed to initialise"); + shutdown(1); + } + + success = audio->init(Audio::MPEG2_PES); + if (success) + { + logger->log("Core", Log::INFO, "Audio module initialised"); + } + else + { + logger->log("Core", Log::EMERG, "Audio module failed to initialise"); + shutdown(1); + } + + success = vdr->init(3024); + if (success) + { + logger->log("Core", Log::INFO, "VDR module initialised"); + } + else + { + logger->log("Core", Log::EMERG, "VDR module failed to initialise"); + shutdown(1); + } + + success = boxstack->init(); + if (success) + { + logger->log("Core", Log::INFO, "BoxStack module initialised"); + } + else + { + logger->log("Core", Log::EMERG, "BoxStack module failed to initialise"); + shutdown(1); + } + + success = command->init(crashed, setServer); + if (success) + { + logger->log("Core", Log::INFO, "Command module initialised"); + } + else + { + logger->log("Core", Log::EMERG, "Command module failed to initialise"); + shutdown(1); + } + + // Other init ------------------------------------------------------------------------------------------------------ + + logger->log("Core", Log::NOTICE, "Startup successful"); + + // Run main loop --------------------------------------------------------------------------------------------------- + + // Ok, all major device components and other bits are loaded and ready + command->run(); + + // When that returns quit ------------------------------------------------------------------------------------------ + + shutdown(0); + return 0; +} + +// ------------------------------------------------------------------------------------------------------------------- + +void sighandler(int signalReceived) +{ + logger->log("Core", Log::NOTICE, "Signal %i received", signalReceived); + + switch (signalReceived) + { + case SIGINT: + { + logger->log("Core", Log::NOTICE, "Interrupt signal, shutting down..."); + command->stop(); // FIXME this is probably not safe - use the messaging system / is that even safe? + break; + } + case SIGTERM: + { + logger->log("Core", Log::NOTICE, "Term signal, shutting down..."); + command->stop(); // FIXME this is probably not safe - use the messaging system / is that even safe? + break; + } + case SIGUSR1: + { + command->sig1(); + break; + } +/* + case SIGUSR1: + { + logger->log("Core", Log::DEBUG, "SIGUSR1 caught"); + logger->upLogLevel(); + break; + } + case SIGUSR2: + { + logger->log("Core", Log::DEBUG, "SIGUSR2 caught"); + logger->downLogLevel(); + break; + } +*/ + case SIGURG: + { + logger->log("Core", Log::DEBUG, "SIGURG caught"); + break; + } + } +} +#endif + +// ------------------------------------------------------------------------------------------------------------------- + +void shutdown(int code) +{ + if (boxstack) + { + boxstack->shutdown(); + delete boxstack; + logger->log("Core", Log::NOTICE, "BoxStack module shut down"); + } + + // FIXME, send a del all to boxstack first, then get rid of it after command? + if (command) // shut down command here in case views have posted messages + { + command->shutdown(); + delete command; + logger->log("Core", Log::NOTICE, "Command module shut down"); + } + + if (vdr) + { + vdr->shutdown(); + delete vdr; + logger->log("Core", Log::NOTICE, "VDR module shut down"); + } + + if (osd) + { + osd->shutdown(); + delete osd; + logger->log("Core", Log::NOTICE, "OSD module shut down"); + } + + if (audio) + { + audio->shutdown(); + delete audio; + logger->log("Core", Log::NOTICE, "Audio module shut down"); + } + + if (video) + { + video->shutdown(); + delete video; + logger->log("Core", Log::NOTICE, "Video module shut down"); + } + + if (timers) + { + timers->shutdown(); + delete timers; + logger->log("Core", Log::NOTICE, "Timers module shut down"); + } + + if (mtd) + { + mtd->shutdown(); + delete mtd; + logger->log("Core", Log::NOTICE, "MTD module shut down"); + } + + if (led) + { + led->shutdown(); + delete led; + logger->log("Core", Log::NOTICE, "LED module shut down"); + } + + if (remote) + { + remote->shutdown(); + delete remote; + logger->log("Core", Log::NOTICE, "Remote module shut down"); + } + + if (wol) + { + delete wol; + logger->log("Core", Log::NOTICE, "WOL module shut down"); + } + + if (sleeptimer) + { + delete sleeptimer; + logger->log("Core", Log::NOTICE, "Sleeptimer module shut down"); + } + + if (logger) + { + logger->log("Core", Log::NOTICE, "Log module shutting down... bye!\n\n"); + logger->shutdown(); + delete logger; + } + + exit(code); +} + +// ------------------------------------------------------------------------------------------------------------------- + +ULLONG htonll(ULLONG a) +{ + #if BYTE_ORDER == BIG_ENDIAN + return a; + #else + ULLONG b = 0; + + b = ((a << 56) & 0xFF00000000000000ULL) + | ((a << 40) & 0x00FF000000000000ULL) + | ((a << 24) & 0x0000FF0000000000ULL) + | ((a << 8) & 0x000000FF00000000ULL) + | ((a >> 8) & 0x00000000FF000000ULL) + | ((a >> 24) & 0x0000000000FF0000ULL) + | ((a >> 40) & 0x000000000000FF00ULL) + | ((a >> 56) & 0x00000000000000FFULL) ; + + return b; + #endif +} + +ULLONG ntohll(ULLONG a) +{ + return htonll(a); +} + +void MILLISLEEP(ULONG a) +{ +#ifndef WIN32 + struct timespec delayTime; + delayTime.tv_sec = a / 1000; + delayTime.tv_nsec = (a % 1000) * 1000000; + nanosleep(&delayTime, NULL); +#else + Sleep(a); +#endif +} + +int min(UINT a, int b) +{ + if (a > b) return b; + else return a; +} + +int max(int a, int b) +{ + if (a > b) return a; + else return b; +} + diff --git a/message.h b/message.h index 7d5d794..a2c854f 100644 --- a/message.h +++ b/message.h @@ -1,79 +1,82 @@ -/* - 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 MESSAGE_H -#define MESSAGE_H - -#include -#include "defines.h" - -// Usage of messages is more dubious now that the single master mutex lock -// protects all gui actions. Reason(s) for usage: -// 1. View A wants something to be done by View B *after* View A has been deleted -// 2. A thread wants its object/view deleting *after* the thread has exited - -// Put a justification line after call to Message* m = new Message() line -// So that the sources can be grepped for proper message useage - -class Message -{ - public: - Message(); - - void* from; - void* to; - ULONG message; - ULONG parameter; - ULONG tag; // use this for identifying which object / question is being replied to - - const static ULONG QUESTION_YES = 1; - const static ULONG CLOSE_ME = 2; - const static ULONG PLAY_SELECTED_RECORDING = 3; - const static ULONG DELETE_SELECTED_RECORDING = 4; - const static ULONG SCREENSHOT = 5; - const static ULONG CHANNEL_CHANGE = 6; - const static ULONG RESUME_SELECTED_RECORDING = 7; - const static ULONG STOP_PLAYBACK = 9; - const static ULONG SERVER_SELECTED = 10; - const static ULONG VDR_CONNECTED = 11; - const static ULONG ADD_VIEW = 12; - const static ULONG REDRAW_LANG = 14; - const static ULONG EPG = 16; - const static ULONG EPG_CLOSE = 17; - const static ULONG CHANGED_OPTIONS = 18; - const static ULONG CONNECTION_LOST = 19; - const static ULONG MOVE_RECORDING = 20; - const static ULONG UDP_BUTTON = 21; - const static ULONG PLAYER_EVENT = 22; - const static ULONG AUDIO_CHANGE_CHANNEL = 23; - const static ULONG CHILD_CLOSE = 24; - const static ULONG MOUSE_MOVE = 25; - const static ULONG MOUSE_LBDOWN = 26; - const static ULONG CHANGE_LANGUAGE = 27; - const static ULONG LAST_VIEW_CLOSE = 28; - const static ULONG CHANGED_REMOTECONTROL = 29; - const static ULONG DELETE_SELECTED_TIMER = 30; - const static ULONG CHANGED_DEVICEOPTIONS = 31; - const static ULONG TELETEXTUPDATE = 32; - const static ULONG TELETEXTUPDATEFIRSTLINE = 33; - const static ULONG SUBTITLE_CHANGE_CHANNEL = 34; -}; - -#endif +/* + 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 MESSAGE_H +#define MESSAGE_H + +#include + +class Message; +#include "defines.h" + +// Usage of messages is more dubious now that the single master mutex lock +// protects all gui actions. Reason(s) for usage: +// 1. View A wants something to be done by View B *after* View A has been deleted +// 2. A thread wants its object/view deleting *after* the thread has exited + +// Put a justification line after call to Message* m = new Message() line +// So that the sources can be grepped for proper message useage + +class Message +{ + public: + Message(); + + void* from; + void* to; + ULONG message; + ULONG parameter; + ULONG tag; // use this for identifying which object / question is being replied to + + const static ULONG QUESTION_YES = 1; + const static ULONG CLOSE_ME = 2; + const static ULONG PLAY_SELECTED_RECORDING = 3; + const static ULONG DELETE_SELECTED_RECORDING = 4; + const static ULONG SCREENSHOT = 5; + const static ULONG CHANNEL_CHANGE = 6; + const static ULONG RESUME_SELECTED_RECORDING = 7; + const static ULONG STOP_PLAYBACK = 9; + const static ULONG SERVER_SELECTED = 10; + const static ULONG VDR_CONNECTED = 11; + const static ULONG ADD_VIEW = 12; + const static ULONG REDRAW_LANG = 14; + const static ULONG EPG = 16; + const static ULONG EPG_CLOSE = 17; + const static ULONG CHANGED_OPTIONS = 18; + const static ULONG CONNECTION_LOST = 19; + const static ULONG MOVE_RECORDING = 20; + const static ULONG UDP_BUTTON = 21; + const static ULONG PLAYER_EVENT = 22; + const static ULONG AUDIO_CHANGE_CHANNEL = 23; + const static ULONG CHILD_CLOSE = 24; + const static ULONG MOUSE_MOVE = 25; + const static ULONG MOUSE_LBDOWN = 26; + const static ULONG CHANGE_LANGUAGE = 27; + const static ULONG LAST_VIEW_CLOSE = 28; + const static ULONG CHANGED_REMOTECONTROL = 29; + const static ULONG DELETE_SELECTED_TIMER = 30; + const static ULONG CHANGED_DEVICEOPTIONS = 31; + const static ULONG TELETEXTUPDATE = 32; + const static ULONG TELETEXTUPDATEFIRSTLINE = 33; + const static ULONG SUBTITLE_CHANGE_CHANNEL = 34; + const static ULONG MOUSE_ANDROID_SCROLL = 35; +}; + +#endif diff --git a/objects.mk b/objects.mk index f724306..ddbd015 100644 --- a/objects.mk +++ b/objects.mk @@ -1,28 +1,28 @@ -OBJECTS1 = command.o log.o tcp.o dsock.o thread.o timers.o i18n.o mutex.o \ - message.o messagequeue.o udp.o wol.o \ - vdr.o recman.o recording.o recinfo.o channel.o rectimer.o event.o \ - directory.o mark.o option.o \ - player.o playerradio.o vfeed.o afeed.o \ - demuxer.o demuxervdr.o demuxerts.o stream.o draintarget.o \ - region.o colour.o boxstack.o boxx.o tbboxx.o \ - vinfo.o vquestion.o vrecordinglist.o vrecording.o \ - vmute.o vvolume.o vtimerlist.o vtimeredit.o vrecordingmenu.o \ - vchannellist.o vwelcome.o vvideorec.o vepgsettimer.o \ - vchannelselect.o vserverselect.o vconnect.o vepg.o vrecmove.o \ - vradiorec.o vaudioselector.o vscreensaver.o vopts.o \ - wselectlist.o wjpeg.o wsymbol.o wbutton.o wtextbox.o wwss.o \ - woptionpane.o woptionbox.o wremoteconfig.o wtabbar.o \ - fonts/helvB24.o fonts/helvB18.o \ - remote.o led.o mtd.o video.o audio.o osd.o surface.o \ - vmedialist.o media.o vpicturebanner.o \ - audioplayer.o demuxeraudio.o abstractoption.o \ - eventdispatcher.o vdrrequestpacket.o vdrresponsepacket.o \ - vvideolivetv.o vsleeptimer.o \ - playerlivetv.o playerliveradio.o \ - wprogressbar.o \ - bitmap.o dvbsubtitles.o \ - imagereader.o vcolourtuner.o mediaoptions.o mediaplayer.o mediafile.o \ - serialize.o localmediafile.o vmediaview.o vvideomedia.o playermedia.o \ - demuxermedia.o tfeed.o vteletextview.o teletextdecodervbiebu.o \ - teletxt/txtfont.o - +OBJECTS1 = command.o tcp.o dsock.o thread.o timers.o i18n.o \ + message.o messagequeue.o udp.o wol.o audio.o video.o log.o mutex.o \ + vdr.o recman.o recording.o recinfo.o channel.o rectimer.o event.o \ + directory.o mark.o option.o \ + player.o playerradio.o vfeed.o afeed.o \ + demuxer.o demuxervdr.o demuxerts.o stream.o \ + region.o colour.o boxstack.o boxx.o tbboxx.o \ + vinfo.o vquestion.o vrecordinglist.o vrecording.o \ + vmute.o vvolume.o vtimerlist.o vtimeredit.o vrecordingmenu.o \ + vchannellist.o vwelcome.o vvideorec.o vepgsettimer.o \ + vchannelselect.o vserverselect.o vconnect.o vepg.o vrecmove.o \ + vradiorec.o vaudioselector.o vscreensaver.o vopts.o \ + wselectlist.o wjpeg.o wsymbol.o wbutton.o wtextbox.o wwss.o \ + woptionpane.o woptionbox.o wremoteconfig.o wtabbar.o \ + fonts/helvB24.o fonts/helvB18.o \ + remote.o led.o mtd.o osd.o surface.o \ + media.o vpicturebanner.o \ + audioplayer.o demuxeraudio.o abstractoption.o \ + eventdispatcher.o vdrrequestpacket.o vdrresponsepacket.o \ + vvideolivetv.o vsleeptimer.o \ + playerlivetv.o playerliveradio.o \ + wprogressbar.o \ + bitmap.o dvbsubtitles.o \ + imagereader.o mediaoptions.o mediaplayer.o mediafile.o \ + serialize.o localmediafile.o playermedia.o \ + demuxermedia.o tfeed.o vteletextview.o teletextdecodervbiebu.o \ + teletxt/txtfont.o + diff --git a/osdmvp.cc b/osdmvp.cc index 0e7ca79..1f4b22c 100644 --- a/osdmvp.cc +++ b/osdmvp.cc @@ -36,6 +36,11 @@ int OsdMVP::getFD() return fdOsd; } +Surface * OsdMvp::createNewSurface();{ + return (Surface*)new SurfaceMvp(); +} + + int OsdMVP::init(void* device) { if (initted) return 0; @@ -69,7 +74,7 @@ int OsdMVP::shutdown() return 1; } -void OsdMVP::screenShot(char* fileName) +void OsdMVP::screenShot(const char* fileName) { screen->screenShot(fileName); } diff --git a/osdmvp.h b/osdmvp.h index ac6ba58..5cdad84 100644 --- a/osdmvp.h +++ b/osdmvp.h @@ -40,9 +40,11 @@ class OsdMVP : public Osd int init(void* device); int shutdown(); + Surface * createNewSurface(); + int getFD(); - void screenShot(char* fileName); + void screenShot(const char* fileName); private: }; diff --git a/osdwin.cc b/osdwin.cc index 1583460..4f90c5b 100644 --- a/osdwin.cc +++ b/osdwin.cc @@ -1,599 +1,603 @@ -/* - 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. -*/ - - -#include "osdwin.h" -#include "mtd.h" -#include "videowin.h" -#include "surfacewin.h" - -#include "dsallocator.h" -#include "message.h" -#include "command.h" - -#define BACKBUFFER_WIDTH 1280 -#define BACKBUFFER_HEIGHT 720 - - - -typedef HRESULT (__stdcall *FCT_DXVA2CreateDirect3DDeviceManager9)(UINT* pResetToken, IDirect3DDeviceManager9** ppDeviceManager); -typedef HRESULT (__stdcall *FCT_MFCreateVideoSampleFromSurface)(IUnknown* pUnkSurface, IMFSample** ppSample); - -FCT_DXVA2CreateDirect3DDeviceManager9 ptrDXVA2CreateDirect3DDeviceManager9=NULL; -FCT_MFCreateVideoSampleFromSurface ptrMFCreateVideoSampleFromSurface=NULL; - -//This is stuff for rendering the OSD - - -OsdWin::OsdWin() -{ - d3d=NULL; - d3ddevice=NULL; - d3drtsurf=NULL; - swappy=NULL; - swapsurf=NULL; - evrstate=EVR_pres_off; - window=NULL; - - external_driving=false; - dsallocator=NULL; - filter_type=D3DTEXF_FORCE_DWORD; - lastrendertime=timeGetTime(); - event = CreateEvent(NULL,/*FALSE*/TRUE,FALSE,NULL); - d3dmutex = CreateMutex(NULL,FALSE,NULL); - /*EVR stuff*/ - dxvadevicehandle=NULL; - evrsupported=true; - HMODULE hlib=NULL; - hlib=LoadLibrary("dxva2.dll"); - if (!hlib) { - evrsupported=false; - return; - } - ptrDXVA2CreateDirect3DDeviceManager9=(FCT_DXVA2CreateDirect3DDeviceManager9)GetProcAddress(hlib, "DXVA2CreateDirect3DDeviceManager9"); - if (!ptrDXVA2CreateDirect3DDeviceManager9){ - evrsupported=false; - return; - } - - hlib=LoadLibrary("evr.dll"); - if (!hlib) { - evrsupported=false; - return; - } - - ptrMFCreateVideoSampleFromSurface = (FCT_MFCreateVideoSampleFromSurface)GetProcAddress(hlib,"MFCreateVideoSampleFromSurface"); - if (!ptrMFCreateVideoSampleFromSurface){ - evrsupported=false; - return; - } - -} - -OsdWin::~OsdWin() -{ - - if (initted) - { - threadStop(); - shutdown(); - } - CloseHandle(event); - CloseHandle(d3dmutex); -} - -int OsdWin::getFD() -{ - if (!initted) return 0; - return fdOsd; -} - -int OsdWin::init(void* device) -{ - if (initted) return 0; - Video* video = Video::getInstance(); - window=*((HWND*)device); - //First Create Direct 3D Object - d3d=Direct3DCreate9(D3D_SDK_VERSION); - if (!d3d) - { - Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 object!"); - return 0; - } - // then create the Device - D3DPRESENT_PARAMETERS d3dparas; - ZeroMemory(&d3dparas,sizeof(d3dparas)); - d3dparas.BackBufferWidth=BACKBUFFER_WIDTH; - d3dparas.BackBufferHeight=BACKBUFFER_HEIGHT; - d3dparas.Windowed=TRUE; - d3dparas.SwapEffect=D3DSWAPEFFECT_COPY; - if (d3d->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,*((HWND*) device), - D3DCREATE_SOFTWARE_VERTEXPROCESSING |D3DCREATE_MULTITHREADED,&d3dparas,&d3ddevice)!=D3D_OK) { - Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 device!"); - return 0; - } - d3ddevice->GetRenderTarget(0,&d3drtsurf); - - /* - if (!InitVertexBuffer()) { - Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 vertex buf!"); - return 0; - }*/ - /* We have to determine which kind of filtering is supported*/ - D3DCAPS9 caps; - d3ddevice->GetDeviceCaps(&caps); - if ( ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFLINEAR)!=0) - && ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFLINEAR)!=0)) { - if (filter_type==D3DTEXF_FORCE_DWORD) { - filter_type=D3DTEXF_LINEAR; - } - } else { - if (filter_type==D3DTEXF_LINEAR) - { - filter_type=D3DTEXF_POINT; - } - } - - if ( ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFPOINT)!=0) - && ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFPOINT)!=0)) { - if (filter_type==D3DTEXF_FORCE_DWORD) - { - filter_type=D3DTEXF_POINT; - } - } else { - if (filter_type==D3DTEXF_POINT) { - filter_type=D3DTEXF_NONE; - } - } - if (filter_type==D3DTEXF_FORCE_DWORD) { - filter_type=D3DTEXF_NONE; - } - - if (evrsupported) - { - if (ptrDXVA2CreateDirect3DDeviceManager9(&dxvatoken,&d3ddevman)!=S_OK) evrsupported=false; - else { - d3ddevman->ResetDevice(d3ddevice,dxvatoken); - } - } - - - - - //Now we will create the Screen - screen = new SurfaceWin(Surface::SCREEN); - SetEvent(event);//Now all devices are ready - screen->create(video->getScreenWidth(), video->getScreenHeight()); - screen->display(); - initted = 1; // must set this here or create surface won't work - threadStart(); - - return 1; -} - -void OsdWin::LockDevice() -{ - if (!evrsupported) return; - if (!dxvadevicehandle) - { - d3ddevman->OpenDeviceHandle(&dxvadevicehandle); - } - IDirect3DDevice9 *temp; - d3ddevman->LockDevice(dxvadevicehandle,&temp,TRUE); - -} - -void OsdWin::UnlockDevice() -{ - if (!evrsupported) return; - if (!initted) return; - d3ddevman->UnlockDevice(dxvadevicehandle,TRUE); - if (dxvadevicehandle) - { - d3ddevman->CloseDeviceHandle(dxvadevicehandle); - dxvadevicehandle=NULL; - } - -} - -DWORD OsdWin::getFilterCaps() -{ - if (!initted) return NULL; - D3DCAPS9 caps; - d3ddevice->GetDeviceCaps(&caps); - return caps.StretchRectFilterCaps; -} - -LPDIRECT3DVERTEXBUFFER9 OsdWin::InitVertexBuffer(DWORD width, DWORD height) -{ - LPDIRECT3DVERTEXBUFFER9 ret =NULL; - Video* video=Video::getInstance(); - FLOAT texx=((float)video->getScreenWidth())/1024.f; - FLOAT texy=((float)video->getScreenHeight())/1024.f; - D3DCOLOR osdcolor=D3DCOLOR_RGBA(255,255,255,255); - osdvertices[0].c=osdcolor; - osdvertices[0].x=0.f-0.5f; - osdvertices[0].y=0.f-0.5f; - osdvertices[0].z=0.5f; - osdvertices[0].rhw=1.f; - osdvertices[0].u=0.f; - osdvertices[0].v=0.f; - osdvertices[1].c=osdcolor; - osdvertices[1].x=((float)width)-0.5f;-0.5f; - osdvertices[1].y=0.f-0.5f; - osdvertices[1].z=0.5f; - osdvertices[1].u=texx; - osdvertices[1].v=0.f; - osdvertices[1].rhw=1.f; - osdvertices[2].c=osdcolor; - osdvertices[2].x=((float)width)-0.5f; - osdvertices[2].y=((float)height)-0.5f; - osdvertices[2].z=0.5f; - osdvertices[2].rhw=1.f; - osdvertices[2].u=texx; - osdvertices[2].v=texy; - osdvertices[3].c=osdcolor; - osdvertices[3].x=0.f-0.5f; - osdvertices[3].y=((float)height)-0.5f; - osdvertices[3].z=0.5f; - osdvertices[3].rhw=1.f; - osdvertices[3].u=0.f; - osdvertices[3].v=texy; - - if (d3ddevice->CreateVertexBuffer(4*sizeof(OSDVERTEX),0,D3DFVF_OSDVERTEX,D3DPOOL_MANAGED, - &ret,NULL)!=D3D_OK) { - return NULL; - } - void *pvertex=NULL; - if (ret->Lock(0,sizeof(osdvertices),&pvertex,0)!=D3D_OK) { - return NULL; - } - memcpy(pvertex,osdvertices,sizeof(osdvertices)); - ret->Unlock(); - return ret; -} - -int OsdWin::shutdown() -{ - if (!initted) return 0; - initted = 0; - evrsupported=0; - if (d3ddevman) d3ddevman->Release(); - d3drtsurf->Release(); - d3ddevice->Release(); - d3d->Release(); - if (swapsurf) swapsurf->Release(); - if (swappy) swappy->Release(); - - return 1; -} - -void OsdWin::screenShot(char* fileName) -{ - screen->screenShot(fileName); -} - -void OsdWin::threadMethod() -{ - while (true) - { - DWORD waittime=10; - if (initted){ - if (evrstate==EVR_pres_off || evrstate==EVR_pres_pause) - { - Render(); - } else if (evrstate==EVR_pres_started) - { - LPDIRECT3DSURFACE9 surf; - if (dsallocator) dsallocator->GetNextSurface(&surf,&waittime); - if (surf==NULL) - { - Render(); - } - else - { - RenderDS(surf); - surf->Release(); - if (dsallocator) dsallocator->DiscardSurfaceandgetWait(&waittime); - } - } - } - threadCheckExit(); - if (waittime!=0) Sleep(min(10,waittime)); - //Sleep(1); - } -} - - -void OsdWin::threadPostStopCleanup() -{ - //Doing nothing - //goo; -} - - -// This function is called from the WinMain function in order to get Screen updates -void OsdWin::Render() -{ - if (!initted) return ; - if (external_driving) { - DWORD time1=timeGetTime(); //Updates the Menue - if ((time1-lastrendertime)>200) {//5 fps for OSD updates are enough, avoids tearing - InternalRendering(NULL); - lastrendertime=timeGetTime(); - } else { - //Sleep(5); //Sleep for 5 ms, in order to avoid blocking the other threads - } - } else { - DWORD time1=timeGetTime(); - if ((time1-lastrendertime)>50) {//10 fps for OSD updates are enough, avoids tearing - InternalRendering(NULL); - lastrendertime=timeGetTime(); - } else { - //Sleep(5); - - } - - } -} - -void OsdWin::RenderDS(LPDIRECT3DSURFACE9 present){ - if (!initted) return; - if (external_driving) { - InternalRendering(present); - lastrendertime=timeGetTime(); - } -} - - -void OsdWin::InternalRendering(LPDIRECT3DSURFACE9 present){ - BeginPainting(); - HRESULT losty=d3ddevice->TestCooperativeLevel(); - if (losty==D3DERR_DEVICELOST) { - //Sleep(10); - EndPainting(); - return; //Device Lost - } - if (losty==D3DERR_DEVICENOTRESET){ - EndPainting(); - DoLost(); - return; - } - WaitForSingleObject(event,INFINITE); - - - - - LPDIRECT3DSURFACE9 targetsurf; - if (swappy) - { - targetsurf=swapsurf; - d3ddevice->SetRenderTarget(0,swapsurf);//Stupid VMR manipulates the render target - } - else - { - targetsurf=d3drtsurf; - d3ddevice->SetRenderTarget(0,d3drtsurf);//Stupid VMR manipulates the render target - } - D3DSURFACE_DESC targetdesc; - targetsurf->GetDesc(&targetdesc); - - if (external_driving) { - //Copy video to Backbuffer - if (present!=NULL ) { - VideoWin* video =(VideoWin*) Video::getInstance(); - /*calculating destination rect */ - RECT destrect={0,0,/*video->getScreenWidth()*/ targetdesc.Width, - /*video->getScreenHeight()*/targetdesc.Height}; - UCHAR mode=video->getMode(); - switch (mode) { - case Video::EIGHTH: - destrect.right=destrect.right/2; - destrect.bottom=destrect.bottom/2; - case Video::QUARTER: - destrect.right=destrect.right/2+video->getPosx()*2; - destrect.bottom=destrect.bottom/2+video->getPosy()*2; - destrect.left=video->getPosx()*2; - destrect.top=video->getPosy()*2; - d3ddevice->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0f,0); - break; - }; - D3DSURFACE_DESC surf_desc; - present->GetDesc(&surf_desc);//for chop sides - RECT sourcerect= {0,0,surf_desc.Width,surf_desc.Height}; - if (video->getPseudoTVsize()==Video::ASPECT4X3 - && video->getMode()==Video::NORMAL - && video->getAspectRatio()==Video::ASPECT16X9) { - unsigned int correction=((double) (surf_desc.Width))*4.*9./3./16.; - sourcerect.left=(surf_desc.Width-correction)/2; - sourcerect.right=sourcerect.left+correction; - } - d3ddevice->StretchRect(present,&sourcerect,targetsurf ,&destrect,filter_type); - - } - } else { - VideoWin* video =(VideoWin*) Video::getInstance(); - //Clear Background - if (!video->isVideoOn()) d3ddevice->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0f,0); - } - LPDIRECT3DVERTEXBUFFER9 vb=NULL; - vb=InitVertexBuffer(targetdesc.Width,targetdesc.Height); - - //Drawing the OSD - if (d3ddevice->BeginScene()==D3D_OK) { - d3ddevice->SetStreamSource(0,vb,0,sizeof(OSDVERTEX)); - d3ddevice->SetFVF(D3DFVF_OSDVERTEX); - d3ddevice->SetTexture(0,((SurfaceWin*)screen)->getD3dtexture()); - //d3ddevice->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_MODULATE); - d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP,D3DTOP_MODULATE); - - d3ddevice->SetSamplerState(0,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR); - d3ddevice->SetSamplerState(0,D3DSAMP_MINFILTER,D3DTEXF_LINEAR); - - - d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE); - d3ddevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA); - d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); - d3ddevice->SetRenderState(D3DRS_LIGHTING,FALSE); - - - d3ddevice->DrawPrimitive(D3DPT_TRIANGLEFAN,0,2); - d3ddevice->EndScene(); - //Show it to the user! - HRESULT hres; - if (swappy) - { - if (hres=swappy->Present(NULL,NULL,NULL,NULL,0)==D3DERR_DEVICELOST){ - //EndPainting(); - if (!external_driving) DoLost(); - } - } - else - { - if (hres=d3ddevice->Present(NULL,NULL,NULL,NULL)==D3DERR_DEVICELOST){ - //EndPainting(); - if (!external_driving) DoLost(); - } - } - - } - - vb->Release(); - EndPainting(); - - -// if (!external_driving) { -// Sleep(4);//The User can wait for 4 milliseconds to see his changes -// } -} - -bool OsdWin::DoLost(){ - Log::getInstance()->log("OSD", Log::WARN, "Direct3D Device Lost! Reobtaining..."); - ResetEvent(event); - if (external_driving && dsallocator!=NULL) { - dsallocator->LostDevice(d3ddevice,d3d); //Propagate the information through DS - } - //First we free up all resources - Video* video = Video::getInstance(); - ((SurfaceWin*)screen)->ReleaseSurface(); - if (d3drtsurf) d3drtsurf->Release(); - d3drtsurf=NULL; - D3DPRESENT_PARAMETERS d3dparas; - ZeroMemory(&d3dparas,sizeof(d3dparas)); - d3dparas.BackBufferWidth=BACKBUFFER_WIDTH; - d3dparas.BackBufferHeight=BACKBUFFER_HEIGHT; - d3dparas.Windowed=TRUE; - d3dparas.SwapEffect=D3DSWAPEFFECT_COPY; - - if (swapsurf) {swapsurf->Release();swapsurf=NULL;}; - if (swappy) {swappy->Release();swappy=NULL;}; - - if (d3ddevice->Reset(&d3dparas)!=D3D_OK){ - return false; - } - d3ddevice->GetRenderTarget(0,&d3drtsurf); - if (d3ddevman) d3ddevman->ResetDevice(d3ddevice,dxvatoken); - //InitVertexBuffer(); - //Redraw Views, Chris could you add a member function to BoxStack, so that - // I can cause it to completely redraw the Views? - // Otherwise the OSD would be distorted after Device Lost - // FIXME - - SetEvent(event); - - - screen->create(video->getScreenWidth(), video->getScreenHeight()); - screen->display(); - - return true; - -} -LPDIRECT3DDEVICE9 OsdWin::getD3dDev() { - WaitForSingleObject(event,INFINITE);//We will only return if we are initted - return d3ddevice; -} - -LPDIRECT3D9 OsdWin::getD3d() { - WaitForSingleObject(event,INFINITE);//We will only return if we are initted - return d3d; -} - -void OsdWin::BeginPainting() {//We synchronize calls to d3d between different threads - WaitForSingleObject(d3dmutex,INFINITE); - LockDevice(); -} - -void OsdWin::EndPainting() { - UnlockDevice(); - ReleaseMutex(d3dmutex); -} - -void OsdWin::setExternalDriving(DsAllocator* dsall,DWORD width, DWORD height) { - - if (swappy) - { - BeginPainting(); - d3ddevice->StretchRect(swapsurf,NULL,d3drtsurf,NULL,filter_type); - LPDIRECT3DSWAPCHAIN9 temp=swappy; - LPDIRECT3DSURFACE9 tempsurf=swapsurf; - swappy=NULL; - swapsurf=NULL; - EndPainting(); - tempsurf->Release(); - temp->Release(); - } - - if (dsall==NULL) { - external_driving=false; - dsallocator=NULL; - return; - } - WaitForSingleObject(event,INFINITE);//We will only return if we are initted - BeginPainting(); - - if (width>BACKBUFFER_WIDTH || height>BACKBUFFER_HEIGHT) - { - D3DPRESENT_PARAMETERS d3dparas; - ZeroMemory(&d3dparas,sizeof(d3dparas)); - d3dparas.BackBufferWidth=width; - d3dparas.BackBufferHeight=height; - d3dparas.Windowed=TRUE; - d3dparas.SwapEffect=D3DSWAPEFFECT_COPY; - if (d3ddevice->CreateAdditionalSwapChain(&d3dparas,&swappy)!=D3D_OK){ - Log::getInstance()->log("OSD", Log::WARN, "Could not create Swap Chain!"); - //return 0; - } else { - swappy->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&swapsurf); - } - Log::getInstance()->log("OSD", Log::INFO, "Create Additional Swap Chain %d %d!",width,height); - } - - dsallocator=dsall; - external_driving=true; - - EndPainting(); -} - -void OsdWin::Blank() { - WaitForSingleObject(event,INFINITE); - BeginPainting(); - d3ddevice->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0f,0); - EndPainting(); -} +/* + 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. +*/ + + +#include "osdwin.h" +#include "mtd.h" +#include "videowin.h" +#include "surfacewin.h" + +#include "dsallocator.h" +#include "message.h" +#include "command.h" + +#define BACKBUFFER_WIDTH 1280 +#define BACKBUFFER_HEIGHT 720 + + + +typedef HRESULT (__stdcall *FCT_DXVA2CreateDirect3DDeviceManager9)(UINT* pResetToken, IDirect3DDeviceManager9** ppDeviceManager); +typedef HRESULT (__stdcall *FCT_MFCreateVideoSampleFromSurface)(IUnknown* pUnkSurface, IMFSample** ppSample); + +FCT_DXVA2CreateDirect3DDeviceManager9 ptrDXVA2CreateDirect3DDeviceManager9=NULL; +FCT_MFCreateVideoSampleFromSurface ptrMFCreateVideoSampleFromSurface=NULL; + +//This is stuff for rendering the OSD + + +OsdWin::OsdWin() +{ + d3d=NULL; + d3ddevice=NULL; + d3drtsurf=NULL; + swappy=NULL; + swapsurf=NULL; + evrstate=EVR_pres_off; + window=NULL; + + external_driving=false; + dsallocator=NULL; + filter_type=D3DTEXF_FORCE_DWORD; + lastrendertime=timeGetTime(); + event = CreateEvent(NULL,/*FALSE*/TRUE,FALSE,NULL); + d3dmutex = CreateMutex(NULL,FALSE,NULL); + /*EVR stuff*/ + dxvadevicehandle=NULL; + evrsupported=true; + HMODULE hlib=NULL; + hlib=LoadLibrary("dxva2.dll"); + if (!hlib) { + evrsupported=false; + return; + } + ptrDXVA2CreateDirect3DDeviceManager9=(FCT_DXVA2CreateDirect3DDeviceManager9)GetProcAddress(hlib, "DXVA2CreateDirect3DDeviceManager9"); + if (!ptrDXVA2CreateDirect3DDeviceManager9){ + evrsupported=false; + return; + } + + hlib=LoadLibrary("evr.dll"); + if (!hlib) { + evrsupported=false; + return; + } + + ptrMFCreateVideoSampleFromSurface = (FCT_MFCreateVideoSampleFromSurface)GetProcAddress(hlib,"MFCreateVideoSampleFromSurface"); + if (!ptrMFCreateVideoSampleFromSurface){ + evrsupported=false; + return; + } + +} + +OsdWin::~OsdWin() +{ + + if (initted) + { + threadStop(); + shutdown(); + } + CloseHandle(event); + CloseHandle(d3dmutex); +} + +int OsdWin::getFD() +{ + if (!initted) return 0; + return fdOsd; +} + +Surface * OsdWin::createNewSurface();{ + return (Surface*)new SurfaceWin(); +} + +int OsdWin::init(void* device) +{ + if (initted) return 0; + Video* video = Video::getInstance(); + window=*((HWND*)device); + //First Create Direct 3D Object + d3d=Direct3DCreate9(D3D_SDK_VERSION); + if (!d3d) + { + Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 object!"); + return 0; + } + // then create the Device + D3DPRESENT_PARAMETERS d3dparas; + ZeroMemory(&d3dparas,sizeof(d3dparas)); + d3dparas.BackBufferWidth=BACKBUFFER_WIDTH; + d3dparas.BackBufferHeight=BACKBUFFER_HEIGHT; + d3dparas.Windowed=TRUE; + d3dparas.SwapEffect=D3DSWAPEFFECT_COPY; + if (d3d->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,*((HWND*) device), + D3DCREATE_SOFTWARE_VERTEXPROCESSING |D3DCREATE_MULTITHREADED,&d3dparas,&d3ddevice)!=D3D_OK) { + Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 device!"); + return 0; + } + d3ddevice->GetRenderTarget(0,&d3drtsurf); + + /* + if (!InitVertexBuffer()) { + Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 vertex buf!"); + return 0; + }*/ + /* We have to determine which kind of filtering is supported*/ + D3DCAPS9 caps; + d3ddevice->GetDeviceCaps(&caps); + if ( ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFLINEAR)!=0) + && ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFLINEAR)!=0)) { + if (filter_type==D3DTEXF_FORCE_DWORD) { + filter_type=D3DTEXF_LINEAR; + } + } else { + if (filter_type==D3DTEXF_LINEAR) + { + filter_type=D3DTEXF_POINT; + } + } + + if ( ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFPOINT)!=0) + && ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFPOINT)!=0)) { + if (filter_type==D3DTEXF_FORCE_DWORD) + { + filter_type=D3DTEXF_POINT; + } + } else { + if (filter_type==D3DTEXF_POINT) { + filter_type=D3DTEXF_NONE; + } + } + if (filter_type==D3DTEXF_FORCE_DWORD) { + filter_type=D3DTEXF_NONE; + } + + if (evrsupported) + { + if (ptrDXVA2CreateDirect3DDeviceManager9(&dxvatoken,&d3ddevman)!=S_OK) evrsupported=false; + else { + d3ddevman->ResetDevice(d3ddevice,dxvatoken); + } + } + + + + + //Now we will create the Screen + screen = new SurfaceWin(Surface::SCREEN); + SetEvent(event);//Now all devices are ready + screen->create(video->getScreenWidth(), video->getScreenHeight()); + screen->display(); + initted = 1; // must set this here or create surface won't work + threadStart(); + + return 1; +} + +void OsdWin::LockDevice() +{ + if (!evrsupported) return; + if (!dxvadevicehandle) + { + d3ddevman->OpenDeviceHandle(&dxvadevicehandle); + } + IDirect3DDevice9 *temp; + d3ddevman->LockDevice(dxvadevicehandle,&temp,TRUE); + +} + +void OsdWin::UnlockDevice() +{ + if (!evrsupported) return; + if (!initted) return; + d3ddevman->UnlockDevice(dxvadevicehandle,TRUE); + if (dxvadevicehandle) + { + d3ddevman->CloseDeviceHandle(dxvadevicehandle); + dxvadevicehandle=NULL; + } + +} + +DWORD OsdWin::getFilterCaps() +{ + if (!initted) return NULL; + D3DCAPS9 caps; + d3ddevice->GetDeviceCaps(&caps); + return caps.StretchRectFilterCaps; +} + +LPDIRECT3DVERTEXBUFFER9 OsdWin::InitVertexBuffer(DWORD width, DWORD height) +{ + LPDIRECT3DVERTEXBUFFER9 ret =NULL; + Video* video=Video::getInstance(); + FLOAT texx=((float)video->getScreenWidth())/1024.f; + FLOAT texy=((float)video->getScreenHeight())/1024.f; + D3DCOLOR osdcolor=D3DCOLOR_RGBA(255,255,255,255); + osdvertices[0].c=osdcolor; + osdvertices[0].x=0.f-0.5f; + osdvertices[0].y=0.f-0.5f; + osdvertices[0].z=0.5f; + osdvertices[0].rhw=1.f; + osdvertices[0].u=0.f; + osdvertices[0].v=0.f; + osdvertices[1].c=osdcolor; + osdvertices[1].x=((float)width)-0.5f; + osdvertices[1].y=0.f-0.5f; + osdvertices[1].z=0.5f; + osdvertices[1].u=texx; + osdvertices[1].v=0.f; + osdvertices[1].rhw=1.f; + osdvertices[2].c=osdcolor; + osdvertices[2].x=((float)width)-0.5f; + osdvertices[2].y=((float)height)-0.5f; + osdvertices[2].z=0.5f; + osdvertices[2].rhw=1.f; + osdvertices[2].u=texx; + osdvertices[2].v=texy; + osdvertices[3].c=osdcolor; + osdvertices[3].x=0.f-0.5f; + osdvertices[3].y=((float)height)-0.5f; + osdvertices[3].z=0.5f; + osdvertices[3].rhw=1.f; + osdvertices[3].u=0.f; + osdvertices[3].v=texy; + + if (d3ddevice->CreateVertexBuffer(4*sizeof(OSDVERTEX),0,D3DFVF_OSDVERTEX,D3DPOOL_MANAGED, + &ret,NULL)!=D3D_OK) { + return NULL; + } + void *pvertex=NULL; + if (ret->Lock(0,sizeof(osdvertices),&pvertex,0)!=D3D_OK) { + return NULL; + } + memcpy(pvertex,osdvertices,sizeof(osdvertices)); + ret->Unlock(); + return ret; +} + +int OsdWin::shutdown() +{ + if (!initted) return 0; + initted = 0; + evrsupported=0; + if (d3ddevman) d3ddevman->Release(); + d3drtsurf->Release(); + d3ddevice->Release(); + d3d->Release(); + if (swapsurf) swapsurf->Release(); + if (swappy) swappy->Release(); + + return 1; +} + +void OsdWin::screenShot(const char* fileName) +{ + screen->screenShot(fileName); +} + +void OsdWin::threadMethod() +{ + while (true) + { + DWORD waittime=10; + if (initted){ + if (evrstate==EVR_pres_off || evrstate==EVR_pres_pause) + { + Render(); + } else if (evrstate==EVR_pres_started) + { + LPDIRECT3DSURFACE9 surf; + if (dsallocator) dsallocator->GetNextSurface(&surf,&waittime); + if (surf==NULL) + { + Render(); + } + else + { + RenderDS(surf); + surf->Release(); + if (dsallocator) dsallocator->DiscardSurfaceandgetWait(&waittime); + } + } + } + threadCheckExit(); + if (waittime!=0) Sleep(min(10,waittime)); + //Sleep(1); + } +} + + +void OsdWin::threadPostStopCleanup() +{ + //Doing nothing + //goo; +} + + +// This function is called from the WinMain function in order to get Screen updates +void OsdWin::Render() +{ + if (!initted) return ; + if (external_driving) { + DWORD time1=timeGetTime(); //Updates the Menue + if ((time1-lastrendertime)>200) {//5 fps for OSD updates are enough, avoids tearing + InternalRendering(NULL); + lastrendertime=timeGetTime(); + } else { + //Sleep(5); //Sleep for 5 ms, in order to avoid blocking the other threads + } + } else { + DWORD time1=timeGetTime(); + if ((time1-lastrendertime)>50) {//10 fps for OSD updates are enough, avoids tearing + InternalRendering(NULL); + lastrendertime=timeGetTime(); + } else { + //Sleep(5); + + } + + } +} + +void OsdWin::RenderDS(LPDIRECT3DSURFACE9 present){ + if (!initted) return; + if (external_driving) { + InternalRendering(present); + lastrendertime=timeGetTime(); + } +} + + +void OsdWin::InternalRendering(LPDIRECT3DSURFACE9 present){ + BeginPainting(); + HRESULT losty=d3ddevice->TestCooperativeLevel(); + if (losty==D3DERR_DEVICELOST) { + //Sleep(10); + EndPainting(); + return; //Device Lost + } + if (losty==D3DERR_DEVICENOTRESET){ + EndPainting(); + DoLost(); + return; + } + WaitForSingleObject(event,INFINITE); + + + + + LPDIRECT3DSURFACE9 targetsurf; + if (swappy) + { + targetsurf=swapsurf; + d3ddevice->SetRenderTarget(0,swapsurf);//Stupid VMR manipulates the render target + } + else + { + targetsurf=d3drtsurf; + d3ddevice->SetRenderTarget(0,d3drtsurf);//Stupid VMR manipulates the render target + } + D3DSURFACE_DESC targetdesc; + targetsurf->GetDesc(&targetdesc); + + if (external_driving) { + //Copy video to Backbuffer + if (present!=NULL ) { + VideoWin* video =(VideoWin*) Video::getInstance(); + /*calculating destination rect */ + RECT destrect={0,0,/*video->getScreenWidth()*/ targetdesc.Width, + /*video->getScreenHeight()*/targetdesc.Height}; + UCHAR mode=video->getMode(); + switch (mode) { + case Video::EIGHTH: + destrect.right=destrect.right/2; + destrect.bottom=destrect.bottom/2; + case Video::QUARTER: + destrect.right=destrect.right/2+video->getPosx()*2; + destrect.bottom=destrect.bottom/2+video->getPosy()*2; + destrect.left=video->getPosx()*2; + destrect.top=video->getPosy()*2; + d3ddevice->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0f,0); + break; + }; + D3DSURFACE_DESC surf_desc; + present->GetDesc(&surf_desc);//for chop sides + RECT sourcerect= {0,0,surf_desc.Width,surf_desc.Height}; + if (video->getPseudoTVsize()==Video::ASPECT4X3 + && video->getMode()==Video::NORMAL + && video->getAspectRatio()==Video::ASPECT16X9) { + unsigned int correction=((double) (surf_desc.Width))*4.*9./3./16.; + sourcerect.left=(surf_desc.Width-correction)/2; + sourcerect.right=sourcerect.left+correction; + } + d3ddevice->StretchRect(present,&sourcerect,targetsurf ,&destrect,filter_type); + + } + } else { + VideoWin* video =(VideoWin*) Video::getInstance(); + //Clear Background + if (!video->isVideoOn()) d3ddevice->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0f,0); + } + LPDIRECT3DVERTEXBUFFER9 vb=NULL; + vb=InitVertexBuffer(targetdesc.Width,targetdesc.Height); + + //Drawing the OSD + if (d3ddevice->BeginScene()==D3D_OK) { + d3ddevice->SetStreamSource(0,vb,0,sizeof(OSDVERTEX)); + d3ddevice->SetFVF(D3DFVF_OSDVERTEX); + d3ddevice->SetTexture(0,((SurfaceWin*)screen)->getD3dtexture()); + //d3ddevice->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_MODULATE); + d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP,D3DTOP_MODULATE); + + d3ddevice->SetSamplerState(0,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR); + d3ddevice->SetSamplerState(0,D3DSAMP_MINFILTER,D3DTEXF_LINEAR); + + + d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE); + d3ddevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA); + d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + d3ddevice->SetRenderState(D3DRS_LIGHTING,FALSE); + + + d3ddevice->DrawPrimitive(D3DPT_TRIANGLEFAN,0,2); + d3ddevice->EndScene(); + //Show it to the user! + HRESULT hres; + if (swappy) + { + if (hres=swappy->Present(NULL,NULL,NULL,NULL,0)==D3DERR_DEVICELOST){ + //EndPainting(); + if (!external_driving) DoLost(); + } + } + else + { + if (hres=d3ddevice->Present(NULL,NULL,NULL,NULL)==D3DERR_DEVICELOST){ + //EndPainting(); + if (!external_driving) DoLost(); + } + } + + } + + vb->Release(); + EndPainting(); + + +// if (!external_driving) { +// Sleep(4);//The User can wait for 4 milliseconds to see his changes +// } +} + +bool OsdWin::DoLost(){ + Log::getInstance()->log("OSD", Log::WARN, "Direct3D Device Lost! Reobtaining..."); + ResetEvent(event); + if (external_driving && dsallocator!=NULL) { + dsallocator->LostDevice(d3ddevice,d3d); //Propagate the information through DS + } + //First we free up all resources + Video* video = Video::getInstance(); + ((SurfaceWin*)screen)->ReleaseSurface(); + if (d3drtsurf) d3drtsurf->Release(); + d3drtsurf=NULL; + D3DPRESENT_PARAMETERS d3dparas; + ZeroMemory(&d3dparas,sizeof(d3dparas)); + d3dparas.BackBufferWidth=BACKBUFFER_WIDTH; + d3dparas.BackBufferHeight=BACKBUFFER_HEIGHT; + d3dparas.Windowed=TRUE; + d3dparas.SwapEffect=D3DSWAPEFFECT_COPY; + + if (swapsurf) {swapsurf->Release();swapsurf=NULL;}; + if (swappy) {swappy->Release();swappy=NULL;}; + + if (d3ddevice->Reset(&d3dparas)!=D3D_OK){ + return false; + } + d3ddevice->GetRenderTarget(0,&d3drtsurf); + if (d3ddevman) d3ddevman->ResetDevice(d3ddevice,dxvatoken); + //InitVertexBuffer(); + //Redraw Views, Chris could you add a member function to BoxStack, so that + // I can cause it to completely redraw the Views? + // Otherwise the OSD would be distorted after Device Lost + // FIXME + + SetEvent(event); + + + screen->create(video->getScreenWidth(), video->getScreenHeight()); + screen->display(); + + return true; + +} +LPDIRECT3DDEVICE9 OsdWin::getD3dDev() { + WaitForSingleObject(event,INFINITE);//We will only return if we are initted + return d3ddevice; +} + +LPDIRECT3D9 OsdWin::getD3d() { + WaitForSingleObject(event,INFINITE);//We will only return if we are initted + return d3d; +} + +void OsdWin::BeginPainting() {//We synchronize calls to d3d between different threads + WaitForSingleObject(d3dmutex,INFINITE); + LockDevice(); +} + +void OsdWin::EndPainting() { + UnlockDevice(); + ReleaseMutex(d3dmutex); +} + +void OsdWin::setExternalDriving(DsAllocator* dsall,DWORD width, DWORD height) { + + if (swappy) + { + BeginPainting(); + d3ddevice->StretchRect(swapsurf,NULL,d3drtsurf,NULL,filter_type); + LPDIRECT3DSWAPCHAIN9 temp=swappy; + LPDIRECT3DSURFACE9 tempsurf=swapsurf; + swappy=NULL; + swapsurf=NULL; + EndPainting(); + tempsurf->Release(); + temp->Release(); + } + + if (dsall==NULL) { + external_driving=false; + dsallocator=NULL; + return; + } + WaitForSingleObject(event,INFINITE);//We will only return if we are initted + BeginPainting(); + + if (width>BACKBUFFER_WIDTH || height>BACKBUFFER_HEIGHT) + { + D3DPRESENT_PARAMETERS d3dparas; + ZeroMemory(&d3dparas,sizeof(d3dparas)); + d3dparas.BackBufferWidth=width; + d3dparas.BackBufferHeight=height; + d3dparas.Windowed=TRUE; + d3dparas.SwapEffect=D3DSWAPEFFECT_COPY; + if (d3ddevice->CreateAdditionalSwapChain(&d3dparas,&swappy)!=D3D_OK){ + Log::getInstance()->log("OSD", Log::WARN, "Could not create Swap Chain!"); + //return 0; + } else { + swappy->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&swapsurf); + } + Log::getInstance()->log("OSD", Log::INFO, "Create Additional Swap Chain %d %d!",width,height); + } + + dsallocator=dsall; + external_driving=true; + + EndPainting(); +} + +void OsdWin::Blank() { + WaitForSingleObject(event,INFINITE); + BeginPainting(); + d3ddevice->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0f,0); + EndPainting(); +} diff --git a/osdwin.h b/osdwin.h index a470fbc..81f64f5 100644 --- a/osdwin.h +++ b/osdwin.h @@ -1,122 +1,124 @@ -/* - 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 OSDWIN_H -#define OSDWIN_H - -#include - -#include "osd.h" -#include "defines.h" -#include "log.h" -#include "threadwin.h" -#include -#include -#include - -struct OSDVERTEX -{ - FLOAT x,y,z,rhw; - DWORD c; - FLOAT u,v; -}; - -#define D3DFVF_OSDVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE| D3DFVF_TEX1) - -class DsAllocator; - - - -class OsdWin : public Osd, public ThreadWin -{ - public: - OsdWin(); - ~OsdWin(); - - int init(void* device); - int shutdown(); - - int getFD(); - - void screenShot(char* fileName); - - void threadMethod(); - void threadPostStopCleanup(); - - LPDIRECT3DDEVICE9 getD3dDev() ; - LPDIRECT3D9 getD3d() ; - // This function is called from the WinMain function in order to get Screen updates - void Render(); - void RenderDS(LPDIRECT3DSURFACE9 present); - void BeginPainting(); - void EndPainting(); - void setExternalDriving(DsAllocator* dsall,DWORD width, DWORD height); - void Blank(); - DWORD getFilterCaps(); - DWORD getFilterType(){return filter_type;}; - void setFilterType(D3DTEXTUREFILTERTYPE type) {filter_type=type;}; - - - - - - enum EVR_state { - EVR_pres_off=0, - EVR_pres_started, - EVR_pres_pause - }; - - void SetEVRStatus(EVR_state new_state){evrstate=new_state;}; - - IDirect3DDeviceManager9 * getD3dMan() {return d3ddevman;}; - bool IsEvrSupported() {return evrsupported;}; - HWND getWindow() {return window;}; - -private: - void LockDevice(); - void UnlockDevice(); - - LPDIRECT3D9 d3d; - LPDIRECT3DDEVICE9 d3ddevice; -// LPDIRECT3DVERTEXBUFFER9 d3dvb; - LPDIRECT3DSURFACE9 d3drtsurf; - LPDIRECT3DSWAPCHAIN9 swappy; - LPDIRECT3DSURFACE9 swapsurf; - DsAllocator* dsallocator; - // This indicates, that currently a video is played, thus the osd updates are driven by the Directshow Filtersystem - bool external_driving; - HANDLE d3dmutex; - DWORD lastrendertime; - void InternalRendering(LPDIRECT3DSURFACE9 present); - bool DoLost(); - LPDIRECT3DVERTEXBUFFER9 InitVertexBuffer(DWORD width, DWORD height); - OSDVERTEX osdvertices[4]; - HANDLE event; - D3DTEXTUREFILTERTYPE filter_type; - EVR_state evrstate; - bool evrsupported; - HWND window; - - UINT dxvatoken; - IDirect3DDeviceManager9 *d3ddevman; - HANDLE dxvadevicehandle; -}; - -#endif +/* + 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 OSDWIN_H +#define OSDWIN_H + +#include + +#include "osd.h" +#include "defines.h" +#include "log.h" +#include "threadwin.h" +#include +#include +#include + +struct OSDVERTEX +{ + FLOAT x,y,z,rhw; + DWORD c; + FLOAT u,v; +}; + +#define D3DFVF_OSDVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE| D3DFVF_TEX1) + +class DsAllocator; + + + +class OsdWin : public Osd, public ThreadWin +{ + public: + OsdWin(); + ~OsdWin(); + + int init(void* device); + int shutdown(); + + int getFD(); + + void screenShot(const char* fileName); + + Surface * createNewSurface(); + + void threadMethod(); + void threadPostStopCleanup(); + + LPDIRECT3DDEVICE9 getD3dDev() ; + LPDIRECT3D9 getD3d() ; + // This function is called from the WinMain function in order to get Screen updates + void Render(); + void RenderDS(LPDIRECT3DSURFACE9 present); + void BeginPainting(); + void EndPainting(); + void setExternalDriving(DsAllocator* dsall,DWORD width, DWORD height); + void Blank(); + DWORD getFilterCaps(); + DWORD getFilterType(){return filter_type;}; + void setFilterType(D3DTEXTUREFILTERTYPE type) {filter_type=type;}; + + + + + + enum EVR_state { + EVR_pres_off=0, + EVR_pres_started, + EVR_pres_pause + }; + + void SetEVRStatus(EVR_state new_state){evrstate=new_state;}; + + IDirect3DDeviceManager9 * getD3dMan() {return d3ddevman;}; + bool IsEvrSupported() {return evrsupported;}; + HWND getWindow() {return window;}; + +private: + void LockDevice(); + void UnlockDevice(); + + LPDIRECT3D9 d3d; + LPDIRECT3DDEVICE9 d3ddevice; +// LPDIRECT3DVERTEXBUFFER9 d3dvb; + LPDIRECT3DSURFACE9 d3drtsurf; + LPDIRECT3DSWAPCHAIN9 swappy; + LPDIRECT3DSURFACE9 swapsurf; + DsAllocator* dsallocator; + // This indicates, that currently a video is played, thus the osd updates are driven by the Directshow Filtersystem + bool external_driving; + HANDLE d3dmutex; + DWORD lastrendertime; + void InternalRendering(LPDIRECT3DSURFACE9 present); + bool DoLost(); + LPDIRECT3DVERTEXBUFFER9 InitVertexBuffer(DWORD width, DWORD height); + OSDVERTEX osdvertices[4]; + HANDLE event; + D3DTEXTUREFILTERTYPE filter_type; + EVR_state evrstate; + bool evrsupported; + HWND window; + + UINT dxvatoken; + IDirect3DDeviceManager9 *d3ddevman; + HANDLE dxvadevicehandle; +}; + +#endif diff --git a/player.cc b/player.cc index f250b35..c4f9569 100644 --- a/player.cc +++ b/player.cc @@ -1,1287 +1,1414 @@ -/* - Copyright 2004-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 "player.h" - -#include "log.h" -#include "audio.h" -#include "video.h" -#include "demuxervdr.h" -#include "demuxerts.h" -#include "vdr.h" -#include "messagequeue.h" -#include "remote.h" -#include "message.h" -#include "dvbsubtitles.h" -#include "osdreceiver.h" - -#define USER_RESPONSE_TIME 500 // Milliseconds - -// ----------------------------------- Called from outside, one offs or info funcs - -Player::Player(MessageQueue* tmessageQueue, void* tmessageReceiver, OSDReceiver* tosdReceiver) -: vfeed(this), afeed(this), tfeed(this) -{ - messageQueue = tmessageQueue; - messageReceiver = tmessageReceiver; - osdReceiver = tosdReceiver; - audio = Audio::getInstance(); - video = Video::getInstance(); - logger = Log::getInstance(); - vdr = VDR::getInstance(); - initted = false; - lengthBytes = 0; - lengthFrames = 0; - currentFrameNumber = 0; - state = S_STOP; - ifactor = 4; - is_pesrecording=true; - - subtitlesShowing = false; - - videoStartup = false; - threadBuffer = NULL; - - blockSize = 100000; - startupBlockSize = 250000; - video->turnVideoOn(); -} - -Player::~Player() -{ - if (initted) shutdown(); -} - -int Player::init(bool p_isPesRecording,double framespersecond) -{ - if (initted) return 0; -#ifndef WIN32 - pthread_mutex_init(&mutex, NULL); -#else - mutex=CreateMutex(NULL,FALSE,NULL); -#endif - is_pesrecording = p_isPesRecording; - fps=framespersecond; - if (is_pesrecording) - demuxer = new DemuxerVDR(); - else - demuxer = new DemuxerTS(); - if (!demuxer) return 0; - subtitles = new DVBSubtitles(osdReceiver); - if (!subtitles) return 0; - - teletext = new TeletextDecoderVBIEBU(); - if (!teletext) return 0; - teletext->setRecordigMode(true); - unsigned int demux_video_size=2097152; - if (video->supportsh264()) demux_video_size*=5; - - if (!demuxer->init(this, audio, video,teletext, demux_video_size,524288,65536, framespersecond, subtitles)) - { - logger->log("Player", Log::ERR, "Demuxer failed to init"); - shutdown(); - return 0; - } - - vfeed.init(); - afeed.init(); - tfeed.init(); - - video->stop(); - video->blank(); - audio->stop(); - - initted = true; - return 1; -} - -int Player::shutdown() -{ - if (!initted) return 0; - switchState(S_STOP); - initted = false; - - delete demuxer; - demuxer = NULL; - delete subtitles; - subtitles = NULL; - delete teletext; - teletext = NULL; - -#ifdef WIN32 - CloseHandle(mutex); -#endif - - return 1; -} - -void Player::setStartFrame(ULONG startFrame) -{ - currentFrameNumber = startFrame; -} - -void Player::setLengthBytes(ULLONG length) -{ - lengthBytes = length; - logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes); -} - -void Player::setLengthFrames(ULONG length) -{ - lengthFrames = length; - logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames); -} - -ULONG Player::getLengthFrames() -{ - return lengthFrames; -} - -ULONG Player::getCurrentFrameNum() -{ - if (startup) return 0; - switch(state) - { - case S_PLAY: - case S_PAUSE_P: - return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp()); - case S_PAUSE_I: - case S_FFWD: - case S_FBWD: - return currentFrameNumber; - default: - return 0; // shouldn't happen - } -} - -bool* Player::getDemuxerMpegAudioChannels() -{ - return demuxer->getmpAudioChannels(); -} - -bool* Player::getDemuxerAc3AudioChannels() -{ - return demuxer->getac3AudioChannels(); -} - -bool* Player::getDemuxerSubtitleChannels() -{ - return demuxer->getSubtitleChannels(); -} - -int Player::getCurrentAudioChannel() -{ - if (is_pesrecording) { - return demuxer->getselAudioChannel(); - } else { - return ((DemuxerTS*)demuxer)->getAID(); - } -} - -int Player::getCurrentSubtitleChannel() -{ - if (is_pesrecording) { - return demuxer->getselSubtitleChannel(); - } else { - return ((DemuxerTS*)demuxer)->getSubID(); - } -} - -void Player::setSubtitleChannel(int newChannel) -{ - if (is_pesrecording) { - return demuxer->setDVBSubtitleStream(newChannel); - } else { - return ((DemuxerTS*)demuxer)->setSubID(newChannel); - } -} - -int *Player::getTeletxtSubtitlePages() -{ - return teletext->getSubtitlePages(); -} - -void Player::setAudioChannel(int newChannel, int type) -{ - if (is_pesrecording) { - demuxer->setAudioStream(newChannel); - return; - } else { - ((DemuxerTS*)demuxer)->setAID(newChannel,type); - return; - } -} - -bool Player::toggleSubtitles() -{ - if (!subtitlesShowing) - { - subtitlesShowing = true; - subtitles->show(); - } - else - { - subtitlesShowing = false; - subtitles->hide(); - } - return subtitlesShowing; -} - -void Player::turnSubtitlesOn(bool ison) { - if (ison) - { - subtitlesShowing = true; - subtitles->show(); - } - else - { - subtitlesShowing = false; - subtitles->hide(); - } - -} - -Channel Player::getDemuxerChannel() { - if (!is_pesrecording) { - return ((DemuxerTS*) demuxer)->getChannelInfo(); - } - return Channel(); //Should not happen! -} - - -// ----------------------------------- Externally called events - -void Player::play() -{ - if (!initted) return; - if (state == S_PLAY) return; - lock(); - - bool doUnlock = false; - if (state == S_PAUSE_P) doUnlock = true; - switchState(S_PLAY); - if (doUnlock) unLock(); -} - -void Player::stop() -{ - if (!initted) return; - if (state == S_STOP) return; - lock(); - logger->log("Player", Log::DEBUG, "Stop called lock"); - switchState(S_STOP); - unLock(); -} - -void Player::pause() -{ - if (!initted) return; - lock(); - - if ((state == S_FFWD) || (state == S_FBWD)) - { - switchState(S_PAUSE_I); - } - else if ((state == S_PAUSE_I) || (state == S_PAUSE_P)) - { - switchState(S_PLAY); - } - else - { - switchState(S_PAUSE_P); - } - - unLock(); -} - -void Player::fastForward() -{ - if (!initted) return; - lock(); - - if (state == S_FFWD) - { - // change the rate - switch(ifactor) - { - case 4: ifactor = 8; break; - case 8: ifactor = 16; break; - case 16: ifactor = 32; break; - case 32: ifactor = 4; break; - } - } - else - { - ifactor = 4; - switchState(S_FFWD); - } - unLock(); -} - -void Player::fastBackward() -{ - if (!initted) return; - lock(); - - if (state == S_FBWD) - { - // change the rate - switch(ifactor) - { - case 4: ifactor = 8; break; - case 8: ifactor = 16; break; - case 16: ifactor = 32; break; - case 32: ifactor = 4; break; - } - } - else - { - ifactor = 4; - switchState(S_FBWD); - } - unLock(); -} - -void Player::jumpToPercent(double percent) -{ - lock(); - logger->log("Player", Log::DEBUG, "JUMP TO %f%%", percent); - ULONG newFrame = (ULONG)(percent * lengthFrames / 100); - switchState(S_JUMP, newFrame); -// unLock(); - let thread unlock this -} - -void Player::jumpToMark(int mark) -{ - lock(); - logger->log("Player", Log::DEBUG, "JUMP TO MARK %i%%", mark); - switchState(S_JUMP, mark); -// unLock(); - let thread unlock this -} - -void Player::jumpToFrameP(int newFrame) -{ - lock(); - logger->log("Player", Log::DEBUG, "JUMP TO FRAME AND PAUSE %i", newFrame); - switchState(S_JUMP_PI, newFrame); - unLock(); -} - -void Player::skipForward(int seconds) -{ - lock(); - logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds); - ULONG newFrame = getCurrentFrameNum(); - if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid - newFrame +=(ULONG) (((double)seconds) * fps); - if (newFrame > lengthFrames) { switchState(S_PLAY); unLock(); } - else switchState(S_JUMP, newFrame); -// unLock(); - let thread unlock this -} - -void Player::skipBackward(int seconds) -{ - lock(); - logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds); - long newFrame = getCurrentFrameNum(); - if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid - newFrame -= (ULONG) (((double)seconds) * fps); - if (newFrame < 0) newFrame = 0; - switchState(S_JUMP, newFrame); -// unLock(); - let thread unlock this -} - -// ----------------------------------- Implementations called events - -void Player::switchState(UCHAR toState, ULONG jumpFrame) -{ - if (!initted) return; - - logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState); - - switch(state) // current state selector - { - case S_PLAY: // from S_PLAY ----------------------------------- - { - switch(toState) - { - case S_PLAY: // to S_PLAY - { - return; - } - case S_PAUSE_P: // to S_PAUSE_P - { - video->pause(); - audio->pause(); - state = S_PAUSE_P; - return; - } - case S_PAUSE_I: // to S_PAUSE_I - { - // can't occur - return; - } - case S_FFWD: // to S_FFWD - { - currentFrameNumber = getCurrentFrameNum(); - audio->systemMuteOn(); - threadStop(); - vfeed.stop(); - afeed.stop(); - tfeed.stop(); - subtitles->stop(); - demuxer->flush(); - state = S_FFWD; - threadStart(); - return; - } - case S_FBWD: // to S_FBWD - { - currentFrameNumber = getCurrentFrameNum(); - audio->systemMuteOn(); - threadStop(); - vfeed.stop(); - afeed.stop(); - tfeed.stop(); - subtitles->stop(); - demuxer->flush(); - state = S_FBWD; - threadStart(); - return; - } - case S_STOP: // to S_STOP - { - vfeed.stop(); - afeed.stop(); - tfeed.stop(); - subtitles->stop(); - threadStop(); - video->stop(); - video->blank(); - audio->stop(); - audio->unPause(); - video->reset(); - demuxer->reset(); - state = S_STOP; - return; - } - case S_JUMP: // to S_JUMP - { - restartAtFrame(jumpFrame); - return; - } - case S_JUMP_PI: // to S_JUMP_PI - { - audio->systemMuteOn(); - threadStop(); - vfeed.stop(); - afeed.stop(); - tfeed.stop(); - subtitles->stop(); - demuxer->flush(); - state = S_PAUSE_I; - video->reset(); - video->play(); - restartAtFramePI(jumpFrame); - return; - } - } - } - case S_PAUSE_P: // from S_PAUSE_P ----------------------------------- - { - switch(toState) - { - case S_PLAY: // to S_PLAY - { - video->unPause(); - audio->unPause(); - state = S_PLAY; - return; - } - case S_PAUSE_P: // to S_PAUSE_P - { - return; - } - case S_PAUSE_I: // to S_PAUSE_I - { - return; - } - case S_FFWD: // to S_FFWD - { - currentFrameNumber = getCurrentFrameNum(); - audio->systemMuteOn(); - vfeed.stop(); - afeed.stop(); - tfeed.stop(); - subtitles->stop(); - threadStop(); - video->unPause(); - audio->unPause(); - state = S_FFWD; - threadStart(); - return; - } - case S_FBWD: // to S_FBWD - { - currentFrameNumber = getCurrentFrameNum(); - audio->systemMuteOn(); - vfeed.stop(); - afeed.stop(); - tfeed.stop(); - subtitles->stop(); - threadStop(); - video->unPause(); - audio->unPause(); - state = S_FBWD; - threadStart(); - return; - } - case S_STOP: // to S_STOP - { - vfeed.stop(); - afeed.stop(); - tfeed.stop(); - subtitles->stop(); - threadStop(); - video->stop(); - video->blank(); - audio->stop(); - video->reset(); - audio->unPause(); - demuxer->reset(); - audio->systemMuteOff(); - state = S_STOP; - return; - } - case S_JUMP: // to S_JUMP - { - state = S_PLAY; - audio->systemMuteOn(); - audio->unPause(); - restartAtFrame(jumpFrame); - return; - } - case S_JUMP_PI: // to S_JUMP_PI - { - audio->systemMuteOn(); - audio->unPause(); - threadStop(); - vfeed.stop(); - afeed.stop(); - tfeed.stop(); - subtitles->stop(); - demuxer->flush(); - state = S_PAUSE_I; - video->reset(); - video->play(); - restartAtFramePI(jumpFrame); - return; - } - } - } - case S_PAUSE_I: // from S_PAUSE_I ----------------------------------- - { - switch(toState) - { - case S_PLAY: // to S_PLAY - { - state = S_PLAY; - restartAtFrame(currentFrameNumber); - return; - } - case S_PAUSE_P: // to S_PAUSE_P - { - return; - } - case S_PAUSE_I: // to S_PAUSE_I - { - return; - } - case S_FFWD: // to S_FFWD - { - state = S_FFWD; - threadStart(); - return; - } - case S_FBWD: // to S_FBWD - { - state = S_FBWD; - threadStart(); - return; - } - case S_STOP: // to S_STOP - { - video->stop(); - video->blank(); - audio->stop(); - video->reset(); - demuxer->reset(); - audio->systemMuteOff(); - state = S_STOP; - return; - } - case S_JUMP: // to S_JUMP - { - state = S_PLAY; - restartAtFrame(jumpFrame); - return; - } - case S_JUMP_PI: // to S_JUMP_PI - { - restartAtFramePI(jumpFrame); - return; - } - } - } - case S_FFWD: // from S_FFWD ----------------------------------- - { - switch(toState) - { - case S_PLAY: // to S_PLAY - { - state = S_PLAY; - ULONG stepback = (ULONG)(((double)USER_RESPONSE_TIME * ifactor) * fps / 1000.); - if (stepback < currentFrameNumber) - currentFrameNumber -= stepback; - else - currentFrameNumber = 0; - restartAtFrame(currentFrameNumber); - return; - } - case S_PAUSE_P: // to S_PAUSE_P - { - // can't occur - return; - } - case S_PAUSE_I: // to S_PAUSE_I - { - threadStop(); - state = S_PAUSE_I; - return; - } - case S_FFWD: // to S_FFWD - { - return; - } - case S_FBWD: // to S_FBWD - { - threadStop(); - state = S_FBWD; - threadStart(); - return; - } - case S_STOP: // to S_STOP - { - threadStop(); - video->stop(); - video->blank(); - audio->stop(); - video->reset(); - demuxer->reset(); - state = S_STOP; - return; - } - case S_JUMP: // to S_JUMP - { - state = S_PLAY; - restartAtFrame(jumpFrame); - return; - } - case S_JUMP_PI: // to S_JUMP_PI - { - threadStop(); - state = S_PAUSE_I; - restartAtFramePI(jumpFrame); - return; - } - } - } - case S_FBWD: // from S_FBWD ----------------------------------- - { - switch(toState) - { - case S_PLAY: // to S_PLAY - { - state = S_PLAY; - restartAtFrame(currentFrameNumber); - return; - } - case S_PAUSE_P: // to S_PAUSE_P - { - // can't occur - return; - } - case S_PAUSE_I: // to S_PAUSE_I - { - threadStop(); - state = S_PAUSE_I; - return; - } - case S_FFWD: // to S_FFWD - { - threadStop(); - state = S_FFWD; - threadStart(); - return; - } - case S_FBWD: // to S_FBWD - { - return; - } - case S_STOP: // to S_STOP - { - threadStop(); - video->stop(); - video->blank(); - audio->stop(); - video->reset(); - demuxer->reset(); - state = S_STOP; - return; - } - case S_JUMP: // to S_JUMP - { - state = S_PLAY; - restartAtFrame(jumpFrame); - return; - } - case S_JUMP_PI: // to S_JUMP_PI - { - threadStop(); - state = S_PAUSE_I; - restartAtFramePI(jumpFrame); - return; - } - } - } - case S_STOP: // from S_STOP ----------------------------------- - { - switch(toState) - { - case S_PLAY: // to S_PLAY - { - startup = true; - - audio->reset(); - audio->setStreamType(Audio::MPEG2_PES); - audio->systemMuteOff(); - video->reset(); - demuxer->reset(); - // FIXME use restartAtFrame here? - if (currentFrameNumber > lengthFrames) currentFrameNumber = 0; - demuxer->setFrameNum(currentFrameNumber); - demuxer->seek(); - videoStartup = true; - state = S_PLAY; - threadStart(); - logger->log("Player", Log::DEBUG, "Immediate play"); - afeed.start(); - vfeed.start(); - tfeed.start(); - subtitles->start(); - video->sync(); - audio->sync(); - audio->play(); - video->pause(); - return; - } - case S_PAUSE_P: // to S_PAUSE_P - { - return; - } - case S_PAUSE_I: // to S_PAUSE_I - { - return; - } - case S_FFWD: // to S_FFWD - { - return; - } - case S_FBWD: // to S_FBWD - { - return; - } - case S_STOP: // to S_STOP - { - return; - } - case S_JUMP: // to S_JUMP - { - return; - } - case S_JUMP_PI: // to S_JUMP_PI - { - return; - } - } - } - // case S_JUMP cannot be a start state because it auto flips to play - // case S_JUMP_PI cannot be a start state because it auto flips to S_PAUSE_I - } -} - -// ----------------------------------- Internal functions - -void Player::lock() -{ -#ifndef WIN32 - pthread_mutex_lock(&mutex); - logger->log("Player", Log::DEBUG, "LOCKED"); - -#else - WaitForSingleObject(mutex, INFINITE); -#endif -} - -void Player::unLock() -{ -#ifndef WIN32 - logger->log("Player", Log::DEBUG, "UNLOCKING"); - pthread_mutex_unlock(&mutex); -#else - ReleaseMutex(mutex); -#endif -} - -void Player::restartAtFrame(ULONG newFrame) -{ - vfeed.stop(); - afeed.stop(); - tfeed.stop(); - subtitles->stop(); - threadStop(); - video->stop(); - video->reset(); - audio->reset(); - audio->setStreamType(Audio::MPEG2_PES); - demuxer->flush(); - demuxer->seek(); - currentFrameNumber = newFrame; - demuxer->setFrameNum(newFrame); - videoStartup = true; - afeed.start(); - tfeed.start(); - vfeed.start(); - subtitles->start(); - threadStart(); - audio->play(); - video->sync(); - audio->sync(); - audio->systemMuteOff(); - audio->doMuting(); -} - - -void Player::restartAtFramePI(ULONG newFrame) -{ - ULLONG filePos; - ULONG nextiframeNumber; - ULONG iframeLength; - ULONG iframeNumber; - - UCHAR* buffer; - UINT amountReceived; - UINT videoLength; - - // newFrame could be anywhere, go forwards to next I-Frame - if (!vdr->getNextIFrame(newFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return; - - // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame - vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength); - - buffer = vdr->getBlock(filePos, iframeLength, &amountReceived); - if (!vdr->isConnected()) - { - if (buffer) free(buffer); - doConnectionLost(); - } - else - { - videoLength = demuxer->stripAudio(buffer, amountReceived); - video->displayIFrame(buffer, videoLength); - video->displayIFrame(buffer, videoLength); // If you do it twice, it works :) - free(buffer); - currentFrameNumber = iframeNumber; - } -} - -void Player::doConnectionLost() -{ - logger->log("Player", Log::DEBUG, "Connection lost, sending message"); - Message* m = new Message(); - m->to = messageReceiver; - m->from = this; - m->message = Message::PLAYER_EVENT; - m->parameter = Player::CONNECTION_LOST; - messageQueue->postMessage(m); -} - -// ----------------------------------- Callback - -void Player::call(void* caller) -{ - if (caller == demuxer) - { - logger->log("Player", Log::DEBUG, "Callback from demuxer"); - - if (video->getTVsize() == Video::ASPECT4X3) - { - logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching"); - return; - } - - int dxCurrentAspect = demuxer->getAspectRatio(); - if (dxCurrentAspect == Demuxer::ASPECT_4_3) - { - logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV"); - video->setAspectRatio(Video::ASPECT4X3); - - Message* m = new Message(); - m->from = this; - m->to = messageReceiver; - m->message = Message::PLAYER_EVENT; - m->parameter = Player::ASPECT43; - messageQueue->postMessageFromOuterSpace(m); - } - else if (dxCurrentAspect == Demuxer::ASPECT_16_9) - { - logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV"); - video->setAspectRatio(Video::ASPECT16X9); - - Message* m = new Message(); - m->from = this; - m->to = messageReceiver; - m->message = Message::PLAYER_EVENT; - m->parameter = Player::ASPECT169; - messageQueue->postMessageFromOuterSpace(m); - } - else - { - logger->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring"); - } - - } - else - { - if (videoStartup) - { - videoStartup = false; - video->reset(); - video->play(); - video->sync(); - vfeed.release(); - unLock(); - } - - threadSignalNoLock(); - } -} - -// ----------------------------------- Feed thread - -void Player::threadMethod() -{ - // this method used to be simple, the only thing it does - // is farm out to threadFeed Live/Play/Scan - // All the guff is to support scan hitting one end - - if ((state == S_FFWD) || (state == S_FBWD)) - { - threadFeedScan(); - // if this returns then scan hit one end - if (state == S_FFWD) // scan hit the end. stop - { - threadCheckExit(); - Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex - m->to = messageReceiver; - m->from = this; - m->message = Message::PLAYER_EVENT; - m->parameter = STOP_PLAYBACK; - logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue); - messageQueue->postMessage(m); - logger->log("Player", Log::DEBUG, "Message posted..."); - return; - } - // if execution gets to here, threadFeedScan hit the start, go to play mode - state = S_PLAY; - audio->reset(); - audio->setStreamType(Audio::MPEG2_PES); - demuxer->flush(); - demuxer->seek(); - demuxer->setFrameNum(currentFrameNumber); - videoStartup = true; - afeed.start(); - tfeed.start(); - vfeed.start(); - subtitles->start(); - audio->play(); - audio->sync(); - audio->systemMuteOff(); - audio->doMuting(); - } - - if (state == S_PLAY) threadFeedPlay(); -} - -void Player::threadFeedPlay() -{ - ULLONG feedPosition; - UINT thisRead, writeLength, thisWrite, askFor; - time_t lastRescan = time(NULL); - - feedPosition = vdr->positionFromFrameNumber(currentFrameNumber); - if (!vdr->isConnected()) { doConnectionLost(); return; } - logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition); - - - while(1) - { - thisRead = 0; - writeLength = 0; - thisWrite = 0; - - threadCheckExit(); - - // If we havn't rescanned for a while.. - if ((lastRescan + 60) < time(NULL)) - { - lengthBytes = vdr->rescanRecording(&lengthFrames); - if (!vdr->isConnected()) { doConnectionLost(); return; } - logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes); - lastRescan = time(NULL); - } - - if (feedPosition >= lengthBytes) break; // finished playback - - if (startup) - { - if (startupBlockSize > lengthBytes) - askFor = lengthBytes; // is a very small recording! - else - askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams - } - else - { - if ((feedPosition + blockSize) > lengthBytes) // last block of recording - askFor = lengthBytes - feedPosition; - else // normal - askFor = blockSize; - } - //logger->log("Player", Log::DEBUG, "Get Block in"); - - threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead); - //logger->log("Player", Log::DEBUG, "Get Block out"); - - feedPosition += thisRead; - - if (!vdr->isConnected()) - { - doConnectionLost(); - return; - } - - if (!threadBuffer) break; - - if (startup) - { - int a_stream = demuxer->scan(threadBuffer, thisRead); - demuxer->setAudioStream(a_stream); - logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream); - startup = false; - } - - threadCheckExit(); - - while(writeLength < thisRead) - { - //logger->log("Player", Log::DEBUG, "Put in"); - thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength); - //logger->log("Player", Log::DEBUG, "Put out"); - writeLength += thisWrite; - - if (!thisWrite) - { - // demuxer is full and can't take anymore - threadLock(); - threadWaitForSignal(); - threadUnlock(); - } - - threadCheckExit(); - } - - free(threadBuffer); - threadBuffer = NULL; - - } - - // end of recording - logger->log("Player", Log::DEBUG, "Recording playback ends"); - - if (videoStartup) // oh woe. there never was a stream, I was conned! - { - videoStartup = false; - unLock(); - MILLISLEEP(500); // I think this will solve a race - } - - threadCheckExit(); - - - Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex - m->to = messageReceiver; - m->from = this; - m->message = Message::PLAYER_EVENT; - m->parameter = Player::STOP_PLAYBACK; - logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue); - messageQueue->postMessage(m); -} - -void Player::threadFeedScan() -{ - // This method is actually really simple - get frame from vdr, - // spit it at the video chip, wait for a time. Most of the code here - // is to get the wait right so that the scan occurs at the correct rate. - - ULONG direction = 0; - ULONG baseFrameNumber = 0; - ULONG iframeNumber = 0; - ULONG iframeLength = 0; - ULLONG filePos; - UINT amountReceived; - UINT videoLength; - -#ifndef WIN32 - struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info - struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data - struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame -#else - DWORD clock0 = 0, clock1 = 0, clock2 = 0; -#endif - - int frameTimeOffset = 0; // Time in msec between frames - int disp_msec = 0; // Time taken to display data - int total_msec = 0; // Time taken to fetch data and display it - int sleepTime = 0; - - if (state == S_FFWD) direction = 1; // and 0 for backward - - while(1) - { - // Fetch I-frames until we get one that can be displayed in good time - // Repeat while clock0 + total_msec > clock2 + frameTimeOffset - - baseFrameNumber = currentFrameNumber; - do - { - threadCheckExit(); - if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength)) - return; - - if (iframeNumber >= lengthFrames) return; - // scan has got to the end of what we knew to be there before we started scanning - - baseFrameNumber = iframeNumber; - frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentFrameNumber) * 1000) / (fps * (double)ifactor)); -#ifndef WIN32 - gettimeofday(&clock0, NULL); -#else - clock0 = timeGetTime(); -#endif - } -#ifndef WIN32 - while (clock2.tv_sec != 0 && - (clock0.tv_sec - clock2.tv_sec) * 1000 + - (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec); -#else - while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset); -#endif - logger->log("Player", Log::DEBUG, "XXX Got frame"); - - threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived); - - if (!vdr->isConnected()) - { - if (threadBuffer) free(threadBuffer); - doConnectionLost(); - break; - } - -#ifndef WIN32 - gettimeofday(&clock1, NULL); - if (clock2.tv_sec != 0) - sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000 - + (clock2.tv_usec - clock1.tv_usec) / 1000 - + frameTimeOffset - disp_msec; -#else - clock1 = timeGetTime(); - if (clock2 != 0) - sleepTime = clock2 + frameTimeOffset - disp_msec - clock1; -#endif - if (sleepTime < 0) sleepTime = 0; - threadCheckExit(); - MILLISLEEP(sleepTime); - logger->log("Player", Log::DEBUG, "XXX Slept for %d", sleepTime); - - videoLength = demuxer->stripAudio(threadBuffer, amountReceived); - video->displayIFrame(threadBuffer, videoLength); - currentFrameNumber = iframeNumber; - free(threadBuffer); - threadBuffer = NULL; - -#ifndef WIN32 - gettimeofday(&clock2, NULL); - total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000 - + (clock2.tv_usec - clock0.tv_usec) / 1000 - - sleepTime; - disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000 - + (clock2.tv_usec - clock1.tv_usec) / 1000 - - sleepTime; -#else - clock2 = timeGetTime(); - total_msec = clock2 - clock0 - sleepTime; - disp_msec = clock2 - clock1 - sleepTime; -#endif - logger->log("Player", Log::DEBUG, "XXX disp_msec = %4d total_msec = %4d", disp_msec, total_msec); - } -} - -void Player::threadPostStopCleanup() -{ - if (threadBuffer) - { - free(threadBuffer); - threadBuffer = NULL; - } -} - -// ----------------------------------- Dev - -#ifdef DEV -void Player::test1() -{ - logger->log("Player", Log::DEBUG, "PLAYER TEST 1"); -} - -void Player::test2() -{ - logger->log("Player", Log::DEBUG, "PLAYER TEST 2"); -} -#endif +/* + Copyright 2004-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 "player.h" + +#include "log.h" +#include "audio.h" +#include "video.h" +#include "demuxervdr.h" +#include "demuxerts.h" +#include "vdr.h" +#include "messagequeue.h" +#include "remote.h" +#include "message.h" +#include "dvbsubtitles.h" +#include "osdreceiver.h" + +#define USER_RESPONSE_TIME 500 // Milliseconds + +// ----------------------------------- Called from outside, one offs or info funcs + +Player::Player(MessageQueue* tmessageQueue, void* tmessageReceiver, OSDReceiver* tosdReceiver) +: vfeed(this), afeed(this), tfeed(this) +{ + messageQueue = tmessageQueue; + messageReceiver = tmessageReceiver; + osdReceiver = tosdReceiver; + audio = Audio::getInstance(); + video = Video::getInstance(); + logger = Log::getInstance(); + vdr = VDR::getInstance(); + initted = false; + lengthBytes = 0; + lengthFrames = 0; + currentFrameNumber = 0; + state = S_STOP; + ifactor = 4; + is_pesrecording=true; + + subtitlesShowing = false; + + videoStartup = false; + threadBuffer = NULL; + + blockSize = 100000; + startupBlockSize = 250000; + video->turnVideoOn(); +} + +Player::~Player() +{ + if (initted) shutdown(); +} + +int Player::init(bool p_isPesRecording,double framespersecond) +{ + if (initted) return 0; +#ifndef WIN32 + pthread_mutex_init(&mutex, NULL); +#else + mutex=CreateMutex(NULL,FALSE,NULL); +#endif + is_pesrecording = p_isPesRecording; + fps=framespersecond; + if (is_pesrecording) + demuxer = new DemuxerVDR(); + else + demuxer = new DemuxerTS(); + if (!demuxer) return 0; + subtitles = new DVBSubtitles(osdReceiver); + if (!subtitles) return 0; + + teletext = new TeletextDecoderVBIEBU(); + if (!teletext) return 0; + teletext->setRecordigMode(true); + unsigned int demux_video_size=2097152; + if (video->supportsh264()) demux_video_size*=5; + + if (!demuxer->init(this, audio, video,teletext, demux_video_size,524288,65536, framespersecond, subtitles)) + { + logger->log("Player", Log::ERR, "Demuxer failed to init"); + shutdown(); + return 0; + } + + vfeed.init(); + afeed.init(); + tfeed.init(); + + video->stop(); + video->blank(); + audio->stop(); + + initted = true; + return 1; +} + +int Player::shutdown() +{ + if (!initted) return 0; + switchState(S_STOP); + initted = false; + + delete demuxer; + demuxer = NULL; + delete subtitles; + subtitles = NULL; + delete teletext; + teletext = NULL; + +#ifdef WIN32 + CloseHandle(mutex); +#endif + + return 1; +} + +void Player::setStartFrame(ULONG startFrame) +{ + currentFrameNumber = startFrame; +} + +void Player::setLengthBytes(ULLONG length) +{ + lengthBytes = length; + logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes); +} + +void Player::setLengthFrames(ULONG length) +{ + lengthFrames = length; + logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames); +} + +ULONG Player::getLengthFrames() +{ + return lengthFrames; +} + +ULONG Player::getCurrentFrameNum() +{ + if (startup) return 0; + switch(state) + { + case S_PLAY: + case S_PAUSE_P: + return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp()); + case S_PAUSE_I: + case S_FFWD: + case S_FBWD: + return currentFrameNumber; + default: + return 0; // shouldn't happen + } +} + +bool* Player::getDemuxerMpegAudioChannels() +{ + return demuxer->getmpAudioChannels(); +} + +bool* Player::getDemuxerAc3AudioChannels() +{ + return demuxer->getac3AudioChannels(); +} + +bool* Player::getDemuxerSubtitleChannels() +{ + return demuxer->getSubtitleChannels(); +} + +int Player::getCurrentAudioChannel() +{ + if (is_pesrecording) { + return demuxer->getselAudioChannel(); + } else { + return ((DemuxerTS*)demuxer)->getAID(); + } +} + +int Player::getCurrentSubtitleChannel() +{ + if (is_pesrecording) { + return demuxer->getselSubtitleChannel(); + } else { + return ((DemuxerTS*)demuxer)->getSubID(); + } +} + +void Player::setSubtitleChannel(int newChannel) +{ + if (is_pesrecording) { + demuxer->setDVBSubtitleStream(newChannel); + } else { + ((DemuxerTS*)demuxer)->setSubID(newChannel); + } +} + +int *Player::getTeletxtSubtitlePages() +{ + return teletext->getSubtitlePages(); +} + +void Player::setAudioChannel(int newChannel, int type) +{ + if (is_pesrecording) { + demuxer->setAudioStream(newChannel); + return; + } else { + ((DemuxerTS*)demuxer)->setAID(newChannel,type); + return; + } +} + +bool Player::toggleSubtitles() +{ + if (!subtitlesShowing) + { + subtitlesShowing = true; + subtitles->show(); + } + else + { + subtitlesShowing = false; + subtitles->hide(); + } + return subtitlesShowing; +} + +void Player::turnSubtitlesOn(bool ison) { + if (ison) + { + subtitlesShowing = true; + subtitles->show(); + } + else + { + subtitlesShowing = false; + subtitles->hide(); + } + +} + +Channel Player::getDemuxerChannel() { + if (!is_pesrecording) { + return ((DemuxerTS*) demuxer)->getChannelInfo(); + } + return Channel(); //Should not happen! +} + + +// ----------------------------------- Externally called events + +void Player::play() +{ + if (!initted) return; + if (state == S_PLAY) return; + lock(); + + bool doUnlock = false; + if (state == S_PAUSE_P) doUnlock = true; + switchState(S_PLAY); + if (doUnlock) unLock(); +} + +void Player::playpause() +{ + if (!initted) return; + lock(); + + bool doUnlock = false; + if (state==S_PLAY) { + doUnlock=true; + switchState(S_PAUSE_P); + } else { + if (state == S_PAUSE_P) doUnlock = true; + switchState(S_PLAY); + } + if (doUnlock) unLock(); + +} + + + + +void Player::stop() +{ + if (!initted) return; + if (state == S_STOP) return; + lock(); + logger->log("Player", Log::DEBUG, "Stop called lock"); + switchState(S_STOP); + unLock(); +} + +void Player::pause() +{ + if (!initted) return; + lock(); + + if ((state == S_FFWD) || (state == S_FBWD)) + { + switchState(S_PAUSE_I); + } + else if ((state == S_PAUSE_I) || (state == S_PAUSE_P)) + { + switchState(S_PLAY); + } + else + { + switchState(S_PAUSE_P); + } + + unLock(); +} + +void Player::fastForward() +{ + if (!initted) return; + lock(); + + if (state == S_FFWD) + { + // change the rate + switch(ifactor) + { + case 4: ifactor = 8; break; + case 8: ifactor = 16; break; + case 16: ifactor = 32; break; + case 32: ifactor = 4; break; + } + } + else + { + ifactor = 4; + switchState(S_FFWD); + } + unLock(); +} + +void Player::fastBackward() +{ + if (!initted) return; + lock(); + + if (state == S_FBWD) + { + // change the rate + switch(ifactor) + { + case 4: ifactor = 8; break; + case 8: ifactor = 16; break; + case 16: ifactor = 32; break; + case 32: ifactor = 4; break; + } + } + else + { + ifactor = 4; + switchState(S_FBWD); + } + unLock(); +} + +void Player::jumpToPercent(double percent) +{ + lock(); + logger->log("Player", Log::DEBUG, "JUMP TO %f%%", percent); + ULONG newFrame = (ULONG)(percent * lengthFrames / 100); + switchState(S_JUMP, newFrame); +// unLock(); - let thread unlock this +} + +void Player::jumpToMark(int mark) +{ + lock(); + logger->log("Player", Log::DEBUG, "JUMP TO MARK %i%%", mark); + switchState(S_JUMP, mark); +// unLock(); - let thread unlock this +} + +void Player::jumpToFrameP(int newFrame) +{ + lock(); + logger->log("Player", Log::DEBUG, "JUMP TO FRAME AND PAUSE %i", newFrame); + switchState(S_JUMP_PI, newFrame); + unLock(); +} + +void Player::skipForward(int seconds) +{ + lock(); + logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds); + ULONG newFrame = getCurrentFrameNum(); + if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid + newFrame +=(ULONG) (((double)seconds) * fps); + if (newFrame > lengthFrames) { switchState(S_PLAY); unLock(); } + else switchState(S_JUMP, newFrame); +// unLock(); - let thread unlock this +} + +void Player::skipBackward(int seconds) +{ + lock(); + logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds); + long newFrame = getCurrentFrameNum(); + if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid + newFrame -= (ULONG) (((double)seconds) * fps); + if (newFrame < 0) newFrame = 0; + switchState(S_JUMP, newFrame); +// unLock(); - let thread unlock this +} + +// ----------------------------------- Implementations called events + +void Player::switchState(UCHAR toState, ULONG jumpFrame) +{ + if (!initted) return; + + logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState); + + switch(state) // current state selector + { + case S_PLAY: // from S_PLAY ----------------------------------- + { + switch(toState) + { + case S_PLAY: // to S_PLAY + { + return; + } + case S_PAUSE_P: // to S_PAUSE_P + { + video->pause(); + audio->pause(); + state = S_PAUSE_P; + return; + } + case S_PAUSE_I: // to S_PAUSE_I + { + // can't occur + return; + } + case S_FFWD: // to S_FFWD + { + currentFrameNumber = getCurrentFrameNum(); + audio->systemMuteOn(); + threadStop(); + vfeed.stop(); + afeed.stop(); + tfeed.stop(); + subtitles->stop(); + demuxer->flush(); + state = S_FFWD; + threadStart(); + return; + } + case S_FBWD: // to S_FBWD + { + currentFrameNumber = getCurrentFrameNum(); + audio->systemMuteOn(); + threadStop(); + vfeed.stop(); + afeed.stop(); + tfeed.stop(); + subtitles->stop(); + demuxer->flush(); + state = S_FBWD; + threadStart(); + return; + } + case S_STOP: // to S_STOP + { + vfeed.stop(); + afeed.stop(); + tfeed.stop(); + subtitles->stop(); + threadStop(); + video->stop(); + video->blank(); + audio->stop(); + audio->unPause(); + video->reset(); + demuxer->reset(); + state = S_STOP; + return; + } + case S_JUMP: // to S_JUMP + { + restartAtFrame(jumpFrame); + return; + } + case S_JUMP_PI: // to S_JUMP_PI + { + audio->systemMuteOn(); + threadStop(); + vfeed.stop(); + afeed.stop(); + tfeed.stop(); + subtitles->stop(); + demuxer->flush(); + state = S_PAUSE_I; + video->reset(); + video->play(); + restartAtFramePI(jumpFrame); + return; + } + } + } + case S_PAUSE_P: // from S_PAUSE_P ----------------------------------- + { + switch(toState) + { + case S_PLAY: // to S_PLAY + { + video->unPause(); + audio->unPause(); + state = S_PLAY; + return; + } + case S_PAUSE_P: // to S_PAUSE_P + { + return; + } + case S_PAUSE_I: // to S_PAUSE_I + { + return; + } + case S_FFWD: // to S_FFWD + { + currentFrameNumber = getCurrentFrameNum(); + audio->systemMuteOn(); + vfeed.stop(); + afeed.stop(); + tfeed.stop(); + subtitles->stop(); + threadStop(); + video->unPause(); + audio->unPause(); + state = S_FFWD; + threadStart(); + return; + } + case S_FBWD: // to S_FBWD + { + currentFrameNumber = getCurrentFrameNum(); + audio->systemMuteOn(); + vfeed.stop(); + afeed.stop(); + tfeed.stop(); + subtitles->stop(); + threadStop(); + video->unPause(); + audio->unPause(); + state = S_FBWD; + threadStart(); + return; + } + case S_STOP: // to S_STOP + { + vfeed.stop(); + afeed.stop(); + tfeed.stop(); + subtitles->stop(); + threadStop(); + video->stop(); + video->blank(); + audio->stop(); + video->reset(); + audio->unPause(); + demuxer->reset(); + audio->systemMuteOff(); + state = S_STOP; + return; + } + case S_JUMP: // to S_JUMP + { + state = S_PLAY; + audio->systemMuteOn(); + audio->unPause(); + restartAtFrame(jumpFrame); + return; + } + case S_JUMP_PI: // to S_JUMP_PI + { + audio->systemMuteOn(); + audio->unPause(); + threadStop(); + vfeed.stop(); + afeed.stop(); + tfeed.stop(); + subtitles->stop(); + demuxer->flush(); + state = S_PAUSE_I; + video->reset(); + video->play(); + restartAtFramePI(jumpFrame); + return; + } + } + } + case S_PAUSE_I: // from S_PAUSE_I ----------------------------------- + { + switch(toState) + { + case S_PLAY: // to S_PLAY + { + state = S_PLAY; + restartAtFrame(currentFrameNumber); + return; + } + case S_PAUSE_P: // to S_PAUSE_P + { + return; + } + case S_PAUSE_I: // to S_PAUSE_I + { + return; + } + case S_FFWD: // to S_FFWD + { + state = S_FFWD; + threadStart(); + return; + } + case S_FBWD: // to S_FBWD + { + state = S_FBWD; + threadStart(); + return; + } + case S_STOP: // to S_STOP + { + video->stop(); + video->blank(); + audio->stop(); + video->reset(); + demuxer->reset(); + audio->systemMuteOff(); + state = S_STOP; + return; + } + case S_JUMP: // to S_JUMP + { + state = S_PLAY; + restartAtFrame(jumpFrame); + return; + } + case S_JUMP_PI: // to S_JUMP_PI + { + restartAtFramePI(jumpFrame); + return; + } + } + } + case S_FFWD: // from S_FFWD ----------------------------------- + { + switch(toState) + { + case S_PLAY: // to S_PLAY + { + state = S_PLAY; + ULONG stepback = (ULONG)(((double)USER_RESPONSE_TIME * ifactor) * fps / 1000.); + if (stepback < currentFrameNumber) + currentFrameNumber -= stepback; + else + currentFrameNumber = 0; + restartAtFrame(currentFrameNumber); + return; + } + case S_PAUSE_P: // to S_PAUSE_P + { + // can't occur + return; + } + case S_PAUSE_I: // to S_PAUSE_I + { + threadStop(); + state = S_PAUSE_I; + return; + } + case S_FFWD: // to S_FFWD + { + return; + } + case S_FBWD: // to S_FBWD + { + threadStop(); + state = S_FBWD; + threadStart(); + return; + } + case S_STOP: // to S_STOP + { + threadStop(); + video->stop(); + video->blank(); + audio->stop(); + video->reset(); + demuxer->reset(); + state = S_STOP; + return; + } + case S_JUMP: // to S_JUMP + { + state = S_PLAY; + restartAtFrame(jumpFrame); + return; + } + case S_JUMP_PI: // to S_JUMP_PI + { + threadStop(); + state = S_PAUSE_I; + restartAtFramePI(jumpFrame); + return; + } + } + } + case S_FBWD: // from S_FBWD ----------------------------------- + { + switch(toState) + { + case S_PLAY: // to S_PLAY + { + state = S_PLAY; + restartAtFrame(currentFrameNumber); + return; + } + case S_PAUSE_P: // to S_PAUSE_P + { + // can't occur + return; + } + case S_PAUSE_I: // to S_PAUSE_I + { + threadStop(); + state = S_PAUSE_I; + return; + } + case S_FFWD: // to S_FFWD + { + threadStop(); + state = S_FFWD; + threadStart(); + return; + } + case S_FBWD: // to S_FBWD + { + return; + } + case S_STOP: // to S_STOP + { + threadStop(); + video->stop(); + video->blank(); + audio->stop(); + video->reset(); + demuxer->reset(); + state = S_STOP; + return; + } + case S_JUMP: // to S_JUMP + { + state = S_PLAY; + restartAtFrame(jumpFrame); + return; + } + case S_JUMP_PI: // to S_JUMP_PI + { + threadStop(); + state = S_PAUSE_I; + restartAtFramePI(jumpFrame); + return; + } + } + } + case S_STOP: // from S_STOP ----------------------------------- + { + switch(toState) + { + case S_PLAY: // to S_PLAY + { + startup = true; + + audio->reset(); + audio->setStreamType(Audio::MPEG2_PES); + audio->systemMuteOff(); + video->reset(); + demuxer->reset(); + // FIXME use restartAtFrame here? + if (currentFrameNumber > lengthFrames) currentFrameNumber = 0; + demuxer->setFrameNum(currentFrameNumber); + demuxer->seek(); + videoStartup = true; + state = S_PLAY; + threadStart(); + logger->log("Player", Log::DEBUG, "Immediate play"); + afeed.start(); + vfeed.start(); + tfeed.start(); + subtitles->start(); + video->sync(); + audio->sync(); + audio->play(); + video->pause(); + return; + } + case S_PAUSE_P: // to S_PAUSE_P + { + return; + } + case S_PAUSE_I: // to S_PAUSE_I + { + return; + } + case S_FFWD: // to S_FFWD + { + return; + } + case S_FBWD: // to S_FBWD + { + return; + } + case S_STOP: // to S_STOP + { + return; + } + case S_JUMP: // to S_JUMP + { + return; + } + case S_JUMP_PI: // to S_JUMP_PI + { + return; + } + } + } + // case S_JUMP cannot be a start state because it auto flips to play + // case S_JUMP_PI cannot be a start state because it auto flips to S_PAUSE_I + } +} + +// ----------------------------------- Internal functions + +void Player::lock() +{ +#ifndef WIN32 + pthread_mutex_lock(&mutex); + logger->log("Player", Log::DEBUG, "LOCKED"); + +#else + WaitForSingleObject(mutex, INFINITE); +#endif +} + +void Player::unLock() +{ +#ifndef WIN32 + logger->log("Player", Log::DEBUG, "UNLOCKING"); + pthread_mutex_unlock(&mutex); +#else + ReleaseMutex(mutex); +#endif +} + +void Player::restartAtFrame(ULONG newFrame) +{ + vfeed.stop(); + afeed.stop(); + tfeed.stop(); + subtitles->stop(); + threadStop(); + video->stop(); + video->reset(); + audio->reset(); + audio->setStreamType(Audio::MPEG2_PES); + demuxer->flush(); + demuxer->seek(); + currentFrameNumber = newFrame; + demuxer->setFrameNum(newFrame); + videoStartup = true; + afeed.start(); + tfeed.start(); + vfeed.start(); + subtitles->start(); + threadStart(); + audio->play(); + video->sync(); + audio->sync(); + audio->systemMuteOff(); + audio->doMuting(); +} + + +void Player::restartAtFramePI(ULONG newFrame) +{ + ULLONG filePos; + ULONG nextiframeNumber; + ULONG iframeLength; + ULONG iframeNumber; + + UCHAR* buffer; + UINT amountReceived; + UINT videoLength; + + // newFrame could be anywhere, go forwards to next I-Frame + if (!vdr->getNextIFrame(newFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return; + + // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame + vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength); + + buffer = vdr->getBlock(filePos, iframeLength, &amountReceived); + if (!vdr->isConnected()) + { + if (buffer) free(buffer); + doConnectionLost(); + } + else + { + videoLength = demuxer->stripAudio(buffer, amountReceived); + video->displayIFrame(buffer, videoLength); + video->displayIFrame(buffer, videoLength); // If you do it twice, it works :) + free(buffer); + currentFrameNumber = iframeNumber; + } +} + +void Player::doConnectionLost() +{ + logger->log("Player", Log::DEBUG, "Connection lost, sending message"); + Message* m = new Message(); + m->to = messageReceiver; + m->from = this; + m->message = Message::PLAYER_EVENT; + m->parameter = Player::CONNECTION_LOST; + messageQueue->postMessage(m); +} + +// ----------------------------------- Callback + +void Player::call(void* caller) +{ + if (caller == demuxer) + { + logger->log("Player", Log::DEBUG, "Callback from demuxer"); + + if (video->getTVsize() == Video::ASPECT4X3) + { + logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching"); + return; + } + + int dxCurrentAspect = demuxer->getAspectRatio(); + if (dxCurrentAspect == Demuxer::ASPECT_4_3) + { + logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV"); + video->setAspectRatio(Video::ASPECT4X3); + + Message* m = new Message(); + m->from = this; + m->to = messageReceiver; + m->message = Message::PLAYER_EVENT; + m->parameter = Player::ASPECT43; + messageQueue->postMessageFromOuterSpace(m); + } + else if (dxCurrentAspect == Demuxer::ASPECT_16_9) + { + logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV"); + video->setAspectRatio(Video::ASPECT16X9); + + Message* m = new Message(); + m->from = this; + m->to = messageReceiver; + m->message = Message::PLAYER_EVENT; + m->parameter = Player::ASPECT169; + messageQueue->postMessageFromOuterSpace(m); + } + else + { + logger->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring"); + } + + } + else + { + if (videoStartup) + { + videoStartup = false; + video->reset(); + video->play(); + video->sync(); + vfeed.release(); + unLock(); + } + + threadSignalNoLock(); + } +} + +// ----------------------------------- Feed thread + +void Player::threadMethod() +{ + // this method used to be simple, the only thing it does + // is farm out to threadFeed Live/Play/Scan + // All the guff is to support scan hitting one end + + if ((state == S_FFWD) || (state == S_FBWD)) + { + if (video->PTSIFramePlayback()) threadPTSFeedScan(); + else threadFeedScan(); + // if this returns then scan hit one end + if (state == S_FFWD) // scan hit the end. stop + { + threadCheckExit(); + Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex + m->to = messageReceiver; + m->from = this; + m->message = Message::PLAYER_EVENT; + m->parameter = STOP_PLAYBACK; + logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue); + messageQueue->postMessage(m); + logger->log("Player", Log::DEBUG, "Message posted..."); + return; + } + // if execution gets to here, threadFeedScan hit the start, go to play mode + state = S_PLAY; + audio->reset(); + audio->setStreamType(Audio::MPEG2_PES); + demuxer->flush(); + demuxer->seek(); + demuxer->setFrameNum(currentFrameNumber); + videoStartup = true; + afeed.start(); + tfeed.start(); + vfeed.start(); + subtitles->start(); + audio->play(); + audio->sync(); + audio->systemMuteOff(); + audio->doMuting(); + } + + if (state == S_PLAY) threadFeedPlay(); +} + +void Player::threadFeedPlay() +{ + ULLONG feedPosition; + UINT thisRead, writeLength, thisWrite, askFor; + time_t lastRescan = time(NULL); + + feedPosition = vdr->positionFromFrameNumber(currentFrameNumber); + if (!vdr->isConnected()) { doConnectionLost(); return; } + logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition); + + + while(1) + { + thisRead = 0; + writeLength = 0; + thisWrite = 0; + + threadCheckExit(); + + // If we havn't rescanned for a while.. + if ((lastRescan + 60) < time(NULL)) + { + lengthBytes = vdr->rescanRecording(&lengthFrames); + if (!vdr->isConnected()) { doConnectionLost(); return; } + logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes); + lastRescan = time(NULL); + } + + if (feedPosition >= lengthBytes) break; // finished playback + + if (startup) + { + if (startupBlockSize > lengthBytes) + askFor = lengthBytes; // is a very small recording! + else + askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams + } + else + { + if ((feedPosition + blockSize) > lengthBytes) // last block of recording + askFor = lengthBytes - feedPosition; + else // normal + askFor = blockSize; + } + //logger->log("Player", Log::DEBUG, "Get Block in"); + + threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead); + //logger->log("Player", Log::DEBUG, "Get Block out"); + + feedPosition += thisRead; + + if (!vdr->isConnected()) + { + doConnectionLost(); + return; + } + + if (!threadBuffer) break; + + if (startup) + { + int a_stream = demuxer->scan(threadBuffer, thisRead); + demuxer->setAudioStream(a_stream); + logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream); + startup = false; + } + + threadCheckExit(); + + while(writeLength < thisRead) + { + //logger->log("Player", Log::DEBUG, "Put in"); + thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength); + //logger->log("Player", Log::DEBUG, "Put out"); + writeLength += thisWrite; + + if (!thisWrite) + { + // demuxer is full and can't take anymore + threadLock(); + threadWaitForSignal(); + threadUnlock(); + } + + threadCheckExit(); + } + + free(threadBuffer); + threadBuffer = NULL; + + } + + // end of recording + logger->log("Player", Log::DEBUG, "Recording playback ends"); + + if (videoStartup) // oh woe. there never was a stream, I was conned! + { + videoStartup = false; + unLock(); + MILLISLEEP(500); // I think this will solve a race + } + + threadCheckExit(); + + + Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex + m->to = messageReceiver; + m->from = this; + m->message = Message::PLAYER_EVENT; + m->parameter = Player::STOP_PLAYBACK; + logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue); + messageQueue->postMessage(m); +} + + +void Player::threadPTSFeedScan() +{ + // This method manipulates the PTS instead of waiting, this is for the android devices, maybe later for windows? + + ULONG direction = 0; + int dir_fac=-1; + ULONG baseFrameNumber = 0; + ULONG iframeNumber = 0; + ULONG iframeLength = 0; + ULONG currentfeedFrameNumber=currentFrameNumber; + ULONG firstFrameNumber=currentFrameNumber; + ULLONG filePos; + UINT amountReceived; + UINT videoLength; + + UINT playtime=0; + +#ifndef WIN32 + struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info + struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data + struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame +#else + DWORD clock0 = 0, clock1 = 0, clock2 = 0; +#endif + + int frameTimeOffset = 0; // Time in msec between frames + int disp_msec = 0; // Time taken to display data + int total_msec = 0; // Time taken to fetch data and display it + int sleepTime = 0; + + if (state == S_FFWD) { + direction = 1; // and 0 for backward + dir_fac=1; + } + video->EnterIframePlayback(); + + while(1) + { + // Fetch I-frames until we get one that can be displayed in good time + // Repeat while clock0 + total_msec > clock2 + frameTimeOffset + + baseFrameNumber = currentfeedFrameNumber; + + threadCheckExit(); + if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength)) + return; + + if (iframeNumber >= lengthFrames) return; + // scan has got to the end of what we knew to be there before we started scanning + + baseFrameNumber = iframeNumber; + + frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentfeedFrameNumber) * 1000) / (fps * (double)ifactor)); + + logger->log("Player", Log::DEBUG, "XXX Got frame"); + + threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived); + + if (!vdr->isConnected()) + { + if (threadBuffer) free(threadBuffer); + doConnectionLost(); + break; + } + + + threadCheckExit(); + + + videoLength = demuxer->stripAudio(threadBuffer, amountReceived); + demuxer->changeTimes(threadBuffer,videoLength,playtime); + int count=0; + while (!video->displayIFrame(threadBuffer, videoLength)) // the device might block + { + MILLISLEEP(20); + threadCheckExit(); + count++; + if (count%300==0) { + ULLONG cur_time=video->getCurrentTimestamp(); + // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g", + // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps); + if (cur_time!=0) { + currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.)); + } + } + + } + playtime +=frameTimeOffset; + currentfeedFrameNumber = iframeNumber; + { + ULLONG cur_time=video->getCurrentTimestamp(); + // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g", + // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps); + if (cur_time!=0) { + currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.)); + } + } + + free(threadBuffer); + threadBuffer = NULL; + } +} + + + +void Player::threadFeedScan() +{ + // This method is actually really simple - get frame from vdr, + // spit it at the video chip, wait for a time. Most of the code here + // is to get the wait right so that the scan occurs at the correct rate. + + ULONG direction = 0; + ULONG baseFrameNumber = 0; + ULONG iframeNumber = 0; + ULONG iframeLength = 0; + ULLONG filePos; + UINT amountReceived; + UINT videoLength; + +#ifndef WIN32 + struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info + struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data + struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame +#else + DWORD clock0 = 0, clock1 = 0, clock2 = 0; +#endif + + int frameTimeOffset = 0; // Time in msec between frames + int disp_msec = 0; // Time taken to display data + int total_msec = 0; // Time taken to fetch data and display it + int sleepTime = 0; + + if (state == S_FFWD) direction = 1; // and 0 for backward + + while(1) + { + // Fetch I-frames until we get one that can be displayed in good time + // Repeat while clock0 + total_msec > clock2 + frameTimeOffset + + baseFrameNumber = currentFrameNumber; + do + { + threadCheckExit(); + if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength)) + return; + + if (iframeNumber >= lengthFrames) return; + // scan has got to the end of what we knew to be there before we started scanning + + baseFrameNumber = iframeNumber; + frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentFrameNumber) * 1000) / (fps * (double)ifactor)); +#ifndef WIN32 + gettimeofday(&clock0, NULL); +#else + clock0 = timeGetTime(); +#endif + } +#ifndef WIN32 + while (clock2.tv_sec != 0 && + (clock0.tv_sec - clock2.tv_sec) * 1000 + + (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec); +#else + while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset); +#endif + logger->log("Player", Log::DEBUG, "XXX Got frame"); + + threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived); + + if (!vdr->isConnected()) + { + if (threadBuffer) free(threadBuffer); + doConnectionLost(); + break; + } + +#ifndef WIN32 + gettimeofday(&clock1, NULL); + if (clock2.tv_sec != 0) + sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000 + + (clock2.tv_usec - clock1.tv_usec) / 1000 + + frameTimeOffset - disp_msec; +#else + clock1 = timeGetTime(); + if (clock2 != 0) + sleepTime = clock2 + frameTimeOffset - disp_msec - clock1; +#endif + if (sleepTime < 0) sleepTime = 0; + threadCheckExit(); + MILLISLEEP(sleepTime); + logger->log("Player", Log::DEBUG, "XXX Slept for %d", sleepTime); + + videoLength = demuxer->stripAudio(threadBuffer, amountReceived); + video->displayIFrame(threadBuffer, videoLength); + currentFrameNumber = iframeNumber; + free(threadBuffer); + threadBuffer = NULL; + +#ifndef WIN32 + gettimeofday(&clock2, NULL); + total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000 + + (clock2.tv_usec - clock0.tv_usec) / 1000 + - sleepTime; + disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000 + + (clock2.tv_usec - clock1.tv_usec) / 1000 + - sleepTime; +#else + clock2 = timeGetTime(); + total_msec = clock2 - clock0 - sleepTime; + disp_msec = clock2 - clock1 - sleepTime; +#endif + logger->log("Player", Log::DEBUG, "XXX disp_msec = %4d total_msec = %4d", disp_msec, total_msec); + } +} + +void Player::threadPostStopCleanup() +{ + if (threadBuffer) + { + free(threadBuffer); + threadBuffer = NULL; + } +} + +// ----------------------------------- Dev + +#ifdef DEV +void Player::test1() +{ + logger->log("Player", Log::DEBUG, "PLAYER TEST 1"); +} + +void Player::test2() +{ + logger->log("Player", Log::DEBUG, "PLAYER TEST 2"); +} +#endif diff --git a/player.h b/player.h index af9bedf..9658544 100644 --- a/player.h +++ b/player.h @@ -1,239 +1,237 @@ -/* - Copyright 2004-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 PLAYER_H -#define PLAYER_H - -#include -#include -#ifndef WIN32 -#include -#endif -#include - -#ifdef WIN32 -#include "threadwin.h" -#else -#include "threadp.h" -#endif - -#include "callback.h" -#include "defines.h" -#include "vfeed.h" -#include "afeed.h" -#include "tfeed.h" - -#include "teletextdecodervbiebu.h" - -class MessageQueue; -class Audio; -class Video; -class VDR; -class Log; -class Demuxer; -class OSDReceiver; -class DVBSubtitles; -class Channel; - -class Player : public Thread_TYPE, public Callback -{ - public: - Player(MessageQueue* messageQueue, void* messageReceiver, OSDReceiver* osdReceiver); - virtual ~Player(); - - int init(bool p_isPesRecording,double framespersec); - int shutdown(); - void setStartFrame(ULONG frameNum); - void setLengthBytes(ULLONG length); - void setLengthFrames(ULONG length); - void setAudioChannel(int newChannel, int type); - void setSubtitleChannel(int newChannel); - bool toggleSubtitles(); - void turnSubtitlesOn(bool ison); - bool isSubtitlesOn() { return subtitlesShowing; } - - void play(); - void stop(); - void pause(); - void fastForward(); - void fastBackward(); - void jumpToPercent(double percent); - void skipForward(int seconds); - void skipBackward(int seconds); - void jumpToMark(int mark); - void jumpToFrameP(int newFrame); - - UCHAR getState() { return state; } - ULONG getCurrentFrameNum(); - ULONG getLengthFrames(); - UCHAR getIScanRate() { return ifactor; } - bool* getDemuxerMpegAudioChannels(); - bool* getDemuxerAc3AudioChannels(); - bool* getDemuxerSubtitleChannels(); - int *getTeletxtSubtitlePages(); - int getCurrentAudioChannel(); - int getCurrentSubtitleChannel(); - bool isPesRecording() { return is_pesrecording; } - Channel getDemuxerChannel(); - - TeletextDecoderVBIEBU * getTeletextDecoder() { return teletext; } - - void call(void*); // for callback interface - - const static UCHAR S_PLAY = 1; - const static UCHAR S_PAUSE_P = 2; - const static UCHAR S_PAUSE_I = 3; - const static UCHAR S_FFWD = 4; - const static UCHAR S_FBWD = 5; - const static UCHAR S_STOP = 6; - const static UCHAR S_JUMP = 7; - const static UCHAR S_JUMP_PI = 8; // Jump to Pause_I mode - - // 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; - -#ifdef DEV - void test1(); - void test2(); -#endif - - protected: - void threadMethod(); - void threadPostStopCleanup(); - - private: - void switchState(UCHAR newState, ULONG jumpFrame=0); - - void threadFeedPlay(); - void threadFeedScan(); - - void doConnectionLost(); - void restartAtFrame(ULONG newFrame); - void restartAtFramePI(ULONG newFrame); - - bool subtitlesShowing; - MessageQueue* messageQueue; - void* messageReceiver; - OSDReceiver* osdReceiver; - Log* logger; - Audio* audio; - Video* video; - Demuxer* demuxer; - DVBSubtitles* subtitles; - VDR* vdr; - VFeed vfeed; - AFeed afeed; - TFeed tfeed; - TeletextDecoderVBIEBU *teletext; - - - - bool initted; - bool startup; - bool videoStartup; - - bool is_pesrecording; - double fps; - -#ifndef WIN32 - pthread_mutex_t mutex; -#else - HANDLE mutex; -#endif - void lock(); - void unLock(); - - ULLONG lengthBytes; - ULONG lengthFrames; - ULONG currentFrameNumber; - UINT blockSize; - UINT startupBlockSize; - UCHAR* threadBuffer; - UCHAR state; - UCHAR ifactor; -}; - -#endif - - -/* - -Possible states: - -Play, Pause, FFwd, FBwd, (Stop), [Jump] - - Possible Working - -Play -> PauseP * * - -> PauseI - -> FFwd * * - -> FBwd * * - -> Stop * * - -> Jump * * - -> Jump_PI * * - -PauseP -> Play * * - -> PauseI - -> FFwd * * - -> FBwd * * - -> Stop * * - -> Jump * * - -> Jump_PI * * - -PauseI -> Play * * - -> PauseP - -> FFwd * * - -> FBwd * * - -> Stop * * - -> Jump * * - -> Jump_PI * * - -FFwd -> Play * * - -> PauseP - -> PauseI * * - -> FBwd * * - -> Stop * * - -> Jump * * - -> Jump_PI * * - -FBwd -> Play * * - -> PauseP - -> PauseI * * - -> FFwd * * - -> Stop * * - -> Jump * * - -> Jump_PI * * - -Stop -> Play * * - -> PauseP - -> PauseI - -> FFwd - -> FBwd - -> Jump - -> Jump_PI - -*/ +/* + Copyright 2004-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 PLAYER_H +#define PLAYER_H + +#include +#include +#ifndef WIN32 +#include +#endif +#include + +#include "threadsystem.h" + +#include "callback.h" +#include "defines.h" +#include "vfeed.h" +#include "afeed.h" +#include "tfeed.h" + +#include "teletextdecodervbiebu.h" + +class MessageQueue; +class Audio; +class Video; +class VDR; +class Log; +class Demuxer; +class OSDReceiver; +class DVBSubtitles; +class Channel; + +class Player : public Thread_TYPE, public Callback +{ + public: + Player(MessageQueue* messageQueue, void* messageReceiver, OSDReceiver* osdReceiver); + virtual ~Player(); + + int init(bool p_isPesRecording,double framespersec); + int shutdown(); + void setStartFrame(ULONG frameNum); + void setLengthBytes(ULLONG length); + void setLengthFrames(ULONG length); + void setAudioChannel(int newChannel, int type); + void setSubtitleChannel(int newChannel); + bool toggleSubtitles(); + void turnSubtitlesOn(bool ison); + bool isSubtitlesOn() { return subtitlesShowing; } + + void play(); + void stop(); + void pause(); + void playpause(); + void fastForward(); + void fastBackward(); + void jumpToPercent(double percent); + void skipForward(int seconds); + void skipBackward(int seconds); + void jumpToMark(int mark); + void jumpToFrameP(int newFrame); + + UCHAR getState() { return state; } + ULONG getCurrentFrameNum(); + ULONG getLengthFrames(); + UCHAR getIScanRate() { return ifactor; } + bool* getDemuxerMpegAudioChannels(); + bool* getDemuxerAc3AudioChannels(); + bool* getDemuxerSubtitleChannels(); + int *getTeletxtSubtitlePages(); + int getCurrentAudioChannel(); + int getCurrentSubtitleChannel(); + bool isPesRecording() { return is_pesrecording; } + Channel getDemuxerChannel(); + + TeletextDecoderVBIEBU * getTeletextDecoder() { return teletext; } + + void call(void*); // for callback interface + + const static UCHAR S_PLAY = 1; + const static UCHAR S_PAUSE_P = 2; + const static UCHAR S_PAUSE_I = 3; + const static UCHAR S_FFWD = 4; + const static UCHAR S_FBWD = 5; + const static UCHAR S_STOP = 6; + const static UCHAR S_JUMP = 7; + const static UCHAR S_JUMP_PI = 8; // Jump to Pause_I mode + + // 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; + +#ifdef DEV + void test1(); + void test2(); +#endif + + protected: + void threadMethod(); + void threadPostStopCleanup(); + + private: + void switchState(UCHAR newState, ULONG jumpFrame=0); + + void threadFeedPlay(); + void threadFeedScan(); + void threadPTSFeedScan(); + + void doConnectionLost(); + void restartAtFrame(ULONG newFrame); + void restartAtFramePI(ULONG newFrame); + + bool subtitlesShowing; + MessageQueue* messageQueue; + void* messageReceiver; + OSDReceiver* osdReceiver; + Log* logger; + Audio* audio; + Video* video; + Demuxer* demuxer; + DVBSubtitles* subtitles; + VDR* vdr; + VFeed vfeed; + AFeed afeed; + TFeed tfeed; + TeletextDecoderVBIEBU *teletext; + + + + bool initted; + bool startup; + bool videoStartup; + + bool is_pesrecording; + double fps; + +#ifndef WIN32 + pthread_mutex_t mutex; +#else + HANDLE mutex; +#endif + void lock(); + void unLock(); + + ULLONG lengthBytes; + ULONG lengthFrames; + ULONG currentFrameNumber; + UINT blockSize; + UINT startupBlockSize; + UCHAR* threadBuffer; + UCHAR state; + UCHAR ifactor; +}; + +#endif + + +/* + +Possible states: + +Play, Pause, FFwd, FBwd, (Stop), [Jump] + + Possible Working + +Play -> PauseP * * + -> PauseI + -> FFwd * * + -> FBwd * * + -> Stop * * + -> Jump * * + -> Jump_PI * * + +PauseP -> Play * * + -> PauseI + -> FFwd * * + -> FBwd * * + -> Stop * * + -> Jump * * + -> Jump_PI * * + +PauseI -> Play * * + -> PauseP + -> FFwd * * + -> FBwd * * + -> Stop * * + -> Jump * * + -> Jump_PI * * + +FFwd -> Play * * + -> PauseP + -> PauseI * * + -> FBwd * * + -> Stop * * + -> Jump * * + -> Jump_PI * * + +FBwd -> Play * * + -> PauseP + -> PauseI * * + -> FFwd * * + -> Stop * * + -> Jump * * + -> Jump_PI * * + +Stop -> Play * * + -> PauseP + -> PauseI + -> FFwd + -> FBwd + -> Jump + -> Jump_PI + +*/ diff --git a/playerliveradio.cc b/playerliveradio.cc index 6b6f35f..90af2b5 100644 --- a/playerliveradio.cc +++ b/playerliveradio.cc @@ -1,475 +1,475 @@ -/* - 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" -#include "video.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; - Video::getInstance()->turnVideoOff(); -} - -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, NULL, 0, 200000,0)) - { - 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; - - - - return 1; -} - -bool* PlayerLiveRadio::getDemuxerMpegAudioChannels() -{ - return demuxer->getmpAudioChannels(); -} - -bool* PlayerLiveRadio::getDemuxerAc3AudioChannels() -{ - return demuxer->getac3AudioChannels(); -} - -int PlayerLiveRadio::getCurrentAudioChannel() -{ - return demuxer->getAID(); -} - -int *PlayerLiveRadio::getTeletxtSubtitlePages(){ - return NULL; -} - -int PlayerLiveRadio::getCurrentSubtitleChannel(){ - return demuxer->getSubID(); -} - -void PlayerLiveRadio::setAudioChannel(int newChannel, int type) -{ - return demuxer->setAID(newChannel, type); -} - -void PlayerLiveRadio::setSubtitleChannel(int newChannel) -{ - return demuxer->setSubID(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); - logger->log("PlayerLiveRadio", Log::DEBUG, "posted setChannel instruction, now %i in queue", instructions.size()); - 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(ULONG flag, void* data, ULONG len) -{ - // Flag: - // 0 = normal stream packet - // 1 = stream end - // 2 = connection lost - - if (flag == 1) - { - if (data) abort(); - - Message* m = new Message(); - m->from = this; - m->to = messageReceiver; - m->message = Message::PLAYER_EVENT; - m->parameter = PlayerLiveRadio::STREAM_END; - messageQueue->postMessageFromOuterSpace(m); - } - - 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->play(); - audio->pause(); - demuxer->reset(); - 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; - } - case S_PREBUFFERING: - { - vdr->stopStreaming(); - clearStreamChunks(); - afeed.stop(); - audio->stop(); - audio->reset(); - audio->play(); - audio->pause(); - demuxer->reset(); - 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_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(); - afeed.stop(); - audio->stop(); - audio->reset(); - audio->play(); - audio->pause(); - demuxer->reset(); - 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; - } - } - } - } -} - -bool PlayerLiveRadio::checkError() -{ - if (!vdr->isConnected()) - { - switchState(S_STOP); - - Message* m = new Message(); - m->from = this; - m->to = messageReceiver; - m->message = Message::PLAYER_EVENT; - m->parameter = PlayerLiveRadio::CONNECTION_LOST; - messageQueue->postMessageFromOuterSpace(m); - - return true; - } - return false; -} - -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) - { - logger->log("PlayerLiveRadio", Log::DEBUG, "Should optimise"); - optimizeInstructionQueue(); - } - - struct PLInstruction i = instructions.front(); - instructions.pop(); - - if (i.instruction == I_SETCHANNEL) - { - logger->log("PlayerLiveRadio", Log::DEBUG, "start new stream"); - - switchState(S_PREBUFFERING); - - if (!checkError()) - { - Channel* chan = (*chanList)[i.channelIndex]; - chan->loadPids(); - - if (chan->numAPids > 0) - { - demuxer->setAID(chan->apids[0].pid,0); - logger->log("PlayerLiveRadio", Log::DEBUG, "Demuxer pids: %u %u", chan->vpid, chan->apids[0].pid); - } - else - { - logger->log("PlayerLiveRadio", Log::WARN, "Demuxer no pids!"); - } - - int streamSuccess = vdr->streamChannel(chan->number, this); - if (!checkError() && !streamSuccess) - { - Message* m = new Message(); - m->from = this; - m->to = messageReceiver; - m->message = Message::PLAYER_EVENT; - m->parameter = PlayerLiveRadio::STREAM_END; - messageQueue->postMessageFromOuterSpace(m); - } - } - } - else if (i.instruction == I_STOP) - { - logger->log("PlayerLiveRadio", Log::DEBUG, "Stopping"); - switchState(S_STOP); - checkError(); - - stopNow = true; - break; - } - } - - if (stopNow) break; - - while(streamChunks.size()) - { - chunkToDemuxer(); - - if (state == S_PREBUFFERING) - { - ++preBufferCount; - ULONG percentDone = (ULONG)(preBufferCount / (float)preBufferAmount * 100); - logger->log("PlayerLiveRadio", Log::DEBUG, "Prebuffering %lu%%", percentDone); - - Message* m = new Message(); - m->from = this; - m->to = messageReceiver; - m->message = Message::PLAYER_EVENT; - m->parameter = PlayerLiveRadio::PREBUFFERING; - m->tag = percentDone; - messageQueue->postMessageFromOuterSpace(m); - - if (preBufferCount == preBufferAmount) - { - switchState(S_PLAY); - checkError(); - } - } - } - - threadLock(); - threadWaitForSignal(); // unlocks and waits for signal - threadUnlock(); - } - - logger->log("PlayerLiveRadio", Log::DEBUG, "End of thread"); -} - +/* + 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" +#include "video.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; + Video::getInstance()->turnVideoOff(); +} + +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, NULL, 0, 200000,0)) + { + 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; + + + + return 1; +} + +bool* PlayerLiveRadio::getDemuxerMpegAudioChannels() +{ + return demuxer->getmpAudioChannels(); +} + +bool* PlayerLiveRadio::getDemuxerAc3AudioChannels() +{ + return demuxer->getac3AudioChannels(); +} + +int PlayerLiveRadio::getCurrentAudioChannel() +{ + return demuxer->getAID(); +} + +int *PlayerLiveRadio::getTeletxtSubtitlePages(){ + return NULL; +} + +int PlayerLiveRadio::getCurrentSubtitleChannel(){ + return demuxer->getSubID(); +} + +void PlayerLiveRadio::setAudioChannel(int newChannel, int type) +{ + demuxer->setAID(newChannel, type); +} + +void PlayerLiveRadio::setSubtitleChannel(int newChannel) +{ + demuxer->setSubID(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); + logger->log("PlayerLiveRadio", Log::DEBUG, "posted setChannel instruction, now %i in queue", instructions.size()); + 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(ULONG flag, void* data, ULONG len) +{ + // Flag: + // 0 = normal stream packet + // 1 = stream end + // 2 = connection lost + + if (flag == 1) + { + if (data) abort(); + + Message* m = new Message(); + m->from = this; + m->to = messageReceiver; + m->message = Message::PLAYER_EVENT; + m->parameter = PlayerLiveRadio::STREAM_END; + messageQueue->postMessageFromOuterSpace(m); + } + + 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->play(); + audio->pause(); + demuxer->reset(); + 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; + } + case S_PREBUFFERING: + { + vdr->stopStreaming(); + clearStreamChunks(); + afeed.stop(); + audio->stop(); + audio->reset(); + audio->play(); + audio->pause(); + demuxer->reset(); + 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_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(); + afeed.stop(); + audio->stop(); + audio->reset(); + audio->play(); + audio->pause(); + demuxer->reset(); + 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; + } + } + } + } +} + +bool PlayerLiveRadio::checkError() +{ + if (!vdr->isConnected()) + { + switchState(S_STOP); + + Message* m = new Message(); + m->from = this; + m->to = messageReceiver; + m->message = Message::PLAYER_EVENT; + m->parameter = PlayerLiveRadio::CONNECTION_LOST; + messageQueue->postMessageFromOuterSpace(m); + + return true; + } + return false; +} + +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) + { + logger->log("PlayerLiveRadio", Log::DEBUG, "Should optimise"); + optimizeInstructionQueue(); + } + + struct PLInstruction i = instructions.front(); + instructions.pop(); + + if (i.instruction == I_SETCHANNEL) + { + logger->log("PlayerLiveRadio", Log::DEBUG, "start new stream"); + + switchState(S_PREBUFFERING); + + if (!checkError()) + { + Channel* chan = (*chanList)[i.channelIndex]; + chan->loadPids(); + + if (chan->numAPids > 0) + { + demuxer->setAID(chan->apids[0].pid,0); + logger->log("PlayerLiveRadio", Log::DEBUG, "Demuxer pids: %u %u", chan->vpid, chan->apids[0].pid); + } + else + { + logger->log("PlayerLiveRadio", Log::WARN, "Demuxer no pids!"); + } + + int streamSuccess = vdr->streamChannel(chan->number, this); + if (!checkError() && !streamSuccess) + { + Message* m = new Message(); + m->from = this; + m->to = messageReceiver; + m->message = Message::PLAYER_EVENT; + m->parameter = PlayerLiveRadio::STREAM_END; + messageQueue->postMessageFromOuterSpace(m); + } + } + } + else if (i.instruction == I_STOP) + { + logger->log("PlayerLiveRadio", Log::DEBUG, "Stopping"); + switchState(S_STOP); + checkError(); + + stopNow = true; + break; + } + } + + if (stopNow) break; + + while(streamChunks.size()) + { + chunkToDemuxer(); + + if (state == S_PREBUFFERING) + { + ++preBufferCount; + ULONG percentDone = (ULONG)(preBufferCount / (float)preBufferAmount * 100); + logger->log("PlayerLiveRadio", Log::DEBUG, "Prebuffering %lu%%", percentDone); + + Message* m = new Message(); + m->from = this; + m->to = messageReceiver; + m->message = Message::PLAYER_EVENT; + m->parameter = PlayerLiveRadio::PREBUFFERING; + m->tag = percentDone; + messageQueue->postMessageFromOuterSpace(m); + + if (preBufferCount == preBufferAmount) + { + switchState(S_PLAY); + checkError(); + } + } + } + + threadLock(); + threadWaitForSignal(); // unlocks and waits for signal + threadUnlock(); + } + + logger->log("PlayerLiveRadio", Log::DEBUG, "End of thread"); +} + diff --git a/playerlivetv.cc b/playerlivetv.cc index 20ff359..7113f8e 100644 --- a/playerlivetv.cc +++ b/playerlivetv.cc @@ -1,766 +1,785 @@ -/* - Copyright 2007 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 "playerlivetv.h" - -#include "log.h" -#include "audio.h" -#include "video.h" -#include "demuxerts.h" -#include "vdr.h" -#include "messagequeue.h" -#include "remote.h" -#include "message.h" -#include "channel.h" -#include "dvbsubtitles.h" -#include "osdreceiver.h" - -// ----------------------------------- Called from outside, one offs or info funcs - -PlayerLiveTV::PlayerLiveTV(MessageQueue* tmessageQueue, void* tmessageReceiver, OSDReceiver* tosdReceiver, ChannelList* tchanList) -: vfeed(this), afeed(this), tfeed(this) -{ - messageQueue = tmessageQueue; - messageReceiver = tmessageReceiver; - osdReceiver = tosdReceiver; - chanList = tchanList; - - audio = Audio::getInstance(); - video = Video::getInstance(); - logger = Log::getInstance(); - vdr = VDR::getInstance(); - initted = false; - - subtitlesShowing = false; - videoStartup = false; - pendingAudioPlay = false; - - stopNow = false; - state = S_STOP; - - video->turnVideoOn(); -} - -PlayerLiveTV::~PlayerLiveTV() -{ - if (initted) shutdown(); -} - -int PlayerLiveTV::init() -{ - if (initted) return 0; - - demuxer = new DemuxerTS(); - if (!demuxer) return 0; - subtitles = new DVBSubtitles(osdReceiver); - if (!subtitles) return 0; - - teletext = new TeletextDecoderVBIEBU(); - - unsigned int demux_video_size=2097152; - if (video->supportsh264()) demux_video_size*=5; - - if (!demuxer->init(this, audio, video, teletext, demux_video_size, 524288, 65536,25./*unimportant*/,subtitles)) - { - logger->log("PlayerLiveTV", Log::ERR, "Demuxer failed to init"); - shutdown(); - return 0; - } - - vfeed.init(); - afeed.init(); - tfeed.init(); - - video->stop(); - video->blank(); - audio->stop(); - - initted = true; - return 1; -} - -int PlayerLiveTV::shutdown() -{ - if (!initted) return 0; - stop(); - initted = false; - - delete demuxer; - delete subtitles; - delete teletext; - teletext = NULL; - return 1; -} - -bool* PlayerLiveTV::getDemuxerMpegAudioChannels() -{ - return demuxer->getmpAudioChannels(); -} - -bool* PlayerLiveTV::getDemuxerAc3AudioChannels() -{ - return demuxer->getac3AudioChannels(); -} - -int PlayerLiveTV::getCurrentAudioChannel() -{ - return demuxer->getAID(); -} - -void PlayerLiveTV::setAudioChannel(int newChannel, int type) -{ - return demuxer->setAID(newChannel,type); -} - -void PlayerLiveTV::setSubtitleChannel(int newChannel) -{ - return demuxer->setSubID(newChannel); -} - -int *PlayerLiveTV::getTeletxtSubtitlePages() -{ - return teletext->getSubtitlePages(); -} - -int PlayerLiveTV::getCurrentSubtitleChannel(){ - return demuxer->getSubID(); -} - -bool PlayerLiveTV::toggleSubtitles() -{ - if (!subtitlesShowing) - { - subtitlesShowing = true; - subtitles->show(); - } - else - { - subtitlesShowing = false; - subtitles->hide(); - } - return subtitlesShowing; -} - - -void PlayerLiveTV::turnSubtitlesOn(bool ison) { - if (ison) - { - subtitlesShowing = true; - subtitles->show(); - } - else - { - subtitlesShowing = false; - subtitles->hide(); - } - -} -// ----------------------------------- Externally called events - -void PlayerLiveTV::go(ULONG index) -{ - struct PLInstruction i; - i.instruction = I_SETCHANNEL; - i.channelIndex = index; - instructions.push(i); - threadStart(); -} - -void PlayerLiveTV::setChannel(ULONG index) -{ - logger->log("PlayerLiveTV", Log::DEBUG, "setChannel"); - struct PLInstruction i; - i.instruction = I_SETCHANNEL; - i.channelIndex = index; - instructions.push(i); - threadSignalNoLock(); -} - -void PlayerLiveTV::stop() -{ - logger->log("PlayerLiveTV", Log::DEBUG, "stop"); - struct PLInstruction i; - i.instruction = I_STOP; - instructions.push(i); - threadSignal(); - threadStop(); -} - -// ----------------------------------- Callback - -void PlayerLiveTV::call(void* caller) -{ - if (caller == demuxer) - { - logger->log("PlayerLiveTV", Log::DEBUG, "Callback from demuxer"); - - int dxCurrentAspect = demuxer->getAspectRatio(); - if (dxCurrentAspect == Demuxer::ASPECT_4_3) - { - if (video->getTVsize() == Video::ASPECT16X9) - { - logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV"); - video->setAspectRatio(Video::ASPECT4X3); - } - else - { - logger->log("PlayerLiveTV", Log::DEBUG, "TV is 4:3, ignoring aspect switching"); - } - - Message* m = new Message(); - m->from = this; - m->to = messageReceiver; - m->message = Message::PLAYER_EVENT; - m->parameter = PlayerLiveTV::ASPECT43; - messageQueue->postMessageFromOuterSpace(m); - } - else if (dxCurrentAspect == Demuxer::ASPECT_16_9) - { - if (video->getTVsize() == Video::ASPECT16X9) - { - logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV"); - video->setAspectRatio(Video::ASPECT16X9); - } - else - { - logger->log("PlayerLiveTV", Log::DEBUG, "TV is 4:3, ignoring aspect switching"); - } - - Message* m = new Message(); - m->from = this; - m->to = messageReceiver; - m->message = Message::PLAYER_EVENT; - m->parameter = PlayerLiveTV::ASPECT169; - messageQueue->postMessageFromOuterSpace(m); - } - else - { - logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is something else... ignoring"); - } - } - else if (caller == &afeed) - { - if (state == S_VIDEOSTARTUP) - { - logger->log("PlayerLiveTV", Log::DEBUG, "afeed video startup"); - videoStartup = true; - threadSignalNoLock(); - } - } -} - -// ----------------------------------- - -void PlayerLiveTV::streamReceive(ULONG flag, void* data, ULONG len) -{ - // Flag: - // 0 = normal stream packet - // 1 = stream end - // 2 = connection lost - -// logger->log("PlayerLiveTV", Log::DEBUG, "Received a streamchunk from VDR, flag = %lu", flag); - - if (flag == 1) - { - if (data) abort(); - - Message* m = new Message(); - m->from = this; - m->to = messageReceiver; - m->message = Message::PLAYER_EVENT; - m->parameter = PlayerLiveTV::STREAM_END; - messageQueue->postMessageFromOuterSpace(m); - } - - 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("PlayerLiveTV", Log::WARN, "Dropped chunk"); - } -} - -void PlayerLiveTV::clearStreamChunks() -{ - while(streamChunks.size()) - { - logger->log("PlayerLiveTV", Log::DEBUG, "Dropping chunk from old stream"); - struct StreamChunk s = streamChunks.front(); - streamChunks.pop(); - free(s.data); - } -} - -void PlayerLiveTV::chunkToDemuxer() -{ - StreamChunk s = streamChunks.front(); - streamChunks.pop(); - //logger->log("PlayerLiveTV", Log::DEBUG, "About to call demuxer with %p %lu", s.data, s.len); - /*int a = */demuxer->put((UCHAR*)s.data, s.len); - //logger->log("PlayerLiveTV", Log::DEBUG, "put %i to demuxer", a); - free(s.data); - if (pendingAudioPlay && demuxer->getHorizontalSize()) //Horizontal Size is zero, if not parsed - { - video->sync(); - video->play(); - video->pause(); - //audio->setStreamType(Audio::MPEG2_PES); - audio->sync(); - audio->play(); - audio->pause(); - pendingAudioPlay = false; - } -} - -void PlayerLiveTV::switchState(UCHAR newState) -{ - logger->log("PlayerLiveTV", Log::DEBUG, "Switch from state %u to state %u", state, newState); - - switch(state) - { - case S_STOP: // FROM S_STOP - { - switch(newState) - { - case S_VIDEOSTARTUP: - { - video->blank(); - video->reset(); - //video->sync(); - //video->play(); - //video->pause(); - - audio->stop(); - audio->unPause(); - audio->reset(); - //audio->setStreamType(Audio::MPEG2_PES); - //audio->sync(); - // I make this modification, since the video/audio devices needs to know at least - // which kind of video is embedded inside the stream - // therefore the demuxer needs to feeded at least with enough data - // to have one video header - // This is crucial, if we have mixed h264/mpeg2 channels - // the information from channels is not enough since some directshow decoders need - // width and height information before startup - pendingAudioPlay = true; - - //audio->play(); - //audio->pause(); - - demuxer->reset(); - demuxer->seek(); - - afeed.start(); - vfeed.start(); - subtitles->start(); - tfeed.start(); - - state = newState; - return; - } - default: - { - logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState); - abort(); - break; - } - } - } - - case S_VIDEOSTARTUP: // FROM S_VIDEOSTARTUP - { - switch(newState) - { - case S_PREBUFFERING: - { - pendingAudioPlay=false; - vfeed.release(); - state = newState; - return; - } - - case S_VIDEOSTARTUP: - { - vdr->stopStreaming(); - clearStreamChunks(); - vfeed.stop(); - afeed.stop(); - subtitles->stop(); - tfeed.stop(); - - video->blank(); - video->reset(); - //video->sync(); - //video->play(); - //video->pause(); - - audio->stop(); - audio->unPause(); - audio->reset(); - //audio->setStreamType(Audio::MPEG2_PES); - //audio->sync(); - pendingAudioPlay = true; - //audio->play(); - //audio->pause(); - - demuxer->reset(); - demuxer->seek(); - - afeed.start(); - vfeed.start(); - subtitles->start(); - tfeed.start(); - state = newState; - return; - } - case S_STOP: - { - vdr->stopStreaming(); - pendingAudioPlay=false; - clearStreamChunks(); - vfeed.stop(); - afeed.stop(); - subtitles->stop(); - tfeed.stop(); - video->stop(); - video->blank(); - audio->stop(); - audio->reset(); - video->reset(); - state = newState; - return; - } - default: - { - logger->log("PlayerLiveTV", 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: - { - pendingAudioPlay=false; - audio->unPause(); - video->unPause(); - state = newState; - return; - } - case S_VIDEOSTARTUP: - { - vdr->stopStreaming(); - clearStreamChunks(); - vfeed.stop(); - afeed.stop(); - subtitles->stop(); - tfeed.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(); - pendingAudioPlay = true; - //audio->play(); - //audio->pause(); - - demuxer->reset(); - demuxer->seek(); - - afeed.start(); - vfeed.start(); - subtitles->start(); - tfeed.start(); - - state = newState; - return; - } - case S_STOP: - { - pendingAudioPlay=false; - vdr->stopStreaming(); - clearStreamChunks(); - vfeed.stop(); - afeed.stop(); - subtitles->stop(); - tfeed.stop(); - video->stop(); - video->blank(); - audio->stop(); - audio->reset(); - video->reset(); - state = newState; - return; - } - default: - { - logger->log("PlayerLiveTV", 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: - { - pendingAudioPlay=false; - vdr->stopStreaming(); - clearStreamChunks(); - vfeed.stop(); - afeed.stop(); - subtitles->stop(); - tfeed.stop(); - video->stop(); - video->blank(); - audio->stop(); - audio->reset(); - video->reset(); - state = newState; - return; - } - case S_VIDEOSTARTUP: - { - vdr->stopStreaming(); - clearStreamChunks(); - vfeed.stop(); - afeed.stop(); - subtitles->stop(); - tfeed.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(); - pendingAudioPlay = true; - - demuxer->reset(); - demuxer->seek(); - - afeed.start(); - vfeed.start(); - subtitles->start(); - tfeed.start(); - - state = newState; - return; - } - default: - { - logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState); - abort(); - break; - } - } - } - } -} - -bool PlayerLiveTV::checkError() -{ - if (!vdr->isConnected()) - { - if (state != S_STOP) switchState(S_STOP); - - Message* m = new Message(); - m->from = this; - m->to = messageReceiver; - m->message = Message::PLAYER_EVENT; - m->parameter = PlayerLiveTV::CONNECTION_LOST; - messageQueue->postMessageFromOuterSpace(m); - - return true; - } - return false; -} - -void PlayerLiveTV::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 PlayerLiveTV::threadMethod() -{ - while(1) - { - if (videoStartup && !pendingAudioPlay) // we are in S_VIDEOSTARTUP, afeed has signalled that it has written some data - { - switchState(S_PREBUFFERING); - videoStartup = false; - preBufferCount = 0; - - checkError(); - } - - while(!instructions.empty()) - { - if (instructions.size() > 1) optimizeInstructionQueue(); - - struct PLInstruction i = instructions.front(); - instructions.pop(); - - if (i.instruction == I_SETCHANNEL) - { - logger->log("PlayerLiveTV", Log::DEBUG, "start new stream"); - - - switchState(S_VIDEOSTARTUP); - - if (!checkError()) - { - Channel* chan = (*chanList)[i.channelIndex]; - chan->loadPids(); - h264=chan->vstreamtype==0x1b; - demuxer->seth264(h264); - video->seth264mode(chan->vstreamtype==0x1b); - demuxer->setVID(chan->vpid); - video->seth264mode(chan->vstreamtype==0x1b); - - if (chan->numAPids > 0) - { - demuxer->setAID(chan->apids[0].pid,0); - audio->setStreamType(Audio::MPEG2_PES); - logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer pids: %u %u", chan->vpid, chan->apids[0].pid); - } - else - { - if (chan->numDPids > 0 && audio->maysupportAc3()) - { - demuxer->setAID(chan->dpids[0].pid,1); - logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer pids: %u %u (ac3)", chan->vpid, chan->dpids[0].pid); - } - else - { - logger->log("PlayerLiveTV", Log::WARN, "Demuxer video pid only: %u", chan->vpid); - } - } - if (chan->numSPids > 0) - demuxer->setSubID(chan->spids[0].pid); - demuxer->setTID(chan->tpid); - teletext->ResetDecoder(); - int streamSuccess = vdr->streamChannel(chan->number, this); - if (!checkError() && !streamSuccess) - { - Message* m = new Message(); - m->from = this; - m->to = messageReceiver; - m->message = Message::PLAYER_EVENT; - m->parameter = PlayerLiveTV::STREAM_END; - messageQueue->postMessageFromOuterSpace(m); - } - } - } - else if (i.instruction == I_STOP) - { - logger->log("PlayerLiveTV", Log::DEBUG, "Stopping"); - switchState(S_STOP); - checkError(); - - stopNow = true; - break; - } - } - - if (stopNow) break; - - while(streamChunks.size()) - { - chunkToDemuxer(); - - if (state == S_PREBUFFERING) - { - ++preBufferCount; - ULONG percentDone = (ULONG)(preBufferCount / (float)preBufferAmount * 100); - logger->log("PlayerLiveTV", Log::DEBUG, "Prebuffering %lu%%", percentDone); - - Message* m = new Message(); - m->from = this; - m->to = messageReceiver; - m->message = Message::PLAYER_EVENT; - m->parameter = PlayerLiveTV::PREBUFFERING; - m->tag = percentDone; - messageQueue->postMessageFromOuterSpace(m); - - if (preBufferCount == preBufferAmount) - { - switchState(S_PLAY); - checkError(); - } - } - } - - threadLock(); - threadWaitForSignal(); // unlocks and waits for signal - threadUnlock(); - } - - logger->log("PlayerLiveTV", Log::DEBUG, "End of thread"); -} - +/* + Copyright 2007 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 "playerlivetv.h" + +#include "log.h" +#include "audio.h" +#include "video.h" +#include "demuxerts.h" +#include "vdr.h" +#include "messagequeue.h" +#include "remote.h" +#include "message.h" +#include "channel.h" +#include "dvbsubtitles.h" +#include "osdreceiver.h" + +// ----------------------------------- Called from outside, one offs or info funcs + +PlayerLiveTV::PlayerLiveTV(MessageQueue* tmessageQueue, void* tmessageReceiver, OSDReceiver* tosdReceiver, ChannelList* tchanList) +: vfeed(this), afeed(this), tfeed(this) +{ + messageQueue = tmessageQueue; + messageReceiver = tmessageReceiver; + osdReceiver = tosdReceiver; + chanList = tchanList; + + audio = Audio::getInstance(); + video = Video::getInstance(); + logger = Log::getInstance(); + vdr = VDR::getInstance(); + initted = false; + + subtitlesShowing = false; + videoStartup = false; + pendingAudioPlay = false; + + stopNow = false; + state = S_STOP; + + video->turnVideoOn(); +} + +PlayerLiveTV::~PlayerLiveTV() +{ + if (initted) shutdown(); +} + +int PlayerLiveTV::init() +{ + if (initted) return 0; + + demuxer = new DemuxerTS(); + if (!demuxer) return 0; + subtitles = new DVBSubtitles(osdReceiver); + if (!subtitles) return 0; + + teletext = new TeletextDecoderVBIEBU(); + + unsigned int demux_video_size=2097152; + if (video->supportsh264()) demux_video_size*=5; + + int text_fak=video->getTeletextBufferFaktor(); + + + if (!demuxer->init(this, audio, video, teletext, demux_video_size,524288, 65536*text_fak,25./*unimportant*/,subtitles)) + { + logger->log("PlayerLiveTV", Log::ERR, "Demuxer failed to init"); + shutdown(); + return 0; + } + + vfeed.init(); + afeed.init(); + tfeed.init(); + + video->stop(); + video->blank(); + audio->stop(); + + initted = true; + return 1; +} + +int PlayerLiveTV::shutdown() +{ + if (!initted) return 0; + logger->log("PlayerLiveTV", Log::DEBUG, "Shutdown"); + stop(); + initted = false; + + delete demuxer; + delete subtitles; + delete teletext; + teletext = NULL; + return 1; +} + +bool* PlayerLiveTV::getDemuxerMpegAudioChannels() +{ + return demuxer->getmpAudioChannels(); +} + +bool* PlayerLiveTV::getDemuxerAc3AudioChannels() +{ + return demuxer->getac3AudioChannels(); +} + +int PlayerLiveTV::getCurrentAudioChannel() +{ + return demuxer->getAID(); +} + +void PlayerLiveTV::setAudioChannel(int newChannel, int type) +{ + demuxer->setAID(newChannel,type); +} + +void PlayerLiveTV::setSubtitleChannel(int newChannel) +{ + demuxer->setSubID(newChannel); +} + +int *PlayerLiveTV::getTeletxtSubtitlePages() +{ + return teletext->getSubtitlePages(); +} + +int PlayerLiveTV::getCurrentSubtitleChannel(){ + return demuxer->getSubID(); +} + +bool PlayerLiveTV::toggleSubtitles() +{ + if (!subtitlesShowing) + { + subtitlesShowing = true; + subtitles->show(); + } + else + { + subtitlesShowing = false; + subtitles->hide(); + } + return subtitlesShowing; +} + + +void PlayerLiveTV::turnSubtitlesOn(bool ison) { + if (ison) + { + subtitlesShowing = true; + subtitles->show(); + } + else + { + subtitlesShowing = false; + subtitles->hide(); + } + +} +// ----------------------------------- Externally called events + +void PlayerLiveTV::go(ULONG index) +{ + struct PLInstruction i; + i.instruction = I_SETCHANNEL; + i.channelIndex = index; + instructions.push(i); + threadStart(); +} + +void PlayerLiveTV::setChannel(ULONG index) +{ + logger->log("PlayerLiveTV", Log::DEBUG, "setChannel"); + struct PLInstruction i; + i.instruction = I_SETCHANNEL; + i.channelIndex = index; + instructions.push(i); + threadSignalNoLock(); +} + +void PlayerLiveTV::stop() +{ + logger->log("PlayerLiveTV", Log::DEBUG, "stop"); + struct PLInstruction i; + i.instruction = I_STOP; + instructions.push(i); + threadSignal(); + threadStop(); + logger->log("PlayerLiveTV", Log::DEBUG, "stop succesfull"); +} + +// ----------------------------------- Callback + +void PlayerLiveTV::call(void* caller) +{ + if (caller == demuxer) + { + logger->log("PlayerLiveTV", Log::DEBUG, "Callback from demuxer"); + + int dxCurrentAspect = demuxer->getAspectRatio(); + if (dxCurrentAspect == Demuxer::ASPECT_4_3) + { + if (video->getTVsize() == Video::ASPECT16X9) + { + logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV"); + video->setAspectRatio(Video::ASPECT4X3); + } + else + { + logger->log("PlayerLiveTV", Log::DEBUG, "TV is 4:3, ignoring aspect switching"); + } + + Message* m = new Message(); + m->from = this; + m->to = messageReceiver; + m->message = Message::PLAYER_EVENT; + m->parameter = PlayerLiveTV::ASPECT43; + messageQueue->postMessageFromOuterSpace(m); + } + else if (dxCurrentAspect == Demuxer::ASPECT_16_9) + { + if (video->getTVsize() == Video::ASPECT16X9) + { + logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV"); + video->setAspectRatio(Video::ASPECT16X9); + } + else + { + logger->log("PlayerLiveTV", Log::DEBUG, "TV is 4:3, ignoring aspect switching"); + } + + Message* m = new Message(); + m->from = this; + m->to = messageReceiver; + m->message = Message::PLAYER_EVENT; + m->parameter = PlayerLiveTV::ASPECT169; + messageQueue->postMessageFromOuterSpace(m); + } + else + { + logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is something else... ignoring"); + } + } + else if (caller == &afeed) + { + if (state == S_VIDEOSTARTUP) + { + logger->log("PlayerLiveTV", Log::DEBUG, "afeed video startup"); + videoStartup = true; + threadSignalNoLock(); + } + } +} + +// ----------------------------------- + +void PlayerLiveTV::streamReceive(ULONG flag, void* data, ULONG len) +{ + // Flag: + // 0 = normal stream packet + // 1 = stream end + // 2 = connection lost + + //logger->log("PlayerLiveTV", Log::DEBUG, "Received a streamchunk from VDR, flag = %lu", flag); + + if (flag == 1) + { + if (data) abort(); + + Message* m = new Message(); + m->from = this; + m->to = messageReceiver; + m->message = Message::PLAYER_EVENT; + m->parameter = PlayerLiveTV::STREAM_END; + messageQueue->postMessageFromOuterSpace(m); + } + + 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("PlayerLiveTV", Log::WARN, "Dropped chunk"); + } +} + +void PlayerLiveTV::clearStreamChunks() +{ + while(streamChunks.size()) + { + logger->log("PlayerLiveTV", Log::DEBUG, "Dropping chunk from old stream"); + struct StreamChunk s = streamChunks.front(); + streamChunks.pop(); + free(s.data); + } +} + +void PlayerLiveTV::chunkToDemuxer() +{ + StreamChunk s = streamChunks.front(); + streamChunks.pop(); +// logger->log("PlayerLiveTV", Log::DEBUG, "About to call demuxer with %p %lu", s.data, s.len); + /* int a =*/ demuxer->put((UCHAR*)s.data, s.len); +// logger->log("PlayerLiveTV", Log::DEBUG, "put %i to demuxer", a); + free(s.data); + if (pendingAudioPlay && (demuxer->getHorizontalSize()|| !video->independentAVStartUp())) //Horizontal Size is zero, if not parsed + { + video->sync(); + video->play(); + video->pause(); + //audio->setStreamType(Audio::MPEG2_PES); + audio->sync(); + audio->play(); + audio->pause(); + pendingAudioPlay = false; + } +} + +void PlayerLiveTV::switchState(UCHAR newState) +{ + logger->log("PlayerLiveTV", Log::DEBUG, "Switch from state %u to state %u", state, newState); + + switch(state) + { + case S_STOP: // FROM S_STOP + { + switch(newState) + { + case S_VIDEOSTARTUP: + { + video->blank(); + video->reset(); + //video->sync(); + //video->play(); + //video->pause(); + + audio->stop(); + audio->unPause(); + audio->reset(); + //audio->setStreamType(Audio::MPEG2_PES); + //audio->sync(); + // I make this modification, since the video/audio devices needs to know at least + // which kind of video is embedded inside the stream + // therefore the demuxer needs to feeded at least with enough data + // to have one video header + // This is crucial, if we have mixed h264/mpeg2 channels + // the information from channels is not enough since some directshow decoders need + // width and height information before startup + pendingAudioPlay = true; + + //audio->play(); + //audio->pause(); + + demuxer->reset(); + demuxer->seek(); + + afeed.start(); + vfeed.start(); + subtitles->start(); + tfeed.start(); + + state = newState; + if (!video->independentAVStartUp()){ + videoStartup = true; + threadSignalNoLock(); + } + return; + } + default: + { + logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState); + abort(); + break; + } + } + } + + case S_VIDEOSTARTUP: // FROM S_VIDEOSTARTUP + { + switch(newState) + { + case S_PREBUFFERING: + { + pendingAudioPlay=false; + vfeed.release(); + state = newState; + return; + } + + case S_VIDEOSTARTUP: + { + + vdr->stopStreaming(); + clearStreamChunks(); + vfeed.stop(); + afeed.stop(); + subtitles->stop(); + tfeed.stop(); + + video->blank(); + video->reset(); + //video->sync(); + //video->play(); + //video->pause(); + audio->stop(); + audio->unPause(); + audio->reset(); + //audio->setStreamType(Audio::MPEG2_PES); + //audio->sync(); + pendingAudioPlay = true; + //audio->play(); + //audio->pause(); + + demuxer->reset(); + demuxer->seek(); + + afeed.start(); + vfeed.start(); + subtitles->start(); + tfeed.start(); + state = newState; + if (!video->independentAVStartUp()){ + videoStartup = true; + threadSignalNoLock(); + } + return; + } + case S_STOP: + { + vdr->stopStreaming(); + pendingAudioPlay=false; + clearStreamChunks(); + vfeed.stop(); + afeed.stop(); + subtitles->stop(); + tfeed.stop(); + video->stop(); + video->blank(); + audio->stop(); + audio->reset(); + video->reset(); + state = newState; + return; + } + default: + { + logger->log("PlayerLiveTV", 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: + { + pendingAudioPlay=false; + audio->unPause(); + video->unPause(); + state = newState; + return; + } + case S_VIDEOSTARTUP: + { + vdr->stopStreaming(); + clearStreamChunks(); + vfeed.stop(); + afeed.stop(); + subtitles->stop(); + tfeed.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(); + pendingAudioPlay = true; + //audio->play(); + //audio->pause(); + + demuxer->reset(); + demuxer->seek(); + + afeed.start(); + vfeed.start(); + subtitles->start(); + tfeed.start(); + + state = newState; + return; + } + case S_STOP: + { + pendingAudioPlay=false; + vdr->stopStreaming(); + clearStreamChunks(); + vfeed.stop(); + afeed.stop(); + subtitles->stop(); + tfeed.stop(); + video->stop(); + video->blank(); + audio->stop(); + audio->reset(); + video->reset(); + state = newState; + return; + } + default: + { + logger->log("PlayerLiveTV", 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: + { + pendingAudioPlay=false; + vdr->stopStreaming(); + clearStreamChunks(); + vfeed.stop(); + afeed.stop(); + subtitles->stop(); + tfeed.stop(); + video->stop(); + video->blank(); + audio->stop(); + audio->reset(); + video->reset(); + state = newState; + return; + } + case S_VIDEOSTARTUP: + { + vdr->stopStreaming(); + clearStreamChunks(); + vfeed.stop(); + afeed.stop(); + subtitles->stop(); + tfeed.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(); + pendingAudioPlay = true; + demuxer->reset(); + demuxer->seek(); + + afeed.start(); + vfeed.start(); + subtitles->start(); + tfeed.start(); + state = newState; + if (!video->independentAVStartUp()){ + videoStartup = true; + threadSignalNoLock(); + } + return; + } + default: + { + logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState); + abort(); + break; + } + } + } + } +} + +bool PlayerLiveTV::checkError() +{ + if (!vdr->isConnected()) + { + if (state != S_STOP) switchState(S_STOP); + + Message* m = new Message(); + m->from = this; + m->to = messageReceiver; + m->message = Message::PLAYER_EVENT; + m->parameter = PlayerLiveTV::CONNECTION_LOST; + messageQueue->postMessageFromOuterSpace(m); + + return true; + } + return false; +} + +void PlayerLiveTV::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 PlayerLiveTV::threadMethod() +{ + while(1) + { + +// logger->log("PlayerLiveTV", Log::DEBUG, "VS: %d pA %d",videoStartup,pendingAudioPlay); + if (videoStartup && !pendingAudioPlay) // we are in S_VIDEOSTARTUP, afeed has signalled that it has written some data + { + logger->log("PlayerLiveTV", Log::DEBUG, "Enter prebuffering"); + switchState(S_PREBUFFERING); + videoStartup = false; + preBufferCount = 0; + + checkError(); + } + + while(!instructions.empty()) + { + if (instructions.size() > 1) optimizeInstructionQueue(); + + struct PLInstruction i = instructions.front(); + instructions.pop(); + + if (i.instruction == I_SETCHANNEL) + { + logger->log("PlayerLiveTV", Log::DEBUG, "start new stream"); + + + switchState(S_VIDEOSTARTUP); + + if (!checkError()) + { + Channel* chan = (*chanList)[i.channelIndex]; + chan->loadPids(); + h264=chan->vstreamtype==0x1b; + demuxer->seth264(h264); + video->seth264mode(chan->vstreamtype==0x1b); + demuxer->setVID(chan->vpid); + video->seth264mode(chan->vstreamtype==0x1b); + + if (chan->numAPids > 0) + { + demuxer->setAID(chan->apids[0].pid,0); + audio->setStreamType(Audio::MPEG2_PES); + logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer pids: %u %u", chan->vpid, chan->apids[0].pid); + } + else + { + if (chan->numDPids > 0 && audio->maysupportAc3()) + { + demuxer->setAID(chan->dpids[0].pid,1); + logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer pids: %u %u (ac3)", chan->vpid, chan->dpids[0].pid); + } + else + { + logger->log("PlayerLiveTV", Log::WARN, "Demuxer video pid only: %u", chan->vpid); + } + } + if (chan->numSPids > 0) + demuxer->setSubID(chan->spids[0].pid); + demuxer->setTID(chan->tpid); + teletext->ResetDecoder(); + int streamSuccess = vdr->streamChannel(chan->number, this); + if (!checkError() && !streamSuccess) + { + Message* m = new Message(); + m->from = this; + m->to = messageReceiver; + m->message = Message::PLAYER_EVENT; + m->parameter = PlayerLiveTV::STREAM_END; + messageQueue->postMessageFromOuterSpace(m); + } + } + } + else if (i.instruction == I_STOP) + { + logger->log("PlayerLiveTV", Log::DEBUG, "Stopping"); + switchState(S_STOP); + checkError(); + + stopNow = true; + break; + } + } + + threadCheckExit(); + + if (stopNow) break; + + while(streamChunks.size()) + { + chunkToDemuxer(); + + if (state == S_PREBUFFERING) + { + ++preBufferCount; + ULONG percentDone = (ULONG)(preBufferCount / (float)preBufferAmount * 100); + logger->log("PlayerLiveTV", Log::DEBUG, "Prebuffering %lu%%", percentDone); + + Message* m = new Message(); + m->from = this; + m->to = messageReceiver; + m->message = Message::PLAYER_EVENT; + m->parameter = PlayerLiveTV::PREBUFFERING; + m->tag = percentDone; + messageQueue->postMessageFromOuterSpace(m); + + if (preBufferCount == preBufferAmount) + { + switchState(S_PLAY); + checkError(); + } + } + } + threadLock(); + threadWaitForSignal(); // unlocks and waits for signal + threadUnlock(); + } + + logger->log("PlayerLiveTV", Log::DEBUG, "End of thread"); +} + diff --git a/playerradio.cc b/playerradio.cc index cdb10e3..1b8e8ba 100644 --- a/playerradio.cc +++ b/playerradio.cc @@ -1,581 +1,594 @@ -/* - Copyright 2004-2006 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 "playerradio.h" - -#include "log.h" -#include "audio.h" -#include "video.h" -#include "demuxervdr.h" -#include "demuxerts.h" -#include "remote.h" -#include "vdr.h" -#include "message.h" -#include "messagequeue.h" - -// ----------------------------------- Called from outside, one offs or info funcs - -PlayerRadio::PlayerRadio(MessageQueue* tmessageQueue, void* tmessageReceiver) -: afeed(this) -{ - messageQueue = tmessageQueue; - messageReceiver = tmessageReceiver; - audio = Audio::getInstance(); - logger = Log::getInstance(); - vdr = VDR::getInstance(); - initted = false; - lengthBytes = 0; - lengthPackets = 0; - streamPos = 0; - state = S_STOP; - - startPTS = 0; - lengthSeconds = 0; - - threadBuffer = NULL; - - blockSize = 10000; - startupBlockSize = 20000; - - Video::getInstance()->turnVideoOff(); -} - -PlayerRadio::~PlayerRadio() -{ - if (initted) shutdown(); -} - -int PlayerRadio::init(ULLONG tlengthBytes, ULONG tlengthPackets, bool isPesRecording) -{ - if (initted) return 0; -#ifndef WIN32 - pthread_mutex_init(&mutex, NULL); -#else - mutex=CreateMutex(NULL,FALSE,NULL); -#endif - - if (isPesRecording) - demuxer = new DemuxerVDR(); - else - demuxer = new DemuxerTS(); - if (!demuxer) return 0; - - if (!demuxer->init(this, audio, NULL, NULL, 0, 40000, 0)) - { - logger->log("PlayerRadio", Log::ERR, "Demuxer failed to init"); - shutdown(); - return 0; - } - - afeed.init(); - audio->stop(); - - lengthBytes = tlengthBytes; - lengthPackets = tlengthPackets; - - logger->log("PlayerRadio", Log::DEBUG, "PlayerRadio has received length bytes of %llu", lengthBytes); - - UINT thisRead = 0; - int success; - - UCHAR* buffer = vdr->getBlock(0, 10000, &thisRead); - if (!buffer) - { - logger->log("PlayerRadio", Log::ERR, "Failed to get start block"); - shutdown(); - if (!vdr->isConnected()) doConnectionLost(); - return 0; - } - - success = demuxer->findPTS(buffer, thisRead, &startPTS); - if (!success) - { - logger->log("PlayerRadio", Log::ERR, "Failed to get start PTS"); - free(buffer); - shutdown(); - return 0; - } - - free(buffer); - - if (!setLengthSeconds()) - { - logger->log("PlayerRadio", Log::ERR, "Failed to setLengthSeconds"); - shutdown(); - return 0; - } - - initted = true; - return 1; -} - -bool PlayerRadio::setLengthSeconds() -{ - int success; - ULLONG endPTS = 0; - UINT thisRead = 0; - UCHAR* buffer = vdr->getBlock(lengthBytes - 10000, 10000, &thisRead); - if (!buffer) - { - logger->log("PlayerRadio", Log::ERR, "Failed to get end block"); - if (!vdr->isConnected()) doConnectionLost(); - return false; - } - - success = demuxer->findPTS(buffer, thisRead, &endPTS); - free(buffer); - if (!success) - { - logger->log("PlayerRadio", Log::ERR, "Failed to get end PTS"); - return false; - } - - if (startPTS < endPTS) - { - lengthSeconds = (endPTS - startPTS) / 90000; - } - else - { - lengthSeconds = (startPTS - endPTS) / 90000; - } - - return true; -} - -int PlayerRadio::shutdown() -{ - if (!initted) return 0; - switchState(S_STOP); - initted = false; - - delete demuxer; - demuxer = NULL; - -#ifdef WIN32 - CloseHandle(mutex); -#endif - - return 1; -} - - -void PlayerRadio::setStartBytes(ULLONG startBytes) -{ - streamPos = startBytes; -} - -ULONG PlayerRadio::getLengthSeconds() -{ - return lengthSeconds; -} - -ULONG PlayerRadio::getCurrentSeconds() -{ - if (startup) return 0; - - long long currentPTS = demuxer->getAudioPTS(); - currentPTS -= startPTS; - if (currentPTS < 0) currentPTS += 8589934592ULL; - ULONG ret = currentPTS / 90000; - return ret; -} - -// ----------------------------------- Externally called events - -void PlayerRadio::play() -{ - if (!initted) return; - if (state == S_PLAY) return; - lock(); - switchState(S_PLAY); - unLock(); -} - -void PlayerRadio::stop() -{ - if (!initted) return; - if (state == S_STOP) return; - lock(); - logger->log("PlayerRadio", Log::DEBUG, "Stop called lock"); - switchState(S_STOP); - unLock(); -} - -void PlayerRadio::pause() -{ - if (!initted) return; - lock(); - - if (state == S_PAUSE_P) - { - switchState(S_PLAY); - } - else - { - switchState(S_PAUSE_P); - } - - unLock(); -} - -void PlayerRadio::jumpToPercent(double percent) -{ - lock(); - logger->log("PlayerRadio", Log::DEBUG, "JUMP TO %i%%", percent); - ULONG newPacket = (ULONG)(percent * lengthPackets / 100); - switchState(S_JUMP, newPacket); - unLock(); -} - -void PlayerRadio::skipForward(UINT seconds) -{ - lock(); - logger->log("PlayerRadio", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds); - ULONG currentSeconds = getCurrentSeconds(); - ULONG currentPacket = demuxer->getPacketNum(); - - if (currentSeconds == 0) { unLock(); return; } // div by zero - if (currentPacket == 0) { unLock(); return; } // Current pos from demuxer is not valid - - ULONG newPacket = currentPacket + (currentPacket * seconds / currentSeconds); - if (newPacket > lengthPackets) { switchState(S_PLAY); unLock(); } - else switchState(S_JUMP, newPacket); - unLock(); -} - -void PlayerRadio::skipBackward(UINT seconds) -{ - lock(); - logger->log("PlayerRadio", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds); - - ULONG currentSeconds = getCurrentSeconds(); - ULONG currentPacket = demuxer->getPacketNum(); - - if (currentSeconds == 0) { unLock(); return; } // div by zero - if (currentPacket == 0) { unLock(); return; } // Current pos from demuxer is not valid - - ULONG newPacket; - if ((UINT)seconds > currentSeconds) - newPacket = 0; - else - newPacket = currentPacket - (currentPacket * seconds / currentSeconds); - - switchState(S_JUMP, newPacket); - unLock(); -} - -// ----------------------------------- Implementations called events - -void PlayerRadio::switchState(UCHAR toState, ULONG jumpPacket) -{ - if (!initted) return; - - logger->log("PlayerRadio", Log::DEBUG, "Switch state from %u to %u", state, toState); - - switch(state) // current state selector - { - case S_PLAY: // from S_PLAY ----------------------------------- - { - switch(toState) - { - case S_PLAY: // to S_PLAY - { - return; - } - case S_PAUSE_P: // to S_PAUSE_P - { - audio->pause(); - state = S_PAUSE_P; - return; - } - case S_STOP: // to S_STOP - { - afeed.stop(); - threadStop(); - audio->stop(); - audio->unPause(); - demuxer->reset(); - state = S_STOP; - return; - } - case S_JUMP: // to S_JUMP - { - restartAtPacket(jumpPacket); - return; - } - } - } - case S_PAUSE_P: // from S_PAUSE_P ----------------------------------- - { - switch(toState) - { - case S_PLAY: // to S_PLAY - { - audio->unPause(); - state = S_PLAY; - return; - } - case S_PAUSE_P: // to S_PAUSE_P - { - return; - } - case S_STOP: // to S_STOP - { - afeed.stop(); - threadStop(); - audio->stop(); - audio->unPause(); - demuxer->reset(); - audio->systemMuteOff(); - state = S_STOP; - return; - } - case S_JUMP: // to S_JUMP - { - state = S_PLAY; - audio->unPause(); - restartAtPacket(jumpPacket); - return; - } - } - } - case S_STOP: // from S_STOP ----------------------------------- - { - switch(toState) - { - case S_PLAY: // to S_PLAY - { - startup = true; - - audio->reset(); - audio->setStreamType(Audio::MPEG2_PES); - audio->systemMuteOff(); - demuxer->reset(); - - // FIXME use restartAtPacket here? - if (currentPacketNumber > lengthPackets) currentPacketNumber = 0; - demuxer->setPacketNum(currentPacketNumber); - state = S_PLAY; - threadStart(); - logger->log("PlayerRadio", Log::DEBUG, "Immediate play"); - afeed.start(); - audio->play(); - - return; - } - case S_PAUSE_P: // to S_PAUSE_P - { - return; - } - case S_STOP: // to S_STOP - { - return; - } - case S_JUMP: // to S_JUMP - { - return; - } - } - } - // case S_JUMP cannot be selected as a start state because it auto flips to play - } -} - -// ----------------------------------- Internal functions - -void PlayerRadio::lock() -{ -#ifndef WIN32 - pthread_mutex_lock(&mutex); - logger->log("PlayerRadio", Log::DEBUG, "LOCKED"); - -#else - WaitForSingleObject(mutex, INFINITE); -#endif -} - -void PlayerRadio::unLock() -{ -#ifndef WIN32 - logger->log("PlayerRadio", Log::DEBUG, "UNLOCKING"); - pthread_mutex_unlock(&mutex); -#else - ReleaseMutex(mutex); -#endif -} - -void PlayerRadio::restartAtPacket(ULONG newPacket) -{ - afeed.stop(); - threadStop(); - audio->reset(); - audio->setStreamType(Audio::MPEG2_PES); - demuxer->flush(); - currentPacketNumber = newPacket; - demuxer->setPacketNum(newPacket); - afeed.start(); - threadStart(); - audio->play(); - audio->systemMuteOff(); - audio->doMuting(); -} - -void PlayerRadio::doConnectionLost() -{ - logger->log("PlayerRadio", Log::DEBUG, "Connection lost, sending message"); - Message* m = new Message(); - m->to = messageReceiver; - m->from = this; - m->message = Message::PLAYER_EVENT; - m->parameter = PlayerRadio::CONNECTION_LOST; - messageQueue->postMessage(m); -} - -// ----------------------------------- Callback - -void PlayerRadio::call(void* caller) -{ - threadSignalNoLock(); -} - -// ----------------------------------- Feed thread - -void PlayerRadio::threadMethod() -{ - if (state == S_PLAY) threadFeedPlay(); -} - -void PlayerRadio::threadFeedPlay() -{ - ULLONG feedPosition; - UINT thisRead, writeLength, thisWrite, askFor; - time_t lastRescan = time(NULL); - - feedPosition = vdr->positionFromFrameNumber(currentPacketNumber); - if (!vdr->isConnected()) { doConnectionLost(); return; } - logger->log("PlayerRadio", Log::DEBUG, "startFeedPlay: wantedPacket %i goto %llu", currentPacketNumber, feedPosition); - - - while(1) - { - thisRead = 0; - writeLength = 0; - thisWrite = 0; - - threadCheckExit(); - - // If we havn't rescanned for a while.. - if ((lastRescan + 60) < time(NULL)) - { - lengthBytes = vdr->rescanRecording(&lengthPackets); - if (!vdr->isConnected()) { doConnectionLost(); return; } - logger->log("PlayerRadio", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes); - lastRescan = time(NULL); - - if (!setLengthSeconds()) - { - logger->log("PlayerRadio", Log::ERR, "Failed to setLengthSeconds in thread"); - return; - } - } - - if (feedPosition >= lengthBytes) break; // finished playback - - if (startup) - { - if (startupBlockSize > lengthBytes) - askFor = lengthBytes; // is a very small recording! - else - askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams - } - else - { - if ((feedPosition + blockSize) > lengthBytes) // last block of recording - askFor = lengthBytes - feedPosition; - else // normal - askFor = blockSize; - } - - threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead); - feedPosition += thisRead; - - if (!vdr->isConnected()) - { - doConnectionLost(); - return; - } - - if (!threadBuffer) break; - - if (startup) - { - int a_stream = demuxer->scan(threadBuffer, thisRead); - demuxer->setAudioStream(a_stream); - logger->log("PlayerRadio", Log::DEBUG, "Startup Audio stream chosen %x", a_stream); - startup = false; - } - - threadCheckExit(); - - while(writeLength < thisRead) - { - thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength); - writeLength += thisWrite; - - if (!thisWrite) - { - // demuxer is full and can't take anymore - threadLock(); - threadWaitForSignal(); - threadUnlock(); - } - - threadCheckExit(); - } - - free(threadBuffer); - threadBuffer = NULL; - - } - - // end of recording - logger->log("PlayerRadio", Log::DEBUG, "Recording playback ends"); - - threadCheckExit(); - - - Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex - m->to = messageReceiver; - m->from = this; - m->message = Message::PLAYER_EVENT; - m->parameter = PlayerRadio::STOP_PLAYBACK; - logger->log("PlayerRadio", Log::DEBUG, "Posting message to %p...", messageQueue); - messageQueue->postMessage(m); -} - -void PlayerRadio::threadPostStopCleanup() -{ - if (threadBuffer) - { - free(threadBuffer); - threadBuffer = NULL; - } -} - +/* + Copyright 2004-2006 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 "playerradio.h" + +#include "log.h" +#include "audio.h" +#include "video.h" +#include "demuxervdr.h" +#include "demuxerts.h" +#include "remote.h" +#include "vdr.h" +#include "message.h" +#include "messagequeue.h" + +// ----------------------------------- Called from outside, one offs or info funcs + +PlayerRadio::PlayerRadio(MessageQueue* tmessageQueue, void* tmessageReceiver) +: afeed(this) +{ + messageQueue = tmessageQueue; + messageReceiver = tmessageReceiver; + audio = Audio::getInstance(); + logger = Log::getInstance(); + vdr = VDR::getInstance(); + initted = false; + lengthBytes = 0; + lengthPackets = 0; + streamPos = 0; + state = S_STOP; + + startPTS = 0; + lengthSeconds = 0; + + threadBuffer = NULL; + + blockSize = 10000; + startupBlockSize = 20000; + + Video::getInstance()->turnVideoOff(); +} + +PlayerRadio::~PlayerRadio() +{ + if (initted) shutdown(); +} + +int PlayerRadio::init(ULLONG tlengthBytes, ULONG tlengthPackets, bool isPesRecording) +{ + if (initted) return 0; +#ifndef WIN32 + pthread_mutex_init(&mutex, NULL); +#else + mutex=CreateMutex(NULL,FALSE,NULL); +#endif + + if (isPesRecording) + demuxer = new DemuxerVDR(); + else + demuxer = new DemuxerTS(); + if (!demuxer) return 0; + + if (!demuxer->init(this, audio, NULL, NULL, 0, 40000, 0)) + { + logger->log("PlayerRadio", Log::ERR, "Demuxer failed to init"); + shutdown(); + return 0; + } + + afeed.init(); + audio->stop(); + + lengthBytes = tlengthBytes; + lengthPackets = tlengthPackets; + + logger->log("PlayerRadio", Log::DEBUG, "PlayerRadio has received length bytes of %llu", lengthBytes); + + UINT thisRead = 0; + int success; + + UCHAR* buffer = vdr->getBlock(0, 10000, &thisRead); + if (!buffer) + { + logger->log("PlayerRadio", Log::ERR, "Failed to get start block"); + shutdown(); + if (!vdr->isConnected()) doConnectionLost(); + return 0; + } + + success = demuxer->findPTS(buffer, thisRead, &startPTS); + if (!success) + { + logger->log("PlayerRadio", Log::ERR, "Failed to get start PTS"); + free(buffer); + shutdown(); + return 0; + } + + free(buffer); + + if (!setLengthSeconds()) + { + logger->log("PlayerRadio", Log::ERR, "Failed to setLengthSeconds"); + shutdown(); + return 0; + } + + initted = true; + return 1; +} + +bool PlayerRadio::setLengthSeconds() +{ + int success; + ULLONG endPTS = 0; + UINT thisRead = 0; + UCHAR* buffer = vdr->getBlock(lengthBytes - 10000, 10000, &thisRead); + if (!buffer) + { + logger->log("PlayerRadio", Log::ERR, "Failed to get end block"); + if (!vdr->isConnected()) doConnectionLost(); + return false; + } + + success = demuxer->findPTS(buffer, thisRead, &endPTS); + free(buffer); + if (!success) + { + logger->log("PlayerRadio", Log::ERR, "Failed to get end PTS"); + return false; + } + + if (startPTS < endPTS) + { + lengthSeconds = (endPTS - startPTS) / 90000; + } + else + { + lengthSeconds = (startPTS - endPTS) / 90000; + } + + return true; +} + +int PlayerRadio::shutdown() +{ + if (!initted) return 0; + switchState(S_STOP); + initted = false; + + delete demuxer; + demuxer = NULL; + +#ifdef WIN32 + CloseHandle(mutex); +#endif + + return 1; +} + + +void PlayerRadio::setStartBytes(ULLONG startBytes) +{ + streamPos = startBytes; +} + +ULONG PlayerRadio::getLengthSeconds() +{ + return lengthSeconds; +} + +ULONG PlayerRadio::getCurrentSeconds() +{ + if (startup) return 0; + + long long currentPTS = demuxer->getAudioPTS(); + currentPTS -= startPTS; + if (currentPTS < 0) currentPTS += 8589934592ULL; + ULONG ret = currentPTS / 90000; + return ret; +} + +// ----------------------------------- Externally called events + +void PlayerRadio::play() +{ + if (!initted) return; + if (state == S_PLAY) return; + lock(); + switchState(S_PLAY); + unLock(); +} + + +void PlayerRadio::playpause() +{ + if (!initted) return; + lock(); + if (state==S_PLAY) { + switchState(S_PAUSE_P); + } else { + switchState(S_PLAY); + } + unLock(); +} + +void PlayerRadio::stop() +{ + if (!initted) return; + if (state == S_STOP) return; + lock(); + logger->log("PlayerRadio", Log::DEBUG, "Stop called lock"); + switchState(S_STOP); + unLock(); +} + +void PlayerRadio::pause() +{ + if (!initted) return; + lock(); + + if (state == S_PAUSE_P) + { + switchState(S_PLAY); + } + else + { + switchState(S_PAUSE_P); + } + + unLock(); +} + +void PlayerRadio::jumpToPercent(double percent) +{ + lock(); + logger->log("PlayerRadio", Log::DEBUG, "JUMP TO %i%%", percent); + ULONG newPacket = (ULONG)(percent * lengthPackets / 100); + switchState(S_JUMP, newPacket); + unLock(); +} + +void PlayerRadio::skipForward(UINT seconds) +{ + lock(); + logger->log("PlayerRadio", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds); + ULONG currentSeconds = getCurrentSeconds(); + ULONG currentPacket = demuxer->getPacketNum(); + + if (currentSeconds == 0) { unLock(); return; } // div by zero + if (currentPacket == 0) { unLock(); return; } // Current pos from demuxer is not valid + + ULONG newPacket = currentPacket + (currentPacket * seconds / currentSeconds); + if (newPacket > lengthPackets) { switchState(S_PLAY); unLock(); } + else switchState(S_JUMP, newPacket); + unLock(); +} + +void PlayerRadio::skipBackward(UINT seconds) +{ + lock(); + logger->log("PlayerRadio", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds); + + ULONG currentSeconds = getCurrentSeconds(); + ULONG currentPacket = demuxer->getPacketNum(); + + if (currentSeconds == 0) { unLock(); return; } // div by zero + if (currentPacket == 0) { unLock(); return; } // Current pos from demuxer is not valid + + ULONG newPacket; + if ((UINT)seconds > currentSeconds) + newPacket = 0; + else + newPacket = currentPacket - (currentPacket * seconds / currentSeconds); + + switchState(S_JUMP, newPacket); + unLock(); +} + +// ----------------------------------- Implementations called events + +void PlayerRadio::switchState(UCHAR toState, ULONG jumpPacket) +{ + if (!initted) return; + + logger->log("PlayerRadio", Log::DEBUG, "Switch state from %u to %u", state, toState); + + switch(state) // current state selector + { + case S_PLAY: // from S_PLAY ----------------------------------- + { + switch(toState) + { + case S_PLAY: // to S_PLAY + { + return; + } + case S_PAUSE_P: // to S_PAUSE_P + { + audio->pause(); + state = S_PAUSE_P; + return; + } + case S_STOP: // to S_STOP + { + afeed.stop(); + threadStop(); + audio->stop(); + audio->unPause(); + demuxer->reset(); + state = S_STOP; + return; + } + case S_JUMP: // to S_JUMP + { + restartAtPacket(jumpPacket); + return; + } + } + } + case S_PAUSE_P: // from S_PAUSE_P ----------------------------------- + { + switch(toState) + { + case S_PLAY: // to S_PLAY + { + audio->unPause(); + state = S_PLAY; + return; + } + case S_PAUSE_P: // to S_PAUSE_P + { + return; + } + case S_STOP: // to S_STOP + { + afeed.stop(); + threadStop(); + audio->stop(); + audio->unPause(); + demuxer->reset(); + audio->systemMuteOff(); + state = S_STOP; + return; + } + case S_JUMP: // to S_JUMP + { + state = S_PLAY; + audio->unPause(); + restartAtPacket(jumpPacket); + return; + } + } + } + case S_STOP: // from S_STOP ----------------------------------- + { + switch(toState) + { + case S_PLAY: // to S_PLAY + { + startup = true; + + audio->reset(); + audio->setStreamType(Audio::MPEG2_PES); + audio->systemMuteOff(); + demuxer->reset(); + + // FIXME use restartAtPacket here? + if (currentPacketNumber > lengthPackets) currentPacketNumber = 0; + demuxer->setPacketNum(currentPacketNumber); + state = S_PLAY; + threadStart(); + logger->log("PlayerRadio", Log::DEBUG, "Immediate play"); + afeed.start(); + audio->play(); + + return; + } + case S_PAUSE_P: // to S_PAUSE_P + { + return; + } + case S_STOP: // to S_STOP + { + return; + } + case S_JUMP: // to S_JUMP + { + return; + } + } + } + // case S_JUMP cannot be selected as a start state because it auto flips to play + } +} + +// ----------------------------------- Internal functions + +void PlayerRadio::lock() +{ +#ifndef WIN32 + pthread_mutex_lock(&mutex); + logger->log("PlayerRadio", Log::DEBUG, "LOCKED"); + +#else + WaitForSingleObject(mutex, INFINITE); +#endif +} + +void PlayerRadio::unLock() +{ +#ifndef WIN32 + logger->log("PlayerRadio", Log::DEBUG, "UNLOCKING"); + pthread_mutex_unlock(&mutex); +#else + ReleaseMutex(mutex); +#endif +} + +void PlayerRadio::restartAtPacket(ULONG newPacket) +{ + afeed.stop(); + threadStop(); + audio->reset(); + audio->setStreamType(Audio::MPEG2_PES); + demuxer->flush(); + currentPacketNumber = newPacket; + demuxer->setPacketNum(newPacket); + afeed.start(); + threadStart(); + audio->play(); + audio->systemMuteOff(); + audio->doMuting(); +} + +void PlayerRadio::doConnectionLost() +{ + logger->log("PlayerRadio", Log::DEBUG, "Connection lost, sending message"); + Message* m = new Message(); + m->to = messageReceiver; + m->from = this; + m->message = Message::PLAYER_EVENT; + m->parameter = PlayerRadio::CONNECTION_LOST; + messageQueue->postMessage(m); +} + +// ----------------------------------- Callback + +void PlayerRadio::call(void* caller) +{ + threadSignalNoLock(); +} + +// ----------------------------------- Feed thread + +void PlayerRadio::threadMethod() +{ + if (state == S_PLAY) threadFeedPlay(); +} + +void PlayerRadio::threadFeedPlay() +{ + ULLONG feedPosition; + UINT thisRead, writeLength, thisWrite, askFor; + time_t lastRescan = time(NULL); + + feedPosition = vdr->positionFromFrameNumber(currentPacketNumber); + if (!vdr->isConnected()) { doConnectionLost(); return; } + logger->log("PlayerRadio", Log::DEBUG, "startFeedPlay: wantedPacket %i goto %llu", currentPacketNumber, feedPosition); + + + while(1) + { + thisRead = 0; + writeLength = 0; + thisWrite = 0; + + threadCheckExit(); + + // If we havn't rescanned for a while.. + if ((lastRescan + 60) < time(NULL)) + { + lengthBytes = vdr->rescanRecording(&lengthPackets); + if (!vdr->isConnected()) { doConnectionLost(); return; } + logger->log("PlayerRadio", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes); + lastRescan = time(NULL); + + if (!setLengthSeconds()) + { + logger->log("PlayerRadio", Log::ERR, "Failed to setLengthSeconds in thread"); + return; + } + } + + if (feedPosition >= lengthBytes) break; // finished playback + + if (startup) + { + if (startupBlockSize > lengthBytes) + askFor = lengthBytes; // is a very small recording! + else + askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams + } + else + { + if ((feedPosition + blockSize) > lengthBytes) // last block of recording + askFor = lengthBytes - feedPosition; + else // normal + askFor = blockSize; + } + + threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead); + feedPosition += thisRead; + + if (!vdr->isConnected()) + { + doConnectionLost(); + return; + } + + if (!threadBuffer) break; + + if (startup) + { + int a_stream = demuxer->scan(threadBuffer, thisRead); + demuxer->setAudioStream(a_stream); + logger->log("PlayerRadio", Log::DEBUG, "Startup Audio stream chosen %x", a_stream); + startup = false; + } + + threadCheckExit(); + + while(writeLength < thisRead) + { + thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength); + writeLength += thisWrite; + + if (!thisWrite) + { + // demuxer is full and can't take anymore + threadLock(); + threadWaitForSignal(); + threadUnlock(); + } + + threadCheckExit(); + } + + free(threadBuffer); + threadBuffer = NULL; + + } + + // end of recording + logger->log("PlayerRadio", Log::DEBUG, "Recording playback ends"); + + threadCheckExit(); + + + Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex + m->to = messageReceiver; + m->from = this; + m->message = Message::PLAYER_EVENT; + m->parameter = PlayerRadio::STOP_PLAYBACK; + logger->log("PlayerRadio", Log::DEBUG, "Posting message to %p...", messageQueue); + messageQueue->postMessage(m); +} + +void PlayerRadio::threadPostStopCleanup() +{ + if (threadBuffer) + { + free(threadBuffer); + threadBuffer = NULL; + } +} + diff --git a/playerradio.h b/playerradio.h index dbfe1bb..a8fed68 100644 --- a/playerradio.h +++ b/playerradio.h @@ -1,159 +1,157 @@ -/* - Copyright 2004-2006 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 PLAYERRADIO_H -#define PLAYERRADIO_H - -#include -#include -#ifndef WIN32 -#include -#endif -#include - -#ifdef WIN32 -#include "threadwin.h" -#else -#include "threadp.h" -#endif -#include "callback.h" -#include "defines.h" -#include "afeed.h" - -class Log; -class Audio; -class Video; -class Demuxer; -class VDR; -class MessageQueue; - -class PlayerRadio : public Thread_TYPE, public Callback -{ - public: - PlayerRadio(MessageQueue* messageQueue, void* messageReceiver); - virtual ~PlayerRadio(); - - int init(ULLONG lengthBytes, ULONG lengthPackets, bool IsPesRecording); - int shutdown(); - void setStartBytes(ULLONG startBytes); - - void play(); - void stop(); - void pause(); - void jumpToPercent(double percent); - void skipForward(UINT seconds); - void skipBackward(UINT seconds); - - UCHAR getState() { return state; } - ULONG getCurrentSeconds(); - ULONG getLengthSeconds(); - - void call(void*); // for callback interface - - const static UCHAR S_PLAY = 1; - const static UCHAR S_PAUSE_P = 2; - const static UCHAR S_STOP = 6; - const static UCHAR S_JUMP = 7; - - // Player events - - const static UCHAR CONNECTION_LOST = 1; - const static UCHAR STOP_PLAYBACK = 2; - const static UCHAR STREAM_END = 3; - - protected: - void threadMethod(); - void threadPostStopCleanup(); - - private: - void switchState(UCHAR newState, ULONG jumpPacket=0); - - void threadFeedPlay(); - void threadFeedScan(); - - void doConnectionLost(); - void restartAtPacket(ULONG newPacket); - bool setLengthSeconds(); - - MessageQueue* messageQueue; - void* messageReceiver; - Log* logger; - Audio* audio; - Demuxer* demuxer; - VDR* vdr; - AFeed afeed; - - bool initted; - bool startup; - - ULLONG startPTS; - ULONG lengthSeconds; - -#ifndef WIN32 - pthread_mutex_t mutex; -#else - HANDLE mutex; -#endif - void lock(); - void unLock(); - - ULLONG lengthBytes; - ULLONG streamPos; - ULONG lengthPackets; - ULONG currentPacketNumber; - UINT blockSize; - UINT startupBlockSize; - UCHAR* threadBuffer; - UCHAR state; -}; - -#endif - - -/* - -Possible states: - -Play, Pause, FFwd, FBwd, (Stop), [Jump] - - Possible Working - -Play -> PauseP * * - -> Stop * * - -> Jump * * - -PauseP -> Play * * - -> Stop * * - -> Jump * * - -PauseI -> Play * * - -> PauseP - -> Stop * * - -> Jump * * - -Stop -> Play * * - -> PauseP - -> Jump - -Jump -> Play - -> PauseP - -> Stop - -*/ +/* + Copyright 2004-2006 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 PLAYERRADIO_H +#define PLAYERRADIO_H + +#include +#include +#ifndef WIN32 +#include +#endif +#include + +#include "threadsystem.h" + +#include "callback.h" +#include "defines.h" +#include "afeed.h" + +class Log; +class Audio; +class Video; +class Demuxer; +class VDR; +class MessageQueue; + +class PlayerRadio : public Thread_TYPE, public Callback +{ + public: + PlayerRadio(MessageQueue* messageQueue, void* messageReceiver); + virtual ~PlayerRadio(); + + int init(ULLONG lengthBytes, ULONG lengthPackets, bool IsPesRecording); + int shutdown(); + void setStartBytes(ULLONG startBytes); + + void play(); + void stop(); + void pause(); + void playpause(); + void jumpToPercent(double percent); + void skipForward(UINT seconds); + void skipBackward(UINT seconds); + + UCHAR getState() { return state; } + ULONG getCurrentSeconds(); + ULONG getLengthSeconds(); + + void call(void*); // for callback interface + + const static UCHAR S_PLAY = 1; + const static UCHAR S_PAUSE_P = 2; + const static UCHAR S_STOP = 6; + const static UCHAR S_JUMP = 7; + + // Player events + + const static UCHAR CONNECTION_LOST = 1; + const static UCHAR STOP_PLAYBACK = 2; + const static UCHAR STREAM_END = 3; + + protected: + void threadMethod(); + void threadPostStopCleanup(); + + private: + void switchState(UCHAR newState, ULONG jumpPacket=0); + + void threadFeedPlay(); + void threadFeedScan(); + + void doConnectionLost(); + void restartAtPacket(ULONG newPacket); + bool setLengthSeconds(); + + MessageQueue* messageQueue; + void* messageReceiver; + Log* logger; + Audio* audio; + Demuxer* demuxer; + VDR* vdr; + AFeed afeed; + + bool initted; + bool startup; + + ULLONG startPTS; + ULONG lengthSeconds; + +#ifndef WIN32 + pthread_mutex_t mutex; +#else + HANDLE mutex; +#endif + void lock(); + void unLock(); + + ULLONG lengthBytes; + ULLONG streamPos; + ULONG lengthPackets; + ULONG currentPacketNumber; + UINT blockSize; + UINT startupBlockSize; + UCHAR* threadBuffer; + UCHAR state; +}; + +#endif + + +/* + +Possible states: + +Play, Pause, FFwd, FBwd, (Stop), [Jump] + + Possible Working + +Play -> PauseP * * + -> Stop * * + -> Jump * * + +PauseP -> Play * * + -> Stop * * + -> Jump * * + +PauseI -> Play * * + -> PauseP + -> Stop * * + -> Jump * * + +Stop -> Play * * + -> PauseP + -> Jump + +Jump -> Play + -> PauseP + -> Stop + +*/ diff --git a/recman.h b/recman.h index 877d17a..8266129 100644 --- a/recman.h +++ b/recman.h @@ -27,6 +27,10 @@ #include #include #define PATH_MAX FILENAME_MAX +#else +// added for raspberry pi, should also work on mvp +#include + #endif #include "directory.h" diff --git a/remote.cc b/remote.cc index 2c83cee..67fff6d 100644 --- a/remote.cc +++ b/remote.cc @@ -27,6 +27,80 @@ Remote* Remote::instance = NULL; + +const ULONG Remote::NOLEARNMODE; +// Not buttons +const UCHAR Remote::NA_LEARN; +const UCHAR Remote::NA_NONE; +const UCHAR Remote::NA_UNKNOWN; +const UCHAR Remote::NA_SIGNAL; +const UCHAR Remote::DF_UP; +const UCHAR Remote::DF_DOWN; +const UCHAR Remote::DF_LEFT; +const UCHAR Remote::DF_RIGHT; + +// Problem common buttons +const UCHAR Remote::VOLUMEUP; +const UCHAR Remote::VOLUMEDOWN; +const UCHAR Remote::CHANNELUP; +const UCHAR Remote::CHANNELDOWN; + +// Common buttons +const UCHAR Remote::ZERO; +const UCHAR Remote::ONE; +const UCHAR Remote::TWO; +const UCHAR Remote::THREE; +const UCHAR Remote::FOUR; +const UCHAR Remote::FIVE; +const UCHAR Remote::SIX; +const UCHAR Remote::SEVEN; +const UCHAR Remote::EIGHT; +const UCHAR Remote::NINE; +const UCHAR Remote::POWER; +const UCHAR Remote::GO; +const UCHAR Remote::BACK; +const UCHAR Remote::MENU; +const UCHAR Remote::RED; +const UCHAR Remote::GREEN; +const UCHAR Remote::YELLOW; +const UCHAR Remote::BLUE; +const UCHAR Remote::MUTE; +const UCHAR Remote::RADIO; +const UCHAR Remote::REVERSE; +const UCHAR Remote::PLAY; +const UCHAR Remote::FORWARD; +const UCHAR Remote::RECORD; +const UCHAR Remote::STOP; +const UCHAR Remote::PAUSE; +const UCHAR Remote::SKIPBACK; +const UCHAR Remote::SKIPFORWARD; +const UCHAR Remote::OK; + +// Old remote only +const UCHAR Remote::FULL; + +// New remote only +const UCHAR Remote::TV; +const UCHAR Remote::VIDEOS; +const UCHAR Remote::MUSIC; +const UCHAR Remote::PICTURES; +const UCHAR Remote::GUIDE; +const UCHAR Remote::UP; +const UCHAR Remote::DOWN; +const UCHAR Remote::LEFT; +const UCHAR Remote::RIGHT; +const UCHAR Remote::PREVCHANNEL; +const UCHAR Remote::STAR; +const UCHAR Remote::HASH; + +// Android only +const UCHAR Remote::PLAYPAUSE; + + +// Remote types +const UCHAR Remote::OLDREMOTE; +const UCHAR Remote::NEWREMOTE; + Remote::Remote() { if (instance) return; @@ -189,8 +263,8 @@ char *Remote::SaveKeysConfig() RemoteTranslationList::const_iterator it; for (it = translist.begin(); it != translist.end(); it++) { - current+=sprintf(current,"H%08lXI%08lXK%02X", - (ULONG)it->first ,(ULONG) (it->first >> 32), it->second); + current+=snprintf(current,21,"H%08lXI%08lXK%02X", + (ULONG)it->first ,(ULONG) (it->first >> 32), it->second); } return output; } @@ -343,6 +417,8 @@ const char *Remote::CommandDesc(UCHAR number) return tr("Star"); case HASH: return tr("Hash"); + case PLAYPAUSE: + return tr("Play/Pause"); default: return NULL; diff --git a/remote.h b/remote.h index aca649a..e693b78 100644 --- a/remote.h +++ b/remote.h @@ -127,6 +127,10 @@ class Remote: public AbstractOption const static UCHAR STAR = 10; const static UCHAR HASH = 14; + // Android only + const static UCHAR PLAYPAUSE = 201; + + // Remote types const static UCHAR OLDREMOTE = 1; const static UCHAR NEWREMOTE = 2; diff --git a/stream.cc b/stream.cc index 0b1a090..eef823a 100644 --- a/stream.cc +++ b/stream.cc @@ -1,204 +1,204 @@ -/* - Copyright 2005-2006 Mark Calderbank - - 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 "stream.h" -#include "log.h" - -Stream::Stream() -{ - initted = 0; - draintarget = NULL; - cur_packet_pos = 0; -} - -Stream::~Stream() -{ - shutdown(); -} - -void Stream::shutdown() -{ - if (initted) - { - free(outbuf); -#ifdef WIN32 - CloseHandle(mutex); -#endif - - } - initted = 0; -} - -int Stream::init(DrainTarget* tdt, int bufsize) -{ - outbuf = (UCHAR*) malloc(bufsize); - if (!outbuf) return 0; - draintarget = tdt; - bufferSize = bufsize; - initted = 1; -#ifndef WIN32 - pthread_mutex_init(&mutex, NULL); -#else - mutex=CreateMutex(NULL,FALSE,NULL); -#endif - return 1; -} - -void Stream::flush() -{ - lock(); - - mediapackets.clear(); - unLock(); - if (draintarget) draintarget->ResetTimeOffsets(); -} - -int Stream::put(const UCHAR* inbuf, int len, UCHAR type,unsigned int index) -{ - int ret = 0; - if (!draintarget) return 0; - MediaPacket newPacket; - newPacket.length = len; - newPacket.pos_buffer = 0; - newPacket.type = type; - newPacket.pts=0; - newPacket.dts=0; - newPacket.synched=false; - newPacket.index=index; -#ifdef WIN32 - newPacket.disconti=false; - newPacket.presentation_time=0; -#endif - if (type!=MPTYPE_MPEG_AUDIO_LAYER3) {//no PES - //Extract the pts... - bool hasdts=false; - if ((inbuf[7] & 0x80) && len>14 ) { - newPacket.synched=true; - newPacket.pts=((ULLONG)(inbuf[9] & 0x0E) << 29 ) | - ( (ULLONG)(inbuf[10]) << 22 ) | - ( (ULLONG)(inbuf[11] & 0xFE) << 14 ) | - ( (ULLONG)(inbuf[12]) << 7 ) | - ( (ULLONG)(inbuf[13] & 0xFE) >> 1 ); - if ((inbuf[7] & 0x40) && len>19) { - newPacket.dts=((ULLONG)(inbuf[14] & 0x0E) << 29 ) | - ( (ULLONG)(inbuf[15]) << 22 ) | - ( (ULLONG)(inbuf[16] & 0xFE) << 14 ) | - ( (ULLONG)(inbuf[17]) << 7 ) | - ( (ULLONG)(inbuf[18] & 0xFE) >> 1 ); - hasdts=true; - } -#ifdef WIN32 - //ok we have the pts now convert it to a continously time code in 100ns units - if (hasdts && draintarget->dtsTimefix()) newPacket.presentation_time=(ULLONG)(newPacket.dts*10000LL/90LL); - else newPacket.presentation_time=(ULLONG)(newPacket.pts*10000LL/90LL); - - //newPacket.presentation_time-=draintarget->SetStartOffset((ULLONG)(newPacket.pts*10000LL/90LL),&newPacket.disconti); - newPacket.presentation_time-=draintarget->SetStartOffset((ULLONG)(newPacket.pts*10000LL/90LL),&newPacket.disconti); -#endif - } - } - - lock(); - int front, back; - if (mediapackets.empty()) - { - back = 0; front = bufferSize; - } - else - { - front = mediapackets.front().pos_buffer; - back = mediapackets.back().pos_buffer + mediapackets.back().length; - if (back == bufferSize) back = 0; - } - unLock(); - - if (back <= front) - { - // The free space (if any) is in one continuous chunk. - if (len <= front - back) ret = len; // Is there enough of it? - } - else if (len <= bufferSize - back) - { - // There is enough space at the end of the buffer - ret = len; - } - else if (len <= front) - { - // There is enough space at the start of the buffer - back = 0; - ret = len; - } - - if (ret) // Nonzero if we managed to find room for the packet - { - memcpy(outbuf + back, inbuf, len); - newPacket.pos_buffer = back; - lock(); - mediapackets.push_back(newPacket); - unLock(); - }// else { - // Log::getInstance()->log("Stream", Log::DEBUG, "We are full %d!",bufferSize); - //} - - return ret; -} - -bool Stream::drain() -{ - bool ret = false; - lock(); - UINT listlength = mediapackets.size(); - if (listlength != 0) - { - draintarget->PrepareMediaSample(mediapackets, cur_packet_pos); - unLock(); - UINT consumed = draintarget->DeliverMediaSample(outbuf, &cur_packet_pos); - lock(); - if (consumed != 0) ret = true; - if (consumed > listlength) consumed = listlength; - while (consumed--) - { - mediapackets.pop_front(); - } - } - unLock(); - return ret; -} - -void Stream::lock() -{ -#ifndef WIN32 - pthread_mutex_lock(&mutex); - //logger->log("Player", Log::DEBUG, "LOCKED"); - -#else - WaitForSingleObject(mutex, INFINITE ); -#endif -} - -void Stream::unLock() -{ -#ifndef WIN32 - //logger->log("Player", Log::DEBUG, "UNLOCKING"); - pthread_mutex_unlock(&mutex); -#else - ReleaseMutex(mutex); -#endif -} +/* + Copyright 2005-2006 Mark Calderbank + + 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 "stream.h" +#include "log.h" + +Stream::Stream() +{ + initted = 0; + draintarget = NULL; + cur_packet_pos = 0; +} + +Stream::~Stream() +{ + shutdown(); +} + +void Stream::shutdown() +{ + if (initted) + { + free(outbuf); +#ifdef WIN32 + CloseHandle(mutex); +#endif + + } + initted = 0; +} + +int Stream::init(DrainTarget* tdt, int bufsize) +{ + outbuf = (UCHAR*) malloc(bufsize); + if (!outbuf) return 0; + draintarget = tdt; + bufferSize = bufsize; + initted = 1; +#ifndef WIN32 + pthread_mutex_init(&mutex, NULL); +#else + mutex=CreateMutex(NULL,FALSE,NULL); +#endif + return 1; +} + +void Stream::flush() +{ + lock(); + + mediapackets.clear(); + unLock(); + if (draintarget) draintarget->ResetTimeOffsets(); +} + +int Stream::put(const UCHAR* inbuf, int len, UCHAR type,unsigned int index) +{ + int ret = 0; + if (!draintarget) return 0; + MediaPacket newPacket; + newPacket.length = len; + newPacket.pos_buffer = 0; + newPacket.type = type; + newPacket.pts=0; + newPacket.dts=0; + newPacket.synched=false; + newPacket.index=index; +#if defined(WIN32) || defined(__ANDROID__) + newPacket.disconti=false; + newPacket.presentation_time=0; +#endif + if (type!=MPTYPE_MPEG_AUDIO_LAYER3) {//no PES + //Extract the pts... + bool hasdts=false; + if ((inbuf[7] & 0x80) && len>14 ) { + newPacket.synched=true; + newPacket.pts=((ULLONG)(inbuf[9] & 0x0E) << 29 ) | + ( (ULLONG)(inbuf[10]) << 22 ) | + ( (ULLONG)(inbuf[11] & 0xFE) << 14 ) | + ( (ULLONG)(inbuf[12]) << 7 ) | + ( (ULLONG)(inbuf[13] & 0xFE) >> 1 ); + if ((inbuf[7] & 0x40) && len>19) { + newPacket.dts=((ULLONG)(inbuf[14] & 0x0E) << 29 ) | + ( (ULLONG)(inbuf[15]) << 22 ) | + ( (ULLONG)(inbuf[16] & 0xFE) << 14 ) | + ( (ULLONG)(inbuf[17]) << 7 ) | + ( (ULLONG)(inbuf[18] & 0xFE) >> 1 ); + hasdts=true; + } +#if defined(WIN32) || defined(__ANDROID__) + //ok we have the pts now convert it to a continously time code in 100ns units + if (hasdts && draintarget->dtsTimefix()) newPacket.presentation_time=(ULLONG)(newPacket.dts*10000LL/90LL); + else newPacket.presentation_time=(ULLONG)(newPacket.pts*10000LL/90LL); + + //newPacket.presentation_time-=draintarget->SetStartOffset((ULLONG)(newPacket.pts*10000LL/90LL),&newPacket.disconti); + newPacket.presentation_time-=draintarget->SetStartOffset((ULLONG)(newPacket.pts*10000LL/90LL),&newPacket.disconti); +#endif + } + } + + lock(); + int front, back; + if (mediapackets.empty()) + { + back = 0; front = bufferSize; + } + else + { + front = mediapackets.front().pos_buffer; + back = mediapackets.back().pos_buffer + mediapackets.back().length; + if (back == bufferSize) back = 0; + } + unLock(); + + if (back <= front) + { + // The free space (if any) is in one continuous chunk. + if (len <= front - back) ret = len; // Is there enough of it? + } + else if (len <= bufferSize - back) + { + // There is enough space at the end of the buffer + ret = len; + } + else if (len <= front) + { + // There is enough space at the start of the buffer + back = 0; + ret = len; + } + + if (ret) // Nonzero if we managed to find room for the packet + { + memcpy(outbuf + back, inbuf, len); + newPacket.pos_buffer = back; + lock(); + mediapackets.push_back(newPacket); + unLock(); + } else { + // Log::getInstance()->log("Stream", Log::DEBUG, "We are full %d!",bufferSize); + } + + return ret; +} + +bool Stream::drain() +{ + bool ret = false; + lock(); + UINT listlength = mediapackets.size(); + if (listlength != 0) + { + draintarget->PrepareMediaSample(mediapackets, cur_packet_pos); + unLock(); + UINT consumed = draintarget->DeliverMediaSample(outbuf, &cur_packet_pos); + lock(); + if (consumed != 0) ret = true; + if (consumed > listlength) consumed = listlength; + while (consumed--) + { + mediapackets.pop_front(); + } + } + unLock(); + return ret; +} + +void Stream::lock() +{ +#ifndef WIN32 + pthread_mutex_lock(&mutex); + //logger->log("Player", Log::DEBUG, "LOCKED"); + +#else + WaitForSingleObject(mutex, INFINITE ); +#endif +} + +void Stream::unLock() +{ +#ifndef WIN32 + //logger->log("Player", Log::DEBUG, "UNLOCKING"); + pthread_mutex_unlock(&mutex); +#else + ReleaseMutex(mutex); +#endif +} diff --git a/surface.cc b/surface.cc index a6cb01f..d8a98da 100644 --- a/surface.cc +++ b/surface.cc @@ -1,127 +1,294 @@ -/* - 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. -*/ - -#include "surface.h" - -#include "osd.h" -#include "log.h" - -Surface* Surface::screen = NULL; -osd_font_t* Surface::font = &font_helvB18; - -Surface::Surface(int id) -{ - if (id == SCREEN) screen = this; -} - -Surface::~Surface() -{ -} - -Surface* Surface::getScreen() -{ - return screen; -} - -int Surface::drawText(const char* text, int x, int y, ULONG rgba) -{ - return drawText(text, x, y, 2000, rgba); -} - -int Surface::drawText(const char* text, int x, int y, int width, ULONG rgba) -{ - int h, n, i; - int Y, X, cx; - - n = strlen(text); - h = font->height; - - X = 0; - cx = 0; - startFastDraw(); - for (i=0; icontent[font->offset[c]]; - int w = font->width[c]; - int pixels = 0; - - for (X=0; (X> (32 - X)) & 0x1) - { - drawPixel(x+X+cx, y+Y, rgba,true); - pixels++; - } - } - } - cx += w; - } - endFastDraw(); - return 1; -} - -int Surface::drawTextRJ(const char* text, int x, int y, ULONG rgba) -{ - int i, n, w; - w = 0; - - n = strlen(text); - - for (i = 0; i < n; i++) - { - w += font->width[(unsigned char)text[i]]; - } - - x -= w; - - if (x < 0) return 0; - else return drawText(text, x, y, rgba); -} - -int Surface::drawTextCentre(const char* text, int x, int y, ULONG rgba) -{ - int i, n, w; - w = 0; - - n = strlen(text); - - for (i = 0; i < n; i++) - { - w += font->width[(unsigned char)text[i]]; //Characters bigger then 128 can appear - } - - x -= w / 2; - - if (x < 0) return 0; - else return drawText(text, x, y, rgba); -} - -int Surface::getCharWidth(char c) -{ - return font->width[(unsigned char) c]; -} - -int Surface::getFontHeight() -{ - return font->spacing; -} +/* + 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. +*/ + +#include "surface.h" + +#include +#include "osd.h" +#include "log.h" +#include "video.h" + +#include "teletxt/txtfont.h" + +unsigned int interpol_table_fac1[16][22]; +unsigned int interpol_table_fac2[16][22]; +unsigned int interpol_table_fac3[16][22]; +unsigned int interpol_table_fac4[16][22]; +int interpol_lowbit[16]; +int interpol_upbit[16]; +int interpol_lowline[22]; +int interpol_upline[22]; +bool pol_table_inited=false; + +void initpol_tables(){ + int charsizex; + int charsizey; + charsizex=16; + if (Video::getInstance()->getFormat() == Video::PAL) + { + charsizey=22; + } else { + charsizey=18; + } + int ttcharsizex=12; + int ttcharsizey=10; + for (int py=0;pyheight; + + X = 0; + cx = 0; + startFastDraw(); + for (i=0; icontent[font->offset[c]]; + int w = font->width[c]; + int pixels = 0; + + for (X=0; (X> (32 - X)) & 0x1) + { + drawPixel(x+X+cx, y+Y, rgba,true); + pixels++; + } + } + } + cx += w; + } + endFastDraw(); + return 1; +} + +int Surface::drawTextRJ(const char* text, int x, int y, ULONG rgba) +{ + int i, n, w; + w = 0; + + n = strlen(text); + + for (i = 0; i < n; i++) + { + w += font->width[(unsigned char)text[i]]; + } + + x -= w; + + if (x < 0) return 0; + else return drawText(text, x, y, rgba); +} + +int Surface::drawTextCentre(const char* text, int x, int y, ULONG rgba) +{ + int i, n, w; + w = 0; + + n = strlen(text); + + for (i = 0; i < n; i++) + { + w += font->width[(unsigned char)text[i]]; //Characters bigger then 128 can appear + } + + x -= w / 2; + + if (x < 0) return 0; + else return drawText(text, x, y, rgba); +} + +int Surface::getCharWidth(char c) +{ + return font->width[(unsigned char) c]; +} + +int Surface::getFontHeight() +{ + return font->spacing; +} + +//Moved from Teletext view in order to allow device depend optimizations + +Colour Surface::enumTeletextColorToCoulour(enumTeletextColor ttcol) +{ + switch (ttcol) { + case ttcBlack: + return Colour(0,0,0); + case ttcRed: + return Colour(255,0,0); + case ttcGreen: + return Colour(0,255,0); + case ttcYellow: + return Colour(255,255,0); + case ttcBlue: + return Colour(0,0,255); + case ttcMagenta: + return Colour(255,0,255); + case ttcCyan: + return Colour(0,255,255); + case ttcWhite: + return Colour(255,255,255); + case ttcTransparent: + return Colour(0,0,0,0); + case ttcHalfRed: + return Colour(127,0,0); + case ttcHalfGreen: + return Colour(0,127,0); + case ttcHalfYellow: + return Colour(127,127,0); + case ttcHalfBlue: + return Colour(0,0,127); + case ttcHalfMagenta: + return Colour(127,0,127); + case ttcHalfCyan: + return Colour(0,127,127); + case ttcGrey: + return Colour(127,127,127); + default: + return Colour(0,0,0); + }; +} + + + +//Next function inspired by osdteletext plugin +void Surface::drawTTChar(int ox, int oy, int x, int y, cTeletextChar c) +{ + if (!pol_table_inited){ + initpol_tables(); + pol_table_inited=true; + } + unsigned int buffer [10]; + unsigned int * charmap=GetFontChar(c,buffer); + if (!charmap) { //invalid char + memset(&buffer,0,10); + charmap=buffer; + } + enumTeletextColor ttforegcolour=c.GetFGColor(); + enumTeletextColor ttbackgcolour=c.GetBGColor(); + if (c.GetBoxedOut()) { + ttforegcolour=ttcTransparent; + ttbackgcolour=ttcTransparent; + } + int charsizex; + int charsizey; + charsizex=16; + + if (Video::getInstance()->getFormat() == Video::PAL) + { + charsizey=22; + } else { + charsizey=18; + } + int ttcharsizex=12; + int ttcharsizey=10; + int screenposx=charsizex*x+ox; //12*40= 480 250 + int screenposy=y*charsizey+oy; + + + // Log::getInstance()->log("Surface", Log::ERR, "TTpos %d %d %d %d %d %d",x,y,ox,oy,screenposx,screenposy); + Colour fgcharcl=enumTeletextColorToCoulour(ttforegcolour); + Colour bgcharcl=enumTeletextColorToCoulour(ttbackgcolour); + + startFastDraw(); + for (int py=0;py>upperbit)) ? fgcharcl: bgcharcl; + Colour ulc=( upperbitline &(0x8000>>lowerbit)) ? fgcharcl: bgcharcl; + Colour luc=( lowerbitline &(0x8000>>upperbit)) ? fgcharcl: bgcharcl; + Colour llc=( lowerbitline &(0x8000>>lowerbit)) ? fgcharcl: bgcharcl; + unsigned int fac1,fac2,fac3,fac4; + fac1=interpol_table_fac1[px][py]; + fac2=interpol_table_fac2[px][py]; + fac3=interpol_table_fac3[px][py]; + fac4=interpol_table_fac4[px][py]; + + Colour res((uuc.red*fac1+ulc.red*fac2+luc.red*fac3+llc.red*fac4)/256, + (uuc.green*fac1+ulc.green*fac2+luc.green*fac3+llc.green*fac4)/256, + (uuc.blue*fac1+ulc.blue*fac2+luc.blue*fac3+llc.blue*fac4)/256, + (uuc.alpha*fac1+ulc.alpha*fac2+luc.alpha*fac3+llc.alpha*fac4)/256); //if this is too slow make a table + int c = ( (res.alpha << 24 ) + | (res.red << 16) + | (res.green << 8) + | (res.blue ) ); + drawPixel(screenposx+px,screenposy+py,c, true); + } + } + + + endFastDraw(); + + +} diff --git a/surface.h b/surface.h index ce412ac..cbf9924 100644 --- a/surface.h +++ b/surface.h @@ -1,87 +1,101 @@ -/* - 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 SURFACE_H -#define SURFACE_H - -#include -#include "defines.h" -#include "colour.h" - -// Font stuff - -typedef struct bogl_font { - char *name; /* Font name. */ - int height; /* Height in pixels. */ - int spacing; /* Vertical spacing in pixels. */ - unsigned long *content; /* 32-bit right-padded bitmap array. */ - short *offset; /* 256 offsets into content. */ - unsigned char *width; /* 256 character widths. */ -} osd_font_t; - -extern osd_font_t font_CaslonRoman_1_25; -extern osd_font_t font_helvB24; -extern osd_font_t font_helvB18; - -class Bitmap; - -class Surface -{ - public: - Surface(int id = 0); - virtual ~Surface(); - - static Surface* getScreen(); - static int getFontHeight(); - int getCharWidth(char c); - - int drawText(const char* text, int x, int y, ULONG rgba); - int drawText(const char* text, int x, int y, int width, ULONG rgba); - int drawTextRJ(const char* text, int x, int y, ULONG rgba); - int drawTextCentre(const char* text, int x, int y, ULONG rgba); - - virtual int create(UINT width, UINT height)=0; - virtual void display()=0; - - virtual int fillblt(int x, int y, int width, int height, unsigned int c)=0; - virtual void drawPixel(int x, int y, unsigned int c, bool fastdraw=false)=0; - virtual void drawPixel(int x, int y, Colour& c, bool fastdraw=false)=0; virtual void drawHorzLine(int x1, int x2, int y, unsigned int c)=0; - virtual void drawVertLine(int x, int y1, int y2, unsigned int c)=0; - virtual void drawBitmap(int x, int y, const Bitmap& bm)=0; - virtual int updateToScreen(int sx, int sy, int w, int h, int dx, int dy)=0; - virtual void readPixel(int x, int y, unsigned char* r, unsigned char* g, unsigned char* b)=0; - virtual void screenShot(char* fileName)=0; - - /* This is for system which need a locking of the drawing surface to speed up drawing */ - virtual void startFastDraw() {}; - virtual void endFastDraw() {}; - - - virtual int blt(int fd, unsigned long shandle, int sx, int sy, int width, int height, unsigned long dhandle, int dx, int dy)=0; - - const static int SCREEN = 1; - const static int BUFFER = 2; - - protected: - static Surface* screen; - static osd_font_t* font; -}; - -#endif +/* + 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 SURFACE_H +#define SURFACE_H + +#include +#include "defines.h" +#include "colour.h" + +#include "teletextdecodervbiebu.h" + +// Font stuff + +typedef struct bogl_font { + char *name; /* Font name. */ + int height; /* Height in pixels. */ + int spacing; /* Vertical spacing in pixels. */ + unsigned long *content; /* 32-bit right-padded bitmap array. */ + short *offset; /* 256 offsets into content. */ + unsigned char *width; /* 256 character widths. */ +} osd_font_t; + +//extern osd_font_t font_CaslonRoman_1_25; +//extern osd_font_t font_helvB24; +extern osd_font_t font_helvB18; + +class Bitmap; + + + +class Surface +{ + public: + Surface(int id = 0); + virtual ~Surface(); + + static Surface* getScreen(); + virtual int getFontHeight(); + virtual int getCharWidth(char c); + + virtual int drawText(const char* text, int x, int y, ULONG rgba); + virtual int drawText(const char* text, int x, int y, int width, ULONG rgba); + virtual int drawTextRJ(const char* text, int x, int y, ULONG rgba); + virtual int drawTextCentre(const char* text, int x, int y, ULONG rgba); + + virtual void drawJpeg(const char *fileName,int x, int y,int *width, int *height) {} + + virtual int create(UINT width, UINT height)=0; + virtual void display()=0; + + virtual int fillblt(int x, int y, int width, int height, unsigned int c)=0; + virtual void drawPixel(int x, int y, unsigned int c, bool fastdraw=false)=0; + virtual void drawPixel(int x, int y, Colour& c, bool fastdraw=false)=0; + virtual void drawHorzLine(int x1, int x2, int y, unsigned int c)=0; + virtual void drawVertLine(int x, int y1, int y2, unsigned int c)=0; + virtual void drawBitmap(int x, int y, const Bitmap& bm)=0; + virtual int updateToScreen(int sx, int sy, int w, int h, int dx, int dy)=0; + virtual void readPixel(int x, int y, unsigned char* r, unsigned char* g, unsigned char* b)=0; + virtual void screenShot(const char* fileName)=0; + + /* This is for system which need a locking of the drawing surface to speed up drawing */ + virtual void startFastDraw() {}; + virtual void endFastDraw() {}; + + + virtual void drawTTChar(int ox, int oy,int x, int y, cTeletextChar c); + + + + + // virtual int blt(int fd, unsigned long shandle, int sx, int sy, int width, int height, unsigned long dhandle, int dx, int dy)=0; + + const static int SCREEN = 1; + const static int BUFFER = 2; + + protected: + static Surface* screen; + static osd_font_t* font; + Colour enumTeletextColorToCoulour(enumTeletextColor ttcol); + +}; + +#endif diff --git a/surfacedirectfb.cc b/surfacedirectfb.cc index f89770d..baeeb91 100644 --- a/surfacedirectfb.cc +++ b/surfacedirectfb.cc @@ -1,304 +1,304 @@ -/* - Copyright 2004-2005 Chris Tallon, 2009 Marten Richter - Portions copyright 2008 Jon Gettler - - 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 "surfacedirectfb.h" - -#include "osd.h" -#include "bitmap.h" -#include "log.h" - -#include "osddirectfb.h" - - - -SurfaceDirectFB::SurfaceDirectFB(int id) -: Surface(id) -{ - surface=NULL; -} - -SurfaceDirectFB::~SurfaceDirectFB() -{ - if (surface) surface->Release(surface); - -} - -int SurfaceDirectFB::create(UINT width, UINT height) -{ - int counter=0; - while (!Osd::getInstance()->isInitted() && counter<2000) { - MILLISLEEP(50); //Wait for Grafiksystem initialization - counter++; - } - if (!Osd::getInstance()->isInitted()) return -1; - - IDirectFB*dfb=((OsdDirectFB*)Osd::getInstance())->getDfb(); -printf("ich bin doof");fflush(stdout); - if (screen == this) - { - IDirectFBDisplayLayer *osd_layer=((OsdDirectFB*)Osd::getInstance())->getOsdLayer(); - if (osd_layer->GetSurface(osd_layer,&surface)!=DFB_OK) - { - - return 0; - } - surface->Clear(surface,0x0,0x0,0x0,0xFF); - } else { - DFBSurfaceDescription dsc; - memset(&dsc,0,sizeof(dsc)); - *((int*)&dsc.flags)=DSDESC_CAPS | DSDESC_WIDTH | DSDESC_HEIGHT; - - dsc.width=width; - dsc.height=height; - dsc.caps= DSCAPS_NONE; - if (dfb->CreateSurface(dfb,&dsc,&surface)!=DFB_OK) - { - return 0; - } - } - return 1; -} - -void SurfaceDirectFB::display() -{ -// unsigned long fb_descriptor[2]; -/* - fb_descriptor[0] = surface.sfc.handle; - fb_descriptor[1] = 1; -*/ -// ioctl(fdOsd, GFX_FB_ATTACH, fb_descriptor); -} - - -// ---------------------------------------------------------------------------- - -// Now for the drawing functions - - -int SurfaceDirectFB::fillblt(int x, int y, int width, int height, unsigned int c) -{ - int sw,sh; - unsigned char r,g,b,a; - int nx,ny,nw,nh; - nx=x; - ny=y; - nw=width; - nh=height; - surface->GetSize(surface,&sw,&sh); - - if (nx >sw) nx=sw-1; - if (ny >sh) ny=sh-1; - - if ((nx+nw) >= sw) nw=sw-nx; - if ((ny+nh) >= sh) nh=sh-ny; - - if ((nx<0) || (ny < 0) || (nh <=0) || (nw <=0)) { - return 0; - } - - a= (c &0xff000000)>>24; - r= (c &0x00ff0000)>>16; - g= (c &0x0000ff00)>>8; - b= (c &0x000000ff); - - - surface->SetColor(surface,r,g,b,a); - surface->FillRectangle(surface,nx,ny,nw,nh); - return 0; -} - -void SurfaceDirectFB::drawPixel(int x, int y, unsigned int c, bool fastdraw) -{ - int sw,sh; - unsigned char r,g,b,a; - char *dst=NULL; - int offset=0; - int pitch; - surface->GetSize(surface,&sw,&sh); - if (x>=sw) return; - if (y>=sh) return; - - - - a= (c &0xff000000)>>24; - r= (c &0x00ff0000)>>16; - g= (c &0x0000ff00)>>8; - b= (c &0x000000ff); - - //TODO Fastdraw - if (surface->Lock(surface,DSLF_WRITE,(void**)(void*)&dst,&pitch) == DFB_OK) { - offset= y* pitch+x*4; - dst[offset++]=b; - dst[offset++]=g; - dst[offset++]=r; - dst[offset]=a; - surface->Unlock(surface); - } - -} - -void SurfaceDirectFB::drawPixel(int x, int y, Colour& c, bool fastdraw) -{ - int sw,sh; - char *dst=NULL; - int offset=0; - int pitch; - surface->GetSize(surface,&sw,&sh); - if (x>=sw) return; - if (y>=sh) return; - - //TODO Fastdraw - if (surface->Lock(surface,DSLF_WRITE,(void**)(void*)&dst,&pitch) == DFB_OK) { - offset= y* pitch+x*4; - dst[offset++]=c.blue; - dst[offset++]=c.green; - dst[offset++]=c.red; - dst[offset]=c.alpha; - surface->Unlock(surface); - } - -} - -void SurfaceDirectFB::drawHorzLine(int x1, int x2, int y, unsigned int c) -{ - fillblt(x1, y, x2-x1, 1, c); -} - -void SurfaceDirectFB::drawVertLine(int x, int y1, int y2, unsigned int c) -{ - fillblt(x, y1, 1, y2-y1, c); -} - -void SurfaceDirectFB::drawBitmap(int x, int y, const Bitmap& bm) -{/* - UINT bmw = bm.getWidth(); UINT bmh = bm.getHeight(); - if (bmw == 0 || bmh == 0) return; - if ((x >= (int)surface.sfc.width) || (y >= (int)surface.sfc.height)) return; - int remainder = (surface.sfc.width % 4); - UINT line; - if (remainder == 0) - line = surface.sfc.width; - else - line = surface.sfc.width + (4 - remainder); - const std::vector& bmdata = bm.rawData(); - const std::vector& Y = bm.palette.getYVector(); - const std::vector& Cr = bm.palette.getCrVector(); - const std::vector& Cb = bm.palette.getCbVector(); - const std::vector& A = bm.palette.getAVector(); - UINT b_offset = 0; - UINT s_offset = x + y*line; - UINT plotWidth = bmw; - UINT plotHeight = bmh; - if (x + plotWidth - 1 > surface.sfc.width) - plotWidth = surface.sfc.width - x + 1; - if (y + plotHeight - 1 > surface.sfc.height) - plotHeight = surface.sfc.height - y + 1; - for (UINT j = 0; j < plotHeight; ++j) - { - UINT i = 0; - if (x & 1) // odd x - need to plot first column separately - { - UCHAR index = bmdata[b_offset]; - *(surface.base[0] + s_offset) = Y[index]; - *(surface.base[1] + s_offset - 1) = Cb[index]; - *(surface.base[1] + s_offset) = Cr[index]; - *(surface.base[2] + s_offset) = A[index]; - i = 1; - } - // Now, plot pairs of pixels with averaged chroma values - while (i < plotWidth - 1) - { - UCHAR index1 = bmdata[b_offset + i]; - UCHAR index2 = bmdata[b_offset + i + 1]; - *(surface.base[0] + s_offset + i) = Y[index1]; - *(surface.base[0] + s_offset + i + 1) = Y[index2]; - *(surface.base[1] + s_offset + i) = (Cb[index1] + Cb[index2]) / 2; - *(surface.base[1] + s_offset + i + 1) = (Cr[index1] + Cr[index2]) / 2; - *(surface.base[2] + s_offset + i) = A[index1]; - *(surface.base[2] + s_offset + i + 1) = A[index2]; - i += 2; - } - if (i == plotWidth - 1) // One column left to do - { - UCHAR index = bmdata[b_offset + i]; - *(surface.base[0] + s_offset + i) = Y[index]; - *(surface.base[1] + s_offset + i) = Cb[index]; - *(surface.base[1] + s_offset + i + 1) = Cr[index]; - *(surface.base[2] + s_offset + i) = A[index]; - } - s_offset += line; - b_offset += bmw; - }*/ -} - - /* surface update to screen needs: - source x distance into this surface - source y distance into this surface - width of update - height of update - destination x on screen - destination y on screen - */ -int SurfaceDirectFB::updateToScreen(int sx, int sy, int w, int h, int dx, int dy) // FIXME new, replace others with this FIXME -{ - IDirectFBSurface* screensurf=((SurfaceDirectFB*)screen)->getSurfaceDFB(); - if (!screensurf) return 0; - if (this==screen) return 0; - - DFBRectangle rect; - int sw,sh; - - screensurf->GetSize(screensurf,&sw,&sh); -// screensurf->Clear(screensurf,0x0,0x0,0x0,0xFF); - rect.x = sx; - rect.y=sy; - rect.w=w; - rect.h=h; - - DFBRectangle drect; //TODO make osd HD - drect.x=dx*sw/720; - drect.y=dy*sh/576; - drect.w=w*sw/720; - drect.h=h*sh/576; - -// screensurf->Blit(screensurf,surface,&rect,dx,dy); - screensurf->StretchBlit(screensurf,surface,&rect,&drect); - - return 0;//blt(fdOsd, surface.sfc.handle, sx, sy, w, h, ((SurfaceDirectFB*)screen)->getSurfaceHandle(), dx, dy); -} - -int SurfaceDirectFB::blt(int fd, unsigned long shandle, int sx, int sy, int width, int height, unsigned long dhandle, int dx, int dy) -{//Skip it, noone uses this! - - return 0; -} - -void SurfaceDirectFB::screenShot(char* fileName) -{ - return; - -} - -void SurfaceDirectFB::readPixel(int x, int y, unsigned char* r, unsigned char* g, unsigned char* b) -{ - -} - +/* + Copyright 2004-2005 Chris Tallon, 2009 Marten Richter + Portions copyright 2008 Jon Gettler + + 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 "surfacedirectfb.h" + +#include "osd.h" +#include "bitmap.h" +#include "log.h" + +#include "osddirectfb.h" + + + +SurfaceDirectFB::SurfaceDirectFB(int id) +: Surface(id) +{ + surface=NULL; +} + +SurfaceDirectFB::~SurfaceDirectFB() +{ + if (surface) surface->Release(surface); + +} + +int SurfaceDirectFB::create(UINT width, UINT height) +{ + int counter=0; + while (!Osd::getInstance()->isInitted() && counter<2000) { + MILLISLEEP(50); //Wait for Grafiksystem initialization + counter++; + } + if (!Osd::getInstance()->isInitted()) return -1; + + IDirectFB*dfb=((OsdDirectFB*)Osd::getInstance())->getDfb(); +printf("ich bin doof");fflush(stdout); + if (screen == this) + { + IDirectFBDisplayLayer *osd_layer=((OsdDirectFB*)Osd::getInstance())->getOsdLayer(); + if (osd_layer->GetSurface(osd_layer,&surface)!=DFB_OK) + { + + return 0; + } + surface->Clear(surface,0x0,0x0,0x0,0xFF); + } else { + DFBSurfaceDescription dsc; + memset(&dsc,0,sizeof(dsc)); + *((int*)&dsc.flags)=DSDESC_CAPS | DSDESC_WIDTH | DSDESC_HEIGHT; + + dsc.width=width; + dsc.height=height; + dsc.caps= DSCAPS_NONE; + if (dfb->CreateSurface(dfb,&dsc,&surface)!=DFB_OK) + { + return 0; + } + } + return 1; +} + +void SurfaceDirectFB::display() +{ +// unsigned long fb_descriptor[2]; +/* + fb_descriptor[0] = surface.sfc.handle; + fb_descriptor[1] = 1; +*/ +// ioctl(fdOsd, GFX_FB_ATTACH, fb_descriptor); +} + + +// ---------------------------------------------------------------------------- + +// Now for the drawing functions + + +int SurfaceDirectFB::fillblt(int x, int y, int width, int height, unsigned int c) +{ + int sw,sh; + unsigned char r,g,b,a; + int nx,ny,nw,nh; + nx=x; + ny=y; + nw=width; + nh=height; + surface->GetSize(surface,&sw,&sh); + + if (nx >sw) nx=sw-1; + if (ny >sh) ny=sh-1; + + if ((nx+nw) >= sw) nw=sw-nx; + if ((ny+nh) >= sh) nh=sh-ny; + + if ((nx<0) || (ny < 0) || (nh <=0) || (nw <=0)) { + return 0; + } + + a= (c &0xff000000)>>24; + r= (c &0x00ff0000)>>16; + g= (c &0x0000ff00)>>8; + b= (c &0x000000ff); + + + surface->SetColor(surface,r,g,b,a); + surface->FillRectangle(surface,nx,ny,nw,nh); + return 0; +} + +void SurfaceDirectFB::drawPixel(int x, int y, unsigned int c, bool fastdraw) +{ + int sw,sh; + unsigned char r,g,b,a; + char *dst=NULL; + int offset=0; + int pitch; + surface->GetSize(surface,&sw,&sh); + if (x>=sw) return; + if (y>=sh) return; + + + + a= (c &0xff000000)>>24; + r= (c &0x00ff0000)>>16; + g= (c &0x0000ff00)>>8; + b= (c &0x000000ff); + + //TODO Fastdraw + if (surface->Lock(surface,DSLF_WRITE,(void**)(void*)&dst,&pitch) == DFB_OK) { + offset= y* pitch+x*4; + dst[offset++]=b; + dst[offset++]=g; + dst[offset++]=r; + dst[offset]=a; + surface->Unlock(surface); + } + +} + +void SurfaceDirectFB::drawPixel(int x, int y, Colour& c, bool fastdraw) +{ + int sw,sh; + char *dst=NULL; + int offset=0; + int pitch; + surface->GetSize(surface,&sw,&sh); + if (x>=sw) return; + if (y>=sh) return; + + //TODO Fastdraw + if (surface->Lock(surface,DSLF_WRITE,(void**)(void*)&dst,&pitch) == DFB_OK) { + offset= y* pitch+x*4; + dst[offset++]=c.blue; + dst[offset++]=c.green; + dst[offset++]=c.red; + dst[offset]=c.alpha; + surface->Unlock(surface); + } + +} + +void SurfaceDirectFB::drawHorzLine(int x1, int x2, int y, unsigned int c) +{ + fillblt(x1, y, x2-x1, 1, c); +} + +void SurfaceDirectFB::drawVertLine(int x, int y1, int y2, unsigned int c) +{ + fillblt(x, y1, 1, y2-y1, c); +} + +void SurfaceDirectFB::drawBitmap(int x, int y, const Bitmap& bm) +{/* + UINT bmw = bm.getWidth(); UINT bmh = bm.getHeight(); + if (bmw == 0 || bmh == 0) return; + if ((x >= (int)surface.sfc.width) || (y >= (int)surface.sfc.height)) return; + int remainder = (surface.sfc.width % 4); + UINT line; + if (remainder == 0) + line = surface.sfc.width; + else + line = surface.sfc.width + (4 - remainder); + const std::vector& bmdata = bm.rawData(); + const std::vector& Y = bm.palette.getYVector(); + const std::vector& Cr = bm.palette.getCrVector(); + const std::vector& Cb = bm.palette.getCbVector(); + const std::vector& A = bm.palette.getAVector(); + UINT b_offset = 0; + UINT s_offset = x + y*line; + UINT plotWidth = bmw; + UINT plotHeight = bmh; + if (x + plotWidth - 1 > surface.sfc.width) + plotWidth = surface.sfc.width - x + 1; + if (y + plotHeight - 1 > surface.sfc.height) + plotHeight = surface.sfc.height - y + 1; + for (UINT j = 0; j < plotHeight; ++j) + { + UINT i = 0; + if (x & 1) // odd x - need to plot first column separately + { + UCHAR index = bmdata[b_offset]; + *(surface.base[0] + s_offset) = Y[index]; + *(surface.base[1] + s_offset - 1) = Cb[index]; + *(surface.base[1] + s_offset) = Cr[index]; + *(surface.base[2] + s_offset) = A[index]; + i = 1; + } + // Now, plot pairs of pixels with averaged chroma values + while (i < plotWidth - 1) + { + UCHAR index1 = bmdata[b_offset + i]; + UCHAR index2 = bmdata[b_offset + i + 1]; + *(surface.base[0] + s_offset + i) = Y[index1]; + *(surface.base[0] + s_offset + i + 1) = Y[index2]; + *(surface.base[1] + s_offset + i) = (Cb[index1] + Cb[index2]) / 2; + *(surface.base[1] + s_offset + i + 1) = (Cr[index1] + Cr[index2]) / 2; + *(surface.base[2] + s_offset + i) = A[index1]; + *(surface.base[2] + s_offset + i + 1) = A[index2]; + i += 2; + } + if (i == plotWidth - 1) // One column left to do + { + UCHAR index = bmdata[b_offset + i]; + *(surface.base[0] + s_offset + i) = Y[index]; + *(surface.base[1] + s_offset + i) = Cb[index]; + *(surface.base[1] + s_offset + i + 1) = Cr[index]; + *(surface.base[2] + s_offset + i) = A[index]; + } + s_offset += line; + b_offset += bmw; + }*/ +} + + /* surface update to screen needs: + source x distance into this surface + source y distance into this surface + width of update + height of update + destination x on screen + destination y on screen + */ +int SurfaceDirectFB::updateToScreen(int sx, int sy, int w, int h, int dx, int dy) // FIXME new, replace others with this FIXME +{ + IDirectFBSurface* screensurf=((SurfaceDirectFB*)screen)->getSurfaceDFB(); + if (!screensurf) return 0; + if (this==screen) return 0; + + DFBRectangle rect; + int sw,sh; + + screensurf->GetSize(screensurf,&sw,&sh); +// screensurf->Clear(screensurf,0x0,0x0,0x0,0xFF); + rect.x = sx; + rect.y=sy; + rect.w=w; + rect.h=h; + + DFBRectangle drect; //TODO make osd HD + drect.x=dx*sw/720; + drect.y=dy*sh/576; + drect.w=w*sw/720; + drect.h=h*sh/576; + +// screensurf->Blit(screensurf,surface,&rect,dx,dy); + screensurf->StretchBlit(screensurf,surface,&rect,&drect); + + return 0;//blt(fdOsd, surface.sfc.handle, sx, sy, w, h, ((SurfaceDirectFB*)screen)->getSurfaceHandle(), dx, dy); +} + +int SurfaceDirectFB::blt(int fd, unsigned long shandle, int sx, int sy, int width, int height, unsigned long dhandle, int dx, int dy) +{//Skip it, noone uses this! + + return 0; +} + +void SurfaceDirectFB::screenShot(const char* fileName) +{ + return; + +} + +void SurfaceDirectFB::readPixel(int x, int y, unsigned char* r, unsigned char* g, unsigned char* b) +{ + +} + diff --git a/surfacedirectfb.h b/surfacedirectfb.h index 1171f32..74c307b 100644 --- a/surfacedirectfb.h +++ b/surfacedirectfb.h @@ -1,73 +1,73 @@ -/* - Copyright 2004-2005 Chris Tallon, 2009 Marten Richter - Portions copyright 2004 Jon Gettler - - 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 SURFACEDirectFB_H -#define SURFACEDirectFB_H - -#include -#include -#include -#include -#include - -extern "C" -{ - #include -} - -#include "defines.h" -#include "surface.h" - -#include - - - -class SurfaceDirectFB : public Surface -{ - public: - SurfaceDirectFB(int id = 0); - ~SurfaceDirectFB(); - - int create(UINT width, UINT height); - void display(); - - int fillblt(int x, int y, int width, int height, unsigned int rgba); - void drawPixel(int x, int y, unsigned int c, bool fastdraw=false); - void drawPixel(int x, int y, Colour& c, bool fastdraw=false); - void drawHorzLine(int x1, int x2, int y, unsigned int c); - void drawVertLine(int x, int y1, int y2, unsigned int c); - void drawBitmap(int x, int y, const Bitmap& bm); - int updateToScreen(int sx, int sy, int w, int h, int dx, int dy); - void readPixel(int x, int y, unsigned char* r, unsigned char* g, unsigned char* b); - void screenShot(char* fileName); - IDirectFBSurface* getSurfaceDFB(){return surface;}; - - - int blt(int fd, unsigned long shandle, int sx, int sy, int width, int height, unsigned long dhandle, int dx, int dy); - - private: - IDirectFBSurface *surface; - - - -}; - -#endif +/* + Copyright 2004-2005 Chris Tallon, 2009 Marten Richter + Portions copyright 2004 Jon Gettler + + 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 SURFACEDirectFB_H +#define SURFACEDirectFB_H + +#include +#include +#include +#include +#include + +extern "C" +{ + #include +} + +#include "defines.h" +#include "surface.h" + +#include + + + +class SurfaceDirectFB : public Surface +{ + public: + SurfaceDirectFB(int id = 0); + ~SurfaceDirectFB(); + + int create(UINT width, UINT height); + void display(); + + int fillblt(int x, int y, int width, int height, unsigned int rgba); + void drawPixel(int x, int y, unsigned int c, bool fastdraw=false); + void drawPixel(int x, int y, Colour& c, bool fastdraw=false); + void drawHorzLine(int x1, int x2, int y, unsigned int c); + void drawVertLine(int x, int y1, int y2, unsigned int c); + void drawBitmap(int x, int y, const Bitmap& bm); + int updateToScreen(int sx, int sy, int w, int h, int dx, int dy); + void readPixel(int x, int y, unsigned char* r, unsigned char* g, unsigned char* b); + void screenShot(const char* fileName); + IDirectFBSurface* getSurfaceDFB(){return surface;}; + + + int blt(int fd, unsigned long shandle, int sx, int sy, int width, int height, unsigned long dhandle, int dx, int dy); + + private: + IDirectFBSurface *surface; + + + +}; + +#endif diff --git a/surfacewin.cc b/surfacewin.cc index 2260ac9..373a0c7 100644 --- a/surfacewin.cc +++ b/surfacewin.cc @@ -1,402 +1,403 @@ -/* - Copyright 2006 Marten Richter - - 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 "surfacewin.h" -#include "osdwin.h" -#include "bitmap.h" -#include "log.h" - -SurfaceWin::SurfaceWin(int id) -: Surface(id) -{ - d3dtexture=NULL; - d3dsurface=NULL; - sheight=swidth=0; -// fastdraw=false; - event = CreateEvent(NULL,/*FALSE*/TRUE,FALSE,NULL); -} - -SurfaceWin::~SurfaceWin() -{ - if (d3dsurface) d3dsurface->Release(); - if (d3dtexture) d3dtexture->Release(); - CloseHandle(event); -} - -int SurfaceWin::create(UINT width, UINT height) -{ - OsdWin* osd=((OsdWin*)(Osd::getInstance())); - - - LPDIRECT3DDEVICE9 d3ddev=osd->getD3dDev(); - - while (true) { - if (screen==this) { - osd->BeginPainting(); - if (d3ddev->CreateTexture(1024,1024,0,0,D3DFMT_A8R8G8B8, - // Does every adapter with alpha blending support this? - D3DPOOL_DEFAULT,&d3dtexture ,NULL)!=D3D_OK) { - osd->EndPainting(); - MILLISLEEP(50);//wait maybe next time it will work - continue; - } - if (d3dtexture->GetSurfaceLevel(0,&d3dsurface)!=D3D_OK) { - d3dtexture->Release(); - d3dtexture=NULL; - MILLISLEEP(50); - osd->EndPainting(); - continue; - } - } else { - HRESULT hres; - if (hres=d3ddev->CreateOffscreenPlainSurface(width,height,D3DFMT_A8R8G8B8, - D3DPOOL_SYSTEMMEM,&d3dsurface,NULL)!=D3D_OK) { - osd->EndPainting(); - MILLISLEEP(50);//wait maybe next time it will work - - continue; - } - - } - osd->EndPainting(); - - sheight=height; - swidth=width; - /* If someone does high performance Animations on the OSD, we have to change the types - of surface in order to address these performance issues, if we have only very few updates - per second this would be fast enough !*/ - break; - } - SetEvent(event); - return 1; -} - -void SurfaceWin::display() -{ -} - -int SurfaceWin::fillblt(int x, int y, int width, int height, unsigned int c) -{ - WaitForSingleObject(event,INFINITE); //since this might be called before surface - //allocation we will wait in this case, hopefully without deadlocks - OsdWin* osd=((OsdWin*)(Osd::getInstance())); - - if (!d3dsurface) { - return 0; //why does this happen - } - - LPDIRECT3DDEVICE9 d3ddev=osd->getD3dDev(); - - if (screen==this) { - //This should not happen! - return 0; - - } else { - osd->BeginPainting(); - D3DLOCKED_RECT lockrect; - int cx,cy,cwidth,cheight; - cx=min(max(x,0),swidth-1); - cy=min(max(y,0),sheight-1); - cwidth=min(width,swidth-x); - cheight=min(height,sheight-y); - RECT rect={cx,cy,cwidth,cheight}; - - if (d3dsurface->LockRect(&lockrect,&rect,D3DLOCK_DISCARD)!=D3D_OK) { - return 0; - } - unsigned int line; - unsigned int column; - for (line=0;lineUnlockRect()!=D3D_OK) { - osd->EndPainting(); - return 0; - } - osd->EndPainting(); - } - - return 0; -} - - -void SurfaceWin::startFastDraw(){ - WaitForSingleObject(event,INFINITE); //since this might be called before surface - //allocation we will wait in this case, hopefully without deadlocks - if (!d3dsurface) { - return; //why does this happen - } - OsdWin* osd=((OsdWin*)(Osd::getInstance())); - LPDIRECT3DDEVICE9 d3ddev=osd->getD3dDev(); - if (screen==this) { - //This should not happen! - return ; - - } else { - osd->BeginPainting(); -// D3DLOCKED_RECT lockrect; - RECT rect={0,0,swidth,sheight}; - if (d3dsurface->LockRect(&lockrect,&rect,D3DLOCK_DISCARD)!=D3D_OK) { - osd->EndPainting(); - return ; - } - } -// fastdraw=true; -} -void SurfaceWin::endFastDraw(){ - OsdWin* osd=((OsdWin*)(Osd::getInstance())); - if (d3dsurface->UnlockRect()!=D3D_OK) { - osd->EndPainting(); - return ; - } - osd->EndPainting(); -// fastdraw=false; - } - -void SurfaceWin::drawPixel(int x, int y, Colour & colour, bool fastdraw) { - int c = ( (0xFF000000 ) - | (colour.red << 16) - | (colour.green << 8) - | (colour.blue ) ); - - drawPixel(x, y, c, fastdraw); - } - -void SurfaceWin::drawPixel(int x, int y, unsigned int c, bool fastdraw) -{ - //FixMe: locking for every single Pixel will be painfully slow - OsdWin* osd; - if (!fastdraw) { - WaitForSingleObject(event,INFINITE); //since this might be called before surface - //allocation we will wait in this case, hopefully without deadlocks - if (!d3dsurface) { - return; //why does this happen - } - osd=((OsdWin*)(Osd::getInstance())); - } - if (x>=swidth || y>=sheight) return; //do not draw outside the surface - if (screen==this) { - //This should not happen! - return ; - - } else { - if (!fastdraw) { - osd->BeginPainting(); -// D3DLOCKED_RECT lockrect; - RECT rect={x,y,x+1,y+1}; - if (d3dsurface->LockRect(&lockrect,&rect,D3DLOCK_DISCARD)!=D3D_OK) { - osd->EndPainting(); - return ; - } - unsigned int*row=(unsigned int*)(((char*)lockrect.pBits)); - row[0]=c; - if (d3dsurface->UnlockRect()!=D3D_OK) { - osd->EndPainting(); - return ; - } - osd->EndPainting(); - } else { - unsigned int*row=(unsigned int*)(((char*)lockrect.pBits+lockrect.Pitch*y+4*x)); - row[0]=c; - } - - } - -} - -void SurfaceWin::drawHorzLine(int x1, int x2, int y, unsigned int c) -{ - fillblt(x1, y, x2-x1, 1, c); -} - -void SurfaceWin::drawVertLine(int x, int y1, int y2, unsigned int c) -{ - fillblt(x, y1, 1, y2-y1, c); -} - -void SurfaceWin::drawBitmap(int x, int y, const Bitmap& bm) -{ - // Temporary code? Draw one pixel at a time using drawPixel() - startFastDraw(); - for (UINT j = 0; j < bm.getHeight(); ++j) - for (UINT i = 0; i < bm.getWidth(); ++i) - drawPixel(x+i, y+j, bm.getColour(i,j),true); - endFastDraw(); -} - -int SurfaceWin::updateToScreen(int sx, int sy, int w, int h, int dx, int dy) // FIXME new, replace others with this FIXME -{ - WaitForSingleObject(event,INFINITE); //since this might be called before surface - //allocation we will wait in this case, hopefully without deadlocks - if (!d3dsurface) { - return 0; //why does this happen - } - OsdWin* osd=((OsdWin*)(Osd::getInstance())); - LPDIRECT3DDEVICE9 d3ddev=osd->getD3dDev(); - LPDIRECT3DSURFACE9 screensurface=((SurfaceWin*)screen)->getD3dsurface(); - if (!screensurface) return 0; - RECT sourcerect={sx,sy,sx+w,sy+h}; - POINT destpoint={dx,dy}; - osd->BeginPainting(); - if (d3ddev->UpdateSurface(d3dsurface,&sourcerect,screensurface,&destpoint)!=D3D_OK) { - Log::getInstance()->log("Surface", Log::DEBUG, "Could not update to Screen!"); - osd->EndPainting(); - return 0; - } - osd->EndPainting(); - return 0; -} - -int SurfaceWin::blt(int fd, unsigned long shandle, int sx, int sy, int width, int height, unsigned long dhandle, int dx, int dy) -{ - //I don't see code using this function, so I skip it, since it is a MVP specific interface - return 0; -} - -void SurfaceWin::screenShot(char* fileName) -{ - //Isn't this for debugging only, so I won't implement it yet -} - -void SurfaceWin::readPixel(int x, int y, unsigned char* r, unsigned char* g, unsigned char* b) -{ - //Isn't this for debugging only, so I won't implement it yet -} -void SurfaceWin::ReleaseSurface() -{ - ResetEvent(event); - LPDIRECT3DSURFACE9 temp_surf=d3dsurface; - LPDIRECT3DTEXTURE9 temp_text=d3dtexture; - d3dsurface=NULL; - d3dtexture=NULL; - sheight=swidth=0; - if (temp_surf) temp_surf->Release(); - if (temp_text) temp_text->Release(); -} -/* -void SurfaceWin::drawJpeg(char *fileName,DWORD x, DWORD y,DWORD *width, DWORD *height){ - WaitForSingleObject(event,INFINITE); //since this might be called before surface - //allocation we will wait in this case, hopefully without deadlocks - if (!d3dsurface) { - return ; //why does this happen - } - OsdWin* osd=((OsdWin*)(Osd::getInstance())); - - - D3DXIMAGE_INFO image_inf; - osd->BeginPainting(); -// D3DXGetImageInfoFromFile(fileName,&image_inf); - D3DXGetImageInfoFromResource(NULL,fileName,&image_inf); - RECT dest_rec={x,y,x+image_inf.Width, - y+image_inf.Height}; -/* if (D3DXLoadSurfaceFromFile( - d3dsurface, - NULL, - &dest_rec, - fileName, - NULL, - D3DX_FILTER_NONE, - 0, - &image_inf)!=D3D_OK) { - Log::getInstance()->log("Surface", Log::DEBUG, "Could not open jpeg!"); - - }* - if (D3DXLoadSurfaceFromResource( - d3dsurface, - NULL, - &dest_rec, - NULL, - fileName, - NULL, - D3DX_FILTER_NONE, - 0, - &image_inf)!=D3D_OK) { - Log::getInstance()->log("Surface", Log::DEBUG, "Could not open jpeg!"); - - } - osd->EndPainting(); - *width=image_inf.Width; - *height=image_inf.Height; - -} - -void SurfaceWin::drawJpeg(char *buffer,ULONG buflength,DWORD x, DWORD y,DWORD *width, DWORD *height){ - WaitForSingleObject(event,INFINITE); //since this might be called before surface - //allocation we will wait in this case, hopefully without deadlocks - if (!d3dsurface) { - return ; //why does this happen - } - OsdWin* osd=((OsdWin*)(Osd::getInstance())); - - - D3DXIMAGE_INFO image_inf; - osd->BeginPainting(); -// D3DXGetImageInfoFromFile(fileName,&image_inf); - D3DXGetImageInfoFromFileInMemory((void*)buffer,buflength,&image_inf); - RECT dest_rec={x,y,x+image_inf.Width, - y+image_inf.Height}; -/* if (D3DXLoadSurfaceFromFile( - d3dsurface, - NULL, - &dest_rec, - fileName, - NULL, - D3DX_FILTER_NONE, - 0, - &image_inf)!=D3D_OK) { - Log::getInstance()->log("Surface", Log::DEBUG, "Could not open jpeg!"); - - }*/ -/* if (D3DXLoadSurfaceFromResource( - d3dsurface, - NULL, - &dest_rec, - NULL, - fileName, - NULL, - D3DX_FILTER_NONE, - 0, - &image_inf)!=D3D_OK) { - Log::getInstance()->log("Surface", Log::DEBUG, "Could not open jpeg!"); - - }* - if (D3DXLoadSurfaceFromFileInMemory( - d3dsurface, - NULL, - &dest_rec, - (void*)buffer, - buflength, - NULL, - D3DX_FILTER_NONE, - 0, - &image_inf)!=D3D_OK) { - Log::getInstance()->log("Surface", Log::DEBUG, "Could not open jpeg!"); - - } - osd->EndPainting(); - *width=image_inf.Width; - *height=image_inf.Height; - -}*/ - +/* + Copyright 2006 Marten Richter + + 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 "surfacewin.h" +#include "osdwin.h" +#include "bitmap.h" +#include "log.h" + +SurfaceWin::SurfaceWin(int id) +: Surface(id) +{ + d3dtexture=NULL; + d3dsurface=NULL; + sheight=swidth=0; +// fastdraw=false; + event = CreateEvent(NULL,/*FALSE*/TRUE,FALSE,NULL); +} + +SurfaceWin::~SurfaceWin() +{ + if (d3dsurface) d3dsurface->Release(); + if (d3dtexture) d3dtexture->Release(); + CloseHandle(event); +} + +int SurfaceWin::create(UINT width, UINT height) +{ + OsdWin* osd=((OsdWin*)(Osd::getInstance())); + + + LPDIRECT3DDEVICE9 d3ddev=osd->getD3dDev(); + + while (true) { + if (screen==this) { + osd->BeginPainting(); + if (d3ddev->CreateTexture(1024,1024,0,0,D3DFMT_A8R8G8B8, + // Does every adapter with alpha blending support this? + D3DPOOL_DEFAULT,&d3dtexture ,NULL)!=D3D_OK) { + osd->EndPainting(); + MILLISLEEP(50);//wait maybe next time it will work + continue; + } + if (d3dtexture->GetSurfaceLevel(0,&d3dsurface)!=D3D_OK) { + d3dtexture->Release(); + d3dtexture=NULL; + MILLISLEEP(50); + osd->EndPainting(); + continue; + } + } else { + HRESULT hres; + if (hres=d3ddev->CreateOffscreenPlainSurface(width,height,D3DFMT_A8R8G8B8, + D3DPOOL_SYSTEMMEM,&d3dsurface,NULL)!=D3D_OK) { + osd->EndPainting(); + MILLISLEEP(50);//wait maybe next time it will work + + continue; + } + + } + osd->EndPainting(); + + sheight=height; + swidth=width; + /* If someone does high performance Animations on the OSD, we have to change the types + of surface in order to address these performance issues, if we have only very few updates + per second this would be fast enough !*/ + break; + } + SetEvent(event); + return 1; +} + +void SurfaceWin::display() +{ +} + +int SurfaceWin::fillblt(int x, int y, int width, int height, unsigned int c) +{ + WaitForSingleObject(event,INFINITE); //since this might be called before surface + //allocation we will wait in this case, hopefully without deadlocks + OsdWin* osd=((OsdWin*)(Osd::getInstance())); + + if (!d3dsurface) { + return 0; //why does this happen + } + + LPDIRECT3DDEVICE9 d3ddev=osd->getD3dDev(); + + if (screen==this) { + //This should not happen! + return 0; + + } else { + osd->BeginPainting(); + D3DLOCKED_RECT lockrect; + int cx,cy,cwidth,cheight; + cx=min(max(x,0),swidth-1); + cy=min(max(y,0),sheight-1); + cwidth=min(width,swidth-x); + cheight=min(height,sheight-y); + RECT rect={cx,cy,cwidth,cheight}; + + if (d3dsurface->LockRect(&lockrect,&rect,D3DLOCK_DISCARD)!=D3D_OK) { + return 0; + } + unsigned int line; + unsigned int column; + for (line=0;lineUnlockRect()!=D3D_OK) { + osd->EndPainting(); + return 0; + } + osd->EndPainting(); + } + + return 0; +} + + +void SurfaceWin::startFastDraw(){ + WaitForSingleObject(event,INFINITE); //since this might be called before surface + //allocation we will wait in this case, hopefully without deadlocks + if (!d3dsurface) { + return; //why does this happen + } + OsdWin* osd=((OsdWin*)(Osd::getInstance())); + LPDIRECT3DDEVICE9 d3ddev=osd->getD3dDev(); + if (screen==this) { + //This should not happen! + return ; + + } else { + osd->BeginPainting(); +// D3DLOCKED_RECT lockrect; + RECT rect={0,0,swidth,sheight}; + if (d3dsurface->LockRect(&lockrect,&rect,D3DLOCK_DISCARD)!=D3D_OK) { + osd->EndPainting(); + return ; + } + } +// fastdraw=true; +} +void SurfaceWin::endFastDraw(){ + OsdWin* osd=((OsdWin*)(Osd::getInstance())); + if (d3dsurface->UnlockRect()!=D3D_OK) { + osd->EndPainting(); + return ; + } + osd->EndPainting(); +// fastdraw=false; + } + +void SurfaceWin::drawPixel(int x, int y, Colour & colour, bool fastdraw) { + int c = ( (0xFF000000 ) + | (colour.red << 16) + | (colour.green << 8) + | (colour.blue ) ); + + drawPixel(x, y, c, fastdraw); + } + +void SurfaceWin::drawPixel(int x, int y, unsigned int c, bool fastdraw) +{ + //FixMe: locking for every single Pixel will be painfully slow + OsdWin* osd; + if (!fastdraw) { + WaitForSingleObject(event,INFINITE); //since this might be called before surface + //allocation we will wait in this case, hopefully without deadlocks + if (!d3dsurface) { + return; //why does this happen + } + osd=((OsdWin*)(Osd::getInstance())); + } + if (x>=swidth || y>=sheight) return; //do not draw outside the surface + if (screen==this) { + //This should not happen! + return ; + + } else { + if (!fastdraw) { + osd->BeginPainting(); +// D3DLOCKED_RECT lockrect; + RECT rect={x,y,x+1,y+1}; + if (d3dsurface->LockRect(&lockrect,&rect,D3DLOCK_DISCARD)!=D3D_OK) { + osd->EndPainting(); + return ; + } + unsigned int*row=(unsigned int*)(((char*)lockrect.pBits)); + row[0]=c; + if (d3dsurface->UnlockRect()!=D3D_OK) { + osd->EndPainting(); + return ; + } + osd->EndPainting(); + } else { + unsigned int*row=(unsigned int*)(((char*)lockrect.pBits+lockrect.Pitch*y+4*x)); + row[0]=c; + } + + } + +} + +void SurfaceWin::drawHorzLine(int x1, int x2, int y, unsigned int c) +{ + fillblt(x1, y, x2-x1, 1, c); +} + +void SurfaceWin::drawVertLine(int x, int y1, int y2, unsigned int c) +{ + fillblt(x, y1, 1, y2-y1, c); +} + +void SurfaceWin::drawBitmap(int x, int y, const Bitmap& bm) +{ + // Temporary code? Draw one pixel at a time using drawPixel() + startFastDraw(); + for (UINT j = 0; j < bm.getHeight(); ++j) + for (UINT i = 0; i < bm.getWidth(); ++i) + drawPixel(x+i, y+j, bm.getColour(i,j),true); + endFastDraw(); +} + +int SurfaceWin::updateToScreen(int sx, int sy, int w, int h, int dx, int dy) // FIXME new, replace others with this FIXME +{ + WaitForSingleObject(event,INFINITE); //since this might be called before surface + //allocation we will wait in this case, hopefully without deadlocks + if (!d3dsurface) { + return 0; //why does this happen + } + OsdWin* osd=((OsdWin*)(Osd::getInstance())); + LPDIRECT3DDEVICE9 d3ddev=osd->getD3dDev(); + LPDIRECT3DSURFACE9 screensurface=((SurfaceWin*)screen)->getD3dsurface(); + if (!screensurface) return 0; + RECT sourcerect={sx,sy,sx+w,sy+h}; + POINT destpoint={dx,dy}; + osd->BeginPainting(); + if (d3ddev->UpdateSurface(d3dsurface,&sourcerect,screensurface,&destpoint)!=D3D_OK) { + Log::getInstance()->log("Surface", Log::DEBUG, "Could not update to Screen!"); + osd->EndPainting(); + return 0; + } + osd->EndPainting(); + return 0; +} + +int SurfaceWin::blt(int fd, unsigned long shandle, int sx, int sy, int width, int height, unsigned long dhandle, int dx, int dy) +{ + //I don't see code using this function, so I skip it, since it is a MVP specific interface + return 0; +} + +void SurfaceWin::screenShot(const char* fileName) +{ + //Isn't this for debugging only, so I won't implement it yet +} + +void SurfaceWin::readPixel(int x, int y, unsigned char* r, unsigned char* g, unsigned char* b) +{ + //Isn't this for debugging only, so I won't implement it yet +} +void SurfaceWin::ReleaseSurface() +{ + ResetEvent(event); + LPDIRECT3DSURFACE9 temp_surf=d3dsurface; + LPDIRECT3DTEXTURE9 temp_text=d3dtexture; + d3dsurface=NULL; + d3dtexture=NULL; + sheight=swidth=0; + if (temp_surf) temp_surf->Release(); + if (temp_text) temp_text->Release(); +} + +void SurfaceWin::drawJpeg(const char *fileName,int x, int y,int *width, int *height){ + WaitForSingleObject(event,INFINITE); //since this might be called before surface + //allocation we will wait in this case, hopefully without deadlocks + if (!d3dsurface) { + return ; //why does this happen + } + OsdWin* osd=((OsdWin*)(Osd::getInstance())); + + + D3DXIMAGE_INFO image_inf; + osd->BeginPainting(); +// D3DXGetImageInfoFromFile(fileName,&image_inf); + D3DXGetImageInfoFromResource(NULL,fileName,&image_inf); + RECT dest_rec={x,y,x+image_inf.Width, + y+image_inf.Height}; +/* if (D3DXLoadSurfaceFromFile( + d3dsurface, + NULL, + &dest_rec, + fileName, + NULL, + D3DX_FILTER_NONE, + 0, + &image_inf)!=D3D_OK) { + Log::getInstance()->log("Surface", Log::DEBUG, "Could not open jpeg!"); + + }*/ + if (D3DXLoadSurfaceFromResource( + d3dsurface, + NULL, + &dest_rec, + NULL, + fileName, + NULL, + D3DX_FILTER_NONE, + 0, + &image_inf)!=D3D_OK) { + Log::getInstance()->log("Surface", Log::DEBUG, "Could not open jpeg!"); + + } + osd->EndPainting(); + *width=image_inf.Width; + *height=image_inf.Height; + +} + +/* +void SurfaceWin::drawJpeg(char *buffer,ULONG buflength,DWORD x, DWORD y,DWORD *width, DWORD *height){ + WaitForSingleObject(event,INFINITE); //since this might be called before surface + //allocation we will wait in this case, hopefully without deadlocks + if (!d3dsurface) { + return ; //why does this happen + } + OsdWin* osd=((OsdWin*)(Osd::getInstance())); + + + D3DXIMAGE_INFO image_inf; + osd->BeginPainting(); +// D3DXGetImageInfoFromFile(fileName,&image_inf); + D3DXGetImageInfoFromFileInMemory((void*)buffer,buflength,&image_inf); + RECT dest_rec={x,y,x+image_inf.Width, + y+image_inf.Height}; +/* if (D3DXLoadSurfaceFromFile( + d3dsurface, + NULL, + &dest_rec, + fileName, + NULL, + D3DX_FILTER_NONE, + 0, + &image_inf)!=D3D_OK) { + Log::getInstance()->log("Surface", Log::DEBUG, "Could not open jpeg!"); + + }*/ +/* if (D3DXLoadSurfaceFromResource( + d3dsurface, + NULL, + &dest_rec, + NULL, + fileName, + NULL, + D3DX_FILTER_NONE, + 0, + &image_inf)!=D3D_OK) { + Log::getInstance()->log("Surface", Log::DEBUG, "Could not open jpeg!"); + + }* + if (D3DXLoadSurfaceFromFileInMemory( + d3dsurface, + NULL, + &dest_rec, + (void*)buffer, + buflength, + NULL, + D3DX_FILTER_NONE, + 0, + &image_inf)!=D3D_OK) { + Log::getInstance()->log("Surface", Log::DEBUG, "Could not open jpeg!"); + + } + osd->EndPainting(); + *width=image_inf.Width; + *height=image_inf.Height; + +}*/ + diff --git a/surfacewin.h b/surfacewin.h index 8d9cd9d..9949972 100644 --- a/surfacewin.h +++ b/surfacewin.h @@ -1,65 +1,65 @@ -/* - Copyright 2006 Marten Richter - - 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 SURFACEWIN_H -#define SURFACEWIN_H - -#include "defines.h" -#include "surface.h" -#include -#include - -class SurfaceWin : public Surface -{ - public: - SurfaceWin(int id = 0); - ~SurfaceWin(); - - int create(UINT width, UINT height); - void display(); - - void startFastDraw(); - void endFastDraw(); - - int fillblt(int x, int y, int width, int height, unsigned int c); - void drawPixel(int x, int y, Colour& c, bool fastdraw=false); - void drawPixel(int x, int y, unsigned int c, bool fastdraw=false); - void drawHorzLine(int x1, int x2, int y, unsigned int c); - void drawVertLine(int x, int y1, int y2, unsigned int c); - void drawBitmap(int x, int y, const Bitmap& bm); - int updateToScreen(int sx, int sy, int w, int h, int dx, int dy); - void readPixel(int x, int y, unsigned char* r, unsigned char* g, unsigned char* b); - void screenShot(char* fileName); - void ReleaseSurface(); - int blt(int fd, unsigned long shandle, int sx, int sy, int width, int height, unsigned long dhandle, int dx, int dy); -/* void drawJpeg(char *fileName,DWORD x, DWORD y,DWORD *width, DWORD *height); - void drawJpeg(char *buffer,ULONG buflength,DWORD x, DWORD y,DWORD *width, DWORD *height);*/ - LPDIRECT3DSURFACE9 getD3dsurface() {WaitForSingleObject(event,INFINITE); - return d3dsurface;}; - LPDIRECT3DTEXTURE9 getD3dtexture() {return d3dtexture;}; - private: - LPDIRECT3DSURFACE9 d3dsurface; - LPDIRECT3DTEXTURE9 d3dtexture; - D3DLOCKED_RECT lockrect; - UINT sheight,swidth; - HANDLE event; -}; - -#endif +/* + Copyright 2006 Marten Richter + + 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 SURFACEWIN_H +#define SURFACEWIN_H + +#include "defines.h" +#include "surface.h" +#include +#include + +class SurfaceWin : public Surface +{ + public: + SurfaceWin(int id = 0); + ~SurfaceWin(); + + int create(UINT width, UINT height); + void display(); + + void startFastDraw(); + void endFastDraw(); + + int fillblt(int x, int y, int width, int height, unsigned int c); + void drawPixel(int x, int y, Colour& c, bool fastdraw=false); + void drawPixel(int x, int y, unsigned int c, bool fastdraw=false); + void drawHorzLine(int x1, int x2, int y, unsigned int c); + void drawVertLine(int x, int y1, int y2, unsigned int c); + void drawBitmap(int x, int y, const Bitmap& bm); + int updateToScreen(int sx, int sy, int w, int h, int dx, int dy); + void readPixel(int x, int y, unsigned char* r, unsigned char* g, unsigned char* b); + void screenShot(const char* fileName); + void ReleaseSurface(); + int blt(int fd, unsigned long shandle, int sx, int sy, int width, int height, unsigned long dhandle, int dx, int dy); + void drawJpeg(const char *fileName,int x, int y,int *width, int *height); +/* void drawJpeg(char *buffer,ULONG buflength,DWORD x, DWORD y,DWORD *width, DWORD *height);*/ + LPDIRECT3DSURFACE9 getD3dsurface() {WaitForSingleObject(event,INFINITE); + return d3dsurface;}; + LPDIRECT3DTEXTURE9 getD3dtexture() {return d3dtexture;}; + private: + LPDIRECT3DSURFACE9 d3dsurface; + LPDIRECT3DTEXTURE9 d3dtexture; + D3DLOCKED_RECT lockrect; + UINT sheight,swidth; + HANDLE event; +}; + +#endif diff --git a/tbboxx.cc b/tbboxx.cc index e027002..1438890 100644 --- a/tbboxx.cc +++ b/tbboxx.cc @@ -45,7 +45,7 @@ void TBBoxx::setTitleText(const char* takeText, int width) void TBBoxx::draw() { - Log::getInstance()->log("TBBoxx", Log::DEBUG, "Draw"); + //Log::getInstance()->log("TBBoxx", Log::DEBUG, "Draw: %d",this); fillColour(Colour::VIEWBACKGROUND); diff --git a/teletextdecodervbiebu.cc b/teletextdecodervbiebu.cc index f5d10bc..1e97f83 100644 --- a/teletextdecodervbiebu.cc +++ b/teletextdecodervbiebu.cc @@ -1,834 +1,837 @@ -/* - Copyright 2008 Marten Richter - - 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. -*/ - - -/* Portions from vdr osdteletext plugin "txtrender.c": */ -/*************************************************************************** - * * - * txtrender.c - Teletext display abstraction and teletext code * - * renderer * - * * - * 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. * - * * - * Changelog: * - * 2005-03 initial version (c) Udo Richter * - * * - ***************************************************************************/ - -#include "teletextdecodervbiebu.h" -#include "teletxt/tables.h" -#include "message.h" -#include "command.h" -#include "video.h" - -#ifdef WIN32 -#include -#endif - -TeletextDecoderVBIEBU::TeletextDecoderVBIEBU() -{ - selectedpage=0x100; - ourpage=false; - flags=lang=0; - - CleanPage(); - FirstG0CodePage=0; - SecondG0CodePage=0; - dirty=false; - txtview=NULL; - firstlineupdate=0; - gotcha=false; - char digits[]={'1','0','0'}; - setKeyinDigits(digits,false); - isrecording=false; - for (int i=0;i<10;i++) record_pages[i]=-1; - - -} - -TeletextDecoderVBIEBU::~TeletextDecoderVBIEBU() -{ - - -} - -long long TeletextDecoderVBIEBU::SetStartOffset(long long curreftime, bool *rsync) -{ - return 0; -} - -void TeletextDecoderVBIEBU::ResetTimeOffsets() -{ - -} - - - -void TeletextDecoderVBIEBU::ResetDecoder() -{ - gotcha=false; - ourpage=false; - firstlineupdate=0; - selectedpage=0x100; - CleanPage(); - char digits[]={'1','0','0'}; - setKeyinDigits(digits,false); - for (int i=0;i<10;i++) record_pages[i]=-1; - -} - -void TeletextDecoderVBIEBU::setPage(unsigned int newpage) -{ - selectedpage=newpage; - gotcha=false; - ourpage=false; - firstlineupdate=0; - for (int i=0;i<25;i++) { - memset(curpage[i],0,40); - } -} - -void TeletextDecoderVBIEBU::CleanPage() -{ - int x,y; - for (int i=0;i<25;i++) { - memset(curpage[i],0,40); - } - for (y=0;y<25;y++) { - for (x=0;x<40;x++) { - cTeletextChar c; - c.SetFGColor(ttcWhite); - c.SetBGColor(ttcBlack); - c.SetCharset(CHARSET_LATIN_G0); - c.SetChar(' '); - if (flags&0x60) { - c.SetBoxedOut(true); - } - setChar(x,y,c); - } - } - -} -void TeletextDecoderVBIEBU::setKeyinDigits(char digits[3],bool inKeying) -{ - int x; - inkeying=inKeying; - for (x=0;x<3;x++) { - cTeletextChar c; - c.SetFGColor(ttcWhite); - c.SetBGColor(ttcBlack); - c.SetCharset(CHARSET_LATIN_G0); - c.SetChar(digits[x]); - if (flags&0x60) { - c.SetBoxedOut(true); - } - setChar(x+3,0,c); - keyindigits[x]=digits[x]; - } -} - -void TeletextDecoderVBIEBU::PrepareMediaSample(const MediaPacketList& mplist, UINT samplepos) -{ - mediapacket = mplist.front(); -} - -static ULLONG PTSDifference(ULLONG pts1, ULLONG pts2) -{ - // Assume pts1, pts2 < 2^33; calculate pts1 - pts2 - if (pts1 > pts2) - return pts1 - pts2; - else - return (1LL<<33) + pts1 - pts2; -} - -UINT TeletextDecoderVBIEBU::DeliverMediaSample(UCHAR* buffer, UINT *samplepos) -{ - if (mediapacket.type != MPTYPE_TELETEXT) - { - *samplepos= 0; - return 1; //Skip it! - } - unsigned int headerstrip; - unsigned char txtdata[43]; - headerstrip=buffer[mediapacket.pos_buffer+8]+9; - //headerstrip+=4; //substream id - unsigned int datapos=0; - unsigned int datatype=buffer[mediapacket.pos_buffer+headerstrip+0]; - //Chris should we use here the pts data from mediapacket ? - // in this case the data fields should also be added in mediamvp - if (mediapacket.synched) - { // An entry exists in the work list - ULLONG nowPTS = Video::getInstance()->getCurrentTimestamp(); - if (PTSDifference(mediapacket.pts, nowPTS) >= 1200*90000) { - *samplepos=0; - return 1;//bad data skip it - } - if (nowPTS < (mediapacket.pts-4000) ) { - *samplepos=0; - return 0; - } - } - - if (( datatype>=0x10 && datatype <=0x1F) || - ( datatype>=0x99 && datatype <=0x9B)) { - //EBU VBI DATA - datapos++; - while (datapos< (mediapacket.length-headerstrip)) { - unsigned int unit_id=buffer[mediapacket.pos_buffer+headerstrip+datapos]; - datapos++; - unsigned int unit_length=buffer[mediapacket.pos_buffer+headerstrip+datapos]; - datapos++; - switch (unit_id) { - case 0x02: - case 0x03: {//Teletext with and without subtitles - //call teletext decoder - unsigned int field=buffer[mediapacket.pos_buffer+headerstrip+datapos]; - - if ((datapos+44)< (mediapacket.length-headerstrip)) { - for (int i=0;i<42;i++) { - txtdata[i]=invtab[buffer[mediapacket.pos_buffer+headerstrip+datapos+2+i]]; - } - DecodeTeletext(txtdata,field); - } - }break; - case 0xC0: //inverted Teletext - //call teletext decoder - break; - - case 0xC3: //VPS - //what to do with this, in the moment we do not need it - break; - case 0xC4: //WSS - //what to do with this, in the moment we do not need it - break; - case 0xC5: //CC - //what to do with this, in the moment we do not need it - break; - case 0xC6: //monochrome 4:2:2 samples, what is that? - //what to do with this, in the moment we do not need it - break; - default: - // case 0x00,0x01,0x04..0x7f,0x80..0xbf,0xc1,0xc2,0xc7..0xfe,0xff: //discard - //discard - break; - }; - datapos+=unit_length; - // while (buffer[mediapacket.pos_buffer+headerstrip+datapos]==0xFF &&(datapos< (mediapacket.length-headerstrip))) { - // datapos++; //stuffing bytes - // } - - - } - *samplepos=0; - return 1; - - - } else { - *samplepos= 0; - return 1; //Skip it! and discard data - } - - return 0; - -} -// This part is inspired by the vdr-plugin-osdteletext of Udo Richter and Marcel Wiesweg -void TeletextDecoderVBIEBU::DecodeTeletext(const UCHAR* buffer, unsigned int field) //needs to be exactly 42 byte long!! -{ - UCHAR hdrbuf[5]; - for (int i=0;i<5;i++) hdrbuf[i]=(unhamtab[buffer[2*i]]&0xF) | ((unhamtab[buffer[2*i+1]]&0xF)<< 4); - int header=hdrbuf[0]; - int magazin=header & 0x7; - int line = (header>>3) & 0x1f; - if (magazin==0) magazin=8; - if (line==0) - { - if (ourpage) { - gotcha=true; - inkeying=false; - RenderTeletextCode(false); - } else { - RenderTeletextCode(true); - } - int pagenumber=hdrbuf[1]; - int pagemagazin=magazin<<8 | pagenumber; - int pagesubnumber=(hdrbuf[2]) || ((hdrbuf[3]<<8) & 0x3f7f); - - if (pagemagazin == selectedpage) ourpage=true; - else ourpage=false; - if (isrecording) { - for (int i=0;i<10;i++) { - if (pagemagazin==record_pages[i]) break; - if (record_pages[i]==-1) { - record_pages[i]=pagemagazin; - break; - } - } - } - if (hdrbuf[3] &0x80) { //This is a subtitle - for (int i=0;i<10;i++) { - if (pagemagazin==record_pages[i]) break; - if (record_pages[i]==-1) { - record_pages[i]=pagemagazin; - break; - } - } - } - - - if (ourpage) { - lang=((hdrbuf[4]>>5) & 0x07); - flags=hdrbuf[2] & 0x80; - flags|=(hdrbuf[3]&0x40)|((hdrbuf[3]>>2)&0x20); //?????? - flags|=((hdrbuf[4]<<4)&0x10)|((hdrbuf[4]<<2)&0x08)|(hdrbuf[4]&0x04)|((hdrbuf[4]>>1)&0x02)|((hdrbuf[4]>>4)&0x01); - for (int i=0;i<25;i++) { - memset(curpage[i],0,40); - } - } - - memcpy(curpage[line],buffer+2,40); - } else if (ourpage && (line<=25)) { - memcpy(curpage[line],buffer+2,40); - - } -} - - -/* from osdteletext plugin: (slightly adapted for vomp)*/ -// Font tables - -// teletext uses 7-bit numbers to identify a font set. -// There are three font sets involved: -// Primary G0, Secondary G0, and G2 font set. - -// Font tables are organized in blocks of 8 fonts: - -enumCharsets FontBlockG0_0000[8] = { - CHARSET_LATIN_G0_EN, - CHARSET_LATIN_G0_DE, - CHARSET_LATIN_G0_SV_FI, - CHARSET_LATIN_G0_IT, - CHARSET_LATIN_G0_FR, - CHARSET_LATIN_G0_PT_ES, - CHARSET_LATIN_G0_CZ_SK, - CHARSET_LATIN_G0 -}; - -enumCharsets FontBlockG2Latin[8]={ - CHARSET_LATIN_G2, - CHARSET_LATIN_G2, - CHARSET_LATIN_G2, - CHARSET_LATIN_G2, - CHARSET_LATIN_G2, - CHARSET_LATIN_G2, - CHARSET_LATIN_G2, - CHARSET_LATIN_G2 -}; - -enumCharsets FontBlockG0_0001[8] = { - CHARSET_LATIN_G0_PL, - CHARSET_LATIN_G0_DE, - CHARSET_LATIN_G0_SV_FI, - CHARSET_LATIN_G0_IT, - CHARSET_LATIN_G0_FR, - CHARSET_LATIN_G0, - CHARSET_LATIN_G0_CZ_SK, - CHARSET_LATIN_G0 -}; - -enumCharsets FontBlockG0_0010[8] = { - CHARSET_LATIN_G0_EN, - CHARSET_LATIN_G0_DE, - CHARSET_LATIN_G0_SV_FI, - CHARSET_LATIN_G0_IT, - CHARSET_LATIN_G0_FR, - CHARSET_LATIN_G0_PT_ES, - CHARSET_LATIN_G0_TR, - CHARSET_LATIN_G0 -}; - - -enumCharsets FontBlockG0_0011[8] = { - CHARSET_LATIN_G0, - CHARSET_LATIN_G0, - CHARSET_LATIN_G0, - CHARSET_LATIN_G0, - CHARSET_LATIN_G0, - CHARSET_LATIN_G0_SR_HR_SL, - CHARSET_LATIN_G0, - CHARSET_LATIN_G0_RO -}; - -enumCharsets FontBlockG0_0100[8] = { - CHARSET_CYRILLIC_G0_SR_HR, - CHARSET_LATIN_G0_DE, - CHARSET_LATIN_G0_EE, - CHARSET_LATIN_G0_LV_LT, - CHARSET_CYRILLIC_G0_RU_BG, - CHARSET_CYRILLIC_G0_UK, - CHARSET_LATIN_G0_CZ_SK, - CHARSET_INVALID -}; - -enumCharsets FontBlockG2_0100[8] = { - CHARSET_CYRILLIC_G2, - CHARSET_LATIN_G2, - CHARSET_LATIN_G2, - CHARSET_LATIN_G2, - CHARSET_CYRILLIC_G2, - CHARSET_CYRILLIC_G2, - CHARSET_LATIN_G2, - CHARSET_INVALID -}; - -enumCharsets FontBlockG0_0110[8] = { - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_LATIN_G0_TR, - CHARSET_GREEK_G0 -}; - -enumCharsets FontBlockG2_0110[8] = { - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_LATIN_G2, - CHARSET_GREEK_G2 -}; - -enumCharsets FontBlockG0_1000[8] = { - CHARSET_LATIN_G0_EN, - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_LATIN_G0_FR, - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_ARABIC_G0 -}; - -enumCharsets FontBlockG2_1000[8] = { - CHARSET_ARABIC_G2, - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_ARABIC_G2, - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_ARABIC_G2 -}; - -enumCharsets FontBlockG0_1010[8] = { - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_HEBREW_G0, - CHARSET_INVALID, - CHARSET_ARABIC_G0, -}; - -enumCharsets FontBlockG2_1010[8] = { - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_ARABIC_G2, - CHARSET_INVALID, - CHARSET_ARABIC_G2, -}; - -enumCharsets FontBlockInvalid[8] = { - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_INVALID, - CHARSET_INVALID -}; - - - -// The actual font table definition: -// Split the 7-bit number into upper 4 and lower 3 bits, -// use upper 4 bits for outer array, -// use lower 3 bits for inner array - -struct structFontBlock { - enumCharsets *G0Block; - enumCharsets *G2Block; -}; - -structFontBlock FontTable[16] = { - { FontBlockG0_0000, FontBlockG2Latin }, // 0000 block - { FontBlockG0_0001, FontBlockG2Latin }, // 0001 block - { FontBlockG0_0010, FontBlockG2Latin }, // 0010 block - { FontBlockG0_0011, FontBlockG2Latin }, // 0011 block - { FontBlockG0_0100, FontBlockG2_0100 }, // 0100 block - { FontBlockInvalid, FontBlockInvalid }, // 0101 block - { FontBlockG0_0110, FontBlockG2_0110 }, // 0110 block - { FontBlockInvalid, FontBlockInvalid }, // 0111 block - { FontBlockG0_1000, FontBlockG2_1000 }, // 1000 block - { FontBlockInvalid, FontBlockInvalid }, // 1001 block - { FontBlockG0_1010, FontBlockG2_1010 }, // 1010 block - { FontBlockInvalid, FontBlockInvalid }, // 1011 block - { FontBlockInvalid, FontBlockInvalid }, // 1100 block - { FontBlockInvalid, FontBlockInvalid }, // 1101 block - { FontBlockInvalid, FontBlockInvalid }, // 1110 block - { FontBlockInvalid, FontBlockInvalid } // 1111 block -}; - -inline enumCharsets GetG0Charset(int codepage) { - return FontTable[codepage>>3].G0Block[codepage&7]; -} -inline enumCharsets GetG2Charset(int codepage) { - return FontTable[codepage>>3].G2Block[codepage&7]; -} - -enum enumSizeMode { - // Possible size modifications of characters - sizeNormal, - sizeDoubleWidth, - sizeDoubleHeight, - sizeDoubleSize -}; - -void TeletextDecoderVBIEBU::RenderTeletextCode(bool renderfirstlineonly) { - int x,y; - bool EmptyNextLine=false; - // Skip one line, in case double height chars were/will be used - - // Get code pages: - int LocalG0CodePage=(FirstG0CodePage & 0x78) - | ((lang & 0x04)>>2) | (lang & 0x02) | ((lang & 0x01)<<2); - enumCharsets FirstG0=GetG0Charset(LocalG0CodePage); - enumCharsets SecondG0=GetG0Charset(SecondG0CodePage); - // Reserved for later use: - // enumCharsets FirstG2=GetG2Charset(LocalG0CodePage); - - for (y=0;y<24;(EmptyNextLine?y+=2:y++)) { - // Start of line: Set start of line defaults - - // Hold Mosaics mode: Remember last mosaic char/charset - // for next spacing code - bool HoldMosaics=false; - unsigned char HoldMosaicChar=' '; - enumCharsets HoldMosaicCharset=FirstG0; - - enumSizeMode Size=sizeNormal; - // Font size modification - bool SecondCharset=false; - // Use primary or secondary G0 charset - bool GraphicCharset=false; - // Graphics charset used? - bool SeparateGraphics=false; - // Use separated vs. contiguous graphics charset - bool NoNextChar=false; - // Skip display of next char, for double-width - EmptyNextLine=false; - // Skip next line, for double-height - - cTeletextChar c; - // auto.initialized to everything off - c.SetFGColor(ttcWhite); - c.SetBGColor(ttcBlack); - c.SetCharset(FirstG0); - - if (y==0 && (flags&0x10)) { - if (!inkeying ) c.SetBoxedOut(true); - - } - if (flags&0x60) { - if (!(inkeying && y==0) ) c.SetBoxedOut(true); - - } - if (y==0) { - - for (x=0;x<8;x++) { - cTeletextChar c2=c; - - if (x>=3 && x<6){ - c2.SetChar(keyindigits[x-3]); - - } - else - c2.SetChar(' '); - setChar(x,0,c2); - } - } - - // Pre-scan for double-height and double-size codes - for (x=0;x<40;x++) { - if (y==0 && x<8) x=8; - if ((curpage[y][x] & 0x7f)==0x0D || (curpage[y][x] & 0x7f)==0x0F) - EmptyNextLine=true; - } - - // Move through line - for (x=0;x<40;x++) { - unsigned char ttc=curpage[y][x] & 0x7f; - // skip parity check - - if (y==0 && x<8) continue; - if (y==0 && x<31 && renderfirstlineonly && gotcha && !inkeying) continue; - // no displayable data here... - -/* // Debug only: Output line data and spacing codes - if (y==6) { - if (ttc<0x20) - printf("%s ",names[ttc]); - else - printf("%02x ",ttc); - if (x==39) printf("\n"); - } -*/ - - // Handle all 'Set-At' spacing codes - switch (ttc) { - case 0x09: // Steady - c.SetBlink(false); - break; - case 0x0C: // Normal Size - if (Size!=sizeNormal) { - Size=sizeNormal; - HoldMosaicChar=' '; - HoldMosaicCharset=FirstG0; - } - break; - case 0x18: // Conceal - c.SetConceal(true); - break; - case 0x19: // Contiguous Mosaic Graphics - SeparateGraphics=false; - if (GraphicCharset) - c.SetCharset(CHARSET_GRAPHICS_G1); - break; - case 0x1A: // Separated Mosaic Graphics - SeparateGraphics=true; - if (GraphicCharset) - c.SetCharset(CHARSET_GRAPHICS_G1_SEP); - break; - case 0x1C: // Black Background - c.SetBGColor(ttcBlack); - break; - case 0x1D: // New Background - c.SetBGColor(c.GetFGColor()); - break; - case 0x1E: // Hold Mosaic - HoldMosaics=true; - break; - } - - // temporary copy of character data: - cTeletextChar c2=c; - // c2 will be text character or space character or hold mosaic - // c2 may also have temporary flags or charsets - - if (ttc<0x20) { - // Spacing code, display space or hold mosaic - if (HoldMosaics) { - c2.SetChar(HoldMosaicChar); - c2.SetCharset(HoldMosaicCharset); - } else { - c2.SetChar(' '); - } - } else { - // Character code - c2.SetChar(ttc); - if (GraphicCharset) { - if (ttc&0x20) { - // real graphics code, remember for HoldMosaics - HoldMosaicChar=ttc; - HoldMosaicCharset=c.GetCharset(); - } else { - // invalid code, pass-through to G0 - c2.SetCharset(SecondCharset?SecondG0:FirstG0); - } - } - } - - // Handle double-height and double-width extremes - if (y>=23) { - if (Size==sizeDoubleHeight) Size=sizeNormal; - if (Size==sizeDoubleSize) Size=sizeDoubleWidth; - } - if (x>=38) { - if (Size==sizeDoubleWidth) Size=sizeNormal; - if (Size==sizeDoubleSize) Size=sizeDoubleHeight; - } - - // Now set character code - - if (NoNextChar) { - // Suppress this char due to double width last char - NoNextChar=false; - } else { - switch (Size) { - case sizeNormal: - // Normal sized - setChar(x,y,c2); - if (EmptyNextLine && y<23) { - // Clean up next line - setChar(x,y+1,c2.ToChar(' ').ToCharset(FirstG0)); - } - break; - case sizeDoubleWidth: - // Double width - setChar(x,y,c2.ToDblWidth(dblw_Left)); - setChar(x+1,y,c2.ToDblWidth(dblw_Right)); - if (EmptyNextLine && y<23) { - // Clean up next line - setChar(x ,y+1,c2.ToChar(' ').ToCharset(FirstG0)); - setChar(x+1,y+1,c2.ToChar(' ').ToCharset(FirstG0)); - } - NoNextChar=true; - break; - case sizeDoubleHeight: - // Double height - setChar(x,y,c2.ToDblHeight(dblh_Top)); - setChar(x,y+1,c2.ToDblHeight(dblh_Bottom)); - break; - case sizeDoubleSize: - // Double Size - setChar(x , y,c2.ToDblHeight(dblh_Top ).ToDblWidth(dblw_Left )); - setChar(x+1, y,c2.ToDblHeight(dblh_Top ).ToDblWidth(dblw_Right)); - setChar(x ,y+1,c2.ToDblHeight(dblh_Bottom).ToDblWidth(dblw_Left )); - setChar(x+1,y+1,c2.ToDblHeight(dblh_Bottom).ToDblWidth(dblw_Right)); - NoNextChar=true; - break; - } - } - - // Handle all 'Set-After' spacing codes - if (ttc>=0x00 && ttc<=0x07) { // Set FG color - if (GraphicCharset) { - // Actual switch from graphics charset - HoldMosaicChar=' '; - HoldMosaicCharset=FirstG0; - } - c.SetFGColor((enumTeletextColor)ttc); - c.SetCharset(SecondCharset?SecondG0:FirstG0); - GraphicCharset=false; - c.SetConceal(false); - } else if (ttc==0x08) { - c.SetBlink(true); - } else if (ttc==0x0A) { - c.SetBoxedOut(true); - } else if (ttc==0x0B) { - // Start Box - c.SetBoxedOut(false); - } else if (ttc==0x0D) { - if (Size!=sizeDoubleHeight) { - Size=sizeDoubleHeight; - HoldMosaicChar=' '; - HoldMosaicCharset=FirstG0; - } - } else if (ttc==0x0E) { - if (Size!=sizeDoubleWidth) { - Size=sizeDoubleWidth; - HoldMosaicChar=' '; - HoldMosaicCharset=FirstG0; - } - } else if (ttc==0x0E) { - if (Size!=sizeDoubleSize) { - Size=sizeDoubleSize; - HoldMosaicChar=' '; - HoldMosaicCharset=FirstG0; - } - } else if (ttc>=0x10 && ttc<=0x17) { - if (!GraphicCharset) { - // Actual switch to graphics charset - HoldMosaicChar=' '; - HoldMosaicCharset=FirstG0; - } - c.SetFGColor((enumTeletextColor)(ttc-0x10)); - c.SetCharset(SeparateGraphics?CHARSET_GRAPHICS_G1_SEP:CHARSET_GRAPHICS_G1); - GraphicCharset=true; - c.SetConceal(false); - } else if (ttc==0x1B) { - SecondCharset=!SecondCharset; - if (!GraphicCharset) c.SetCharset(SecondCharset?SecondG0:FirstG0); - } else if (ttc==0x1F) { - HoldMosaics=false; - } - - } // end for x - if (renderfirstlineonly) break; - } // end for y - - for (x=0;x<40;x++) { - // Clean out last line - cTeletextChar c; - c.SetFGColor(ttcWhite); - c.SetBGColor(ttcBlack); - c.SetCharset(FirstG0); - c.SetChar(' '); - if (flags&0x60) { - c.SetBoxedOut(true); - } - setChar(x,24,c); - } - for (y=0;y<25;y++) { - for (x=0;x<40;x++) { - if (isDirty(x,y)) { - dirty=true; - break; - } - if (dirty) break; - } - if (dirty) break; - } - - if (dirty && txtview!=NULL ) { - - if ( !renderfirstlineonly) { - Message* m= new Message(); - m->message = Message::TELETEXTUPDATE; - m->to = txtview; - m->from = this; - m->parameter = 0; - Command::getInstance()->postMessageFromOuterSpace(m); - } else if (firstlineupdate==10) { - Message* m= new Message(); - m->message = Message::TELETEXTUPDATEFIRSTLINE; - m->to = txtview; - m->from = this; - m->parameter = 0; - Command::getInstance()->postMessageFromOuterSpace(m); - firstlineupdate=0; - } else firstlineupdate++; - - - } - -} +/* + Copyright 2008 Marten Richter + + 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. +*/ + + +/* Portions from vdr osdteletext plugin "txtrender.c": */ +/*************************************************************************** + * * + * txtrender.c - Teletext display abstraction and teletext code * + * renderer * + * * + * 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. * + * * + * Changelog: * + * 2005-03 initial version (c) Udo Richter * + * * + ***************************************************************************/ + +#include "teletextdecodervbiebu.h" +#include "teletxt/tables.h" +#include "message.h" +#include "command.h" +#include "video.h" + +#ifdef WIN32 +#include +#endif + +TeletextDecoderVBIEBU::TeletextDecoderVBIEBU() +{ + selectedpage=0x100; + ourpage=false; + flags=lang=0; + + CleanPage(); + FirstG0CodePage=0; + SecondG0CodePage=0; + dirty=false; + txtview=NULL; + firstlineupdate=0; + gotcha=false; + char digits[]={'1','0','0'}; + setKeyinDigits(digits,false); + isrecording=false; + for (int i=0;i<10;i++) record_pages[i]=-1; + + +} + +TeletextDecoderVBIEBU::~TeletextDecoderVBIEBU() +{ + + +} + +long long TeletextDecoderVBIEBU::SetStartOffset(long long curreftime, bool *rsync) +{ + return 0; +} + +void TeletextDecoderVBIEBU::ResetTimeOffsets() +{ + +} + + + +void TeletextDecoderVBIEBU::ResetDecoder() +{ + gotcha=false; + ourpage=false; + firstlineupdate=0; + selectedpage=0x100; + CleanPage(); + char digits[]={'1','0','0'}; + setKeyinDigits(digits,false); + for (int i=0;i<10;i++) record_pages[i]=-1; + +} + +void TeletextDecoderVBIEBU::setPage(unsigned int newpage) +{ + selectedpage=newpage; + gotcha=false; + ourpage=false; + firstlineupdate=0; + for (int i=0;i<25;i++) { + memset(curpage[i],0,40); + } +} + +void TeletextDecoderVBIEBU::CleanPage() +{ + int x,y; + for (int i=0;i<25;i++) { + memset(curpage[i],0,40); + } + for (y=0;y<25;y++) { + for (x=0;x<40;x++) { + cTeletextChar c; + c.SetFGColor(ttcWhite); + c.SetBGColor(ttcBlack); + c.SetCharset(CHARSET_LATIN_G0); + c.SetChar(' '); + if (flags&0x60) { + c.SetBoxedOut(true); + } + setChar(x,y,c); + } + } + +} +void TeletextDecoderVBIEBU::setKeyinDigits(char digits[3],bool inKeying) +{ + int x; + inkeying=inKeying; + for (x=0;x<3;x++) { + cTeletextChar c; + c.SetFGColor(ttcWhite); + c.SetBGColor(ttcBlack); + c.SetCharset(CHARSET_LATIN_G0); + c.SetChar(digits[x]); + if (flags&0x60) { + c.SetBoxedOut(true); + } + setChar(x+3,0,c); + keyindigits[x]=digits[x]; + } +} + +void TeletextDecoderVBIEBU::PrepareMediaSample(const MediaPacketList& mplist, UINT samplepos) +{ + mediapacket = mplist.front(); +} + +static ULLONG TxtPTSDifference(ULLONG pts1, ULLONG pts2) +{ + // Assume pts1, pts2 < 2^33; calculate pts1 - pts2 + if (pts1 > pts2) + return pts1 - pts2; + else + return -pts1 + pts2; +} + +UINT TeletextDecoderVBIEBU::DeliverMediaSample(UCHAR* buffer, UINT *samplepos) +{ + if (mediapacket.type != MPTYPE_TELETEXT) + { + *samplepos= 0; + return 1; //Skip it! + } + unsigned int headerstrip; + unsigned char txtdata[43]; + headerstrip=buffer[mediapacket.pos_buffer+8]+9; + //headerstrip+=4; //substream id + unsigned int datapos=0; + unsigned int datatype=buffer[mediapacket.pos_buffer+headerstrip+0]; + //Chris should we use here the pts data from mediapacket ? + // in this case the data fields should also be added in mediamvp + if (mediapacket.synched) + { // An entry exists in the work list + ULLONG nowPTS = Video::getInstance()->getCurrentTimestamp(); + + ULLONG ptsdifference=TxtPTSDifference(mediapacket.pts, nowPTS); + + if (ptsdifference >= (120LL*90000LL)) { + *samplepos=0; + return 1;//bad data skip it + } + if (nowPTS < (mediapacket.pts-4000) ) { + *samplepos=0; + return 0; + } + } + + if (( datatype>=0x10 && datatype <=0x1F) || + ( datatype>=0x99 && datatype <=0x9B)) { + //EBU VBI DATA + datapos++; + while (datapos< (mediapacket.length-headerstrip)) { + unsigned int unit_id=buffer[mediapacket.pos_buffer+headerstrip+datapos]; + datapos++; + unsigned int unit_length=buffer[mediapacket.pos_buffer+headerstrip+datapos]; + datapos++; + switch (unit_id) { + case 0x02: + case 0x03: {//Teletext with and without subtitles + //call teletext decoder + unsigned int field=buffer[mediapacket.pos_buffer+headerstrip+datapos]; + + if ((datapos+44)< (mediapacket.length-headerstrip)) { + for (int i=0;i<42;i++) { + txtdata[i]=invtab[buffer[mediapacket.pos_buffer+headerstrip+datapos+2+i]]; + } + DecodeTeletext(txtdata,field); + } + }break; + case 0xC0: //inverted Teletext + //call teletext decoder + break; + + case 0xC3: //VPS + //what to do with this, in the moment we do not need it + break; + case 0xC4: //WSS + //what to do with this, in the moment we do not need it + break; + case 0xC5: //CC + //what to do with this, in the moment we do not need it + break; + case 0xC6: //monochrome 4:2:2 samples, what is that? + //what to do with this, in the moment we do not need it + break; + default: + // case 0x00,0x01,0x04..0x7f,0x80..0xbf,0xc1,0xc2,0xc7..0xfe,0xff: //discard + //discard + break; + }; + datapos+=unit_length; + // while (buffer[mediapacket.pos_buffer+headerstrip+datapos]==0xFF &&(datapos< (mediapacket.length-headerstrip))) { + // datapos++; //stuffing bytes + // } + + + } + *samplepos=0; + return 1; + + + } else { + *samplepos= 0; + return 1; //Skip it! and discard data + } + + return 0; + +} +// This part is inspired by the vdr-plugin-osdteletext of Udo Richter and Marcel Wiesweg +void TeletextDecoderVBIEBU::DecodeTeletext(const UCHAR* buffer, unsigned int field) //needs to be exactly 42 byte long!! +{ + UCHAR hdrbuf[5]; + for (int i=0;i<5;i++) hdrbuf[i]=(unhamtab[buffer[2*i]]&0xF) | ((unhamtab[buffer[2*i+1]]&0xF)<< 4); + int header=hdrbuf[0]; + int magazin=header & 0x7; + int line = (header>>3) & 0x1f; + if (magazin==0) magazin=8; + if (line==0) + { + if (ourpage) { + gotcha=true; + inkeying=false; + RenderTeletextCode(false); + } else { + RenderTeletextCode(true); + } + int pagenumber=hdrbuf[1]; + int pagemagazin=magazin<<8 | pagenumber; + int pagesubnumber=(hdrbuf[2]) || ((hdrbuf[3]<<8) & 0x3f7f); + + if (pagemagazin == selectedpage) ourpage=true; + else ourpage=false; + if (isrecording) { + for (int i=0;i<10;i++) { + if (pagemagazin==record_pages[i]) break; + if (record_pages[i]==-1) { + record_pages[i]=pagemagazin; + break; + } + } + } + if (hdrbuf[3] &0x80) { //This is a subtitle + for (int i=0;i<10;i++) { + if (pagemagazin==record_pages[i]) break; + if (record_pages[i]==-1) { + record_pages[i]=pagemagazin; + break; + } + } + } + + + if (ourpage) { + lang=((hdrbuf[4]>>5) & 0x07); + flags=hdrbuf[2] & 0x80; + flags|=(hdrbuf[3]&0x40)|((hdrbuf[3]>>2)&0x20); //?????? + flags|=((hdrbuf[4]<<4)&0x10)|((hdrbuf[4]<<2)&0x08)|(hdrbuf[4]&0x04)|((hdrbuf[4]>>1)&0x02)|((hdrbuf[4]>>4)&0x01); + for (int i=0;i<25;i++) { + memset(curpage[i],0,40); + } + } + + memcpy(curpage[line],buffer+2,40); + } else if (ourpage && (line<=25)) { + memcpy(curpage[line],buffer+2,40); + + } +} + + +/* from osdteletext plugin: (slightly adapted for vomp)*/ +// Font tables + +// teletext uses 7-bit numbers to identify a font set. +// There are three font sets involved: +// Primary G0, Secondary G0, and G2 font set. + +// Font tables are organized in blocks of 8 fonts: + +enumCharsets FontBlockG0_0000[8] = { + CHARSET_LATIN_G0_EN, + CHARSET_LATIN_G0_DE, + CHARSET_LATIN_G0_SV_FI, + CHARSET_LATIN_G0_IT, + CHARSET_LATIN_G0_FR, + CHARSET_LATIN_G0_PT_ES, + CHARSET_LATIN_G0_CZ_SK, + CHARSET_LATIN_G0 +}; + +enumCharsets FontBlockG2Latin[8]={ + CHARSET_LATIN_G2, + CHARSET_LATIN_G2, + CHARSET_LATIN_G2, + CHARSET_LATIN_G2, + CHARSET_LATIN_G2, + CHARSET_LATIN_G2, + CHARSET_LATIN_G2, + CHARSET_LATIN_G2 +}; + +enumCharsets FontBlockG0_0001[8] = { + CHARSET_LATIN_G0_PL, + CHARSET_LATIN_G0_DE, + CHARSET_LATIN_G0_SV_FI, + CHARSET_LATIN_G0_IT, + CHARSET_LATIN_G0_FR, + CHARSET_LATIN_G0, + CHARSET_LATIN_G0_CZ_SK, + CHARSET_LATIN_G0 +}; + +enumCharsets FontBlockG0_0010[8] = { + CHARSET_LATIN_G0_EN, + CHARSET_LATIN_G0_DE, + CHARSET_LATIN_G0_SV_FI, + CHARSET_LATIN_G0_IT, + CHARSET_LATIN_G0_FR, + CHARSET_LATIN_G0_PT_ES, + CHARSET_LATIN_G0_TR, + CHARSET_LATIN_G0 +}; + + +enumCharsets FontBlockG0_0011[8] = { + CHARSET_LATIN_G0, + CHARSET_LATIN_G0, + CHARSET_LATIN_G0, + CHARSET_LATIN_G0, + CHARSET_LATIN_G0, + CHARSET_LATIN_G0_SR_HR_SL, + CHARSET_LATIN_G0, + CHARSET_LATIN_G0_RO +}; + +enumCharsets FontBlockG0_0100[8] = { + CHARSET_CYRILLIC_G0_SR_HR, + CHARSET_LATIN_G0_DE, + CHARSET_LATIN_G0_EE, + CHARSET_LATIN_G0_LV_LT, + CHARSET_CYRILLIC_G0_RU_BG, + CHARSET_CYRILLIC_G0_UK, + CHARSET_LATIN_G0_CZ_SK, + CHARSET_INVALID +}; + +enumCharsets FontBlockG2_0100[8] = { + CHARSET_CYRILLIC_G2, + CHARSET_LATIN_G2, + CHARSET_LATIN_G2, + CHARSET_LATIN_G2, + CHARSET_CYRILLIC_G2, + CHARSET_CYRILLIC_G2, + CHARSET_LATIN_G2, + CHARSET_INVALID +}; + +enumCharsets FontBlockG0_0110[8] = { + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_LATIN_G0_TR, + CHARSET_GREEK_G0 +}; + +enumCharsets FontBlockG2_0110[8] = { + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_LATIN_G2, + CHARSET_GREEK_G2 +}; + +enumCharsets FontBlockG0_1000[8] = { + CHARSET_LATIN_G0_EN, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_LATIN_G0_FR, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_ARABIC_G0 +}; + +enumCharsets FontBlockG2_1000[8] = { + CHARSET_ARABIC_G2, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_ARABIC_G2, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_ARABIC_G2 +}; + +enumCharsets FontBlockG0_1010[8] = { + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_HEBREW_G0, + CHARSET_INVALID, + CHARSET_ARABIC_G0, +}; + +enumCharsets FontBlockG2_1010[8] = { + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_ARABIC_G2, + CHARSET_INVALID, + CHARSET_ARABIC_G2, +}; + +enumCharsets FontBlockInvalid[8] = { + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID +}; + + + +// The actual font table definition: +// Split the 7-bit number into upper 4 and lower 3 bits, +// use upper 4 bits for outer array, +// use lower 3 bits for inner array + +struct structFontBlock { + enumCharsets *G0Block; + enumCharsets *G2Block; +}; + +structFontBlock FontTable[16] = { + { FontBlockG0_0000, FontBlockG2Latin }, // 0000 block + { FontBlockG0_0001, FontBlockG2Latin }, // 0001 block + { FontBlockG0_0010, FontBlockG2Latin }, // 0010 block + { FontBlockG0_0011, FontBlockG2Latin }, // 0011 block + { FontBlockG0_0100, FontBlockG2_0100 }, // 0100 block + { FontBlockInvalid, FontBlockInvalid }, // 0101 block + { FontBlockG0_0110, FontBlockG2_0110 }, // 0110 block + { FontBlockInvalid, FontBlockInvalid }, // 0111 block + { FontBlockG0_1000, FontBlockG2_1000 }, // 1000 block + { FontBlockInvalid, FontBlockInvalid }, // 1001 block + { FontBlockG0_1010, FontBlockG2_1010 }, // 1010 block + { FontBlockInvalid, FontBlockInvalid }, // 1011 block + { FontBlockInvalid, FontBlockInvalid }, // 1100 block + { FontBlockInvalid, FontBlockInvalid }, // 1101 block + { FontBlockInvalid, FontBlockInvalid }, // 1110 block + { FontBlockInvalid, FontBlockInvalid } // 1111 block +}; + +inline enumCharsets GetG0Charset(int codepage) { + return FontTable[codepage>>3].G0Block[codepage&7]; +} +inline enumCharsets GetG2Charset(int codepage) { + return FontTable[codepage>>3].G2Block[codepage&7]; +} + +enum enumSizeMode { + // Possible size modifications of characters + sizeNormal, + sizeDoubleWidth, + sizeDoubleHeight, + sizeDoubleSize +}; + +void TeletextDecoderVBIEBU::RenderTeletextCode(bool renderfirstlineonly) { + int x,y; + bool EmptyNextLine=false; + // Skip one line, in case double height chars were/will be used + + // Get code pages: + int LocalG0CodePage=(FirstG0CodePage & 0x78) + | ((lang & 0x04)>>2) | (lang & 0x02) | ((lang & 0x01)<<2); + enumCharsets FirstG0=GetG0Charset(LocalG0CodePage); + enumCharsets SecondG0=GetG0Charset(SecondG0CodePage); + // Reserved for later use: + // enumCharsets FirstG2=GetG2Charset(LocalG0CodePage); + + for (y=0;y<24;(EmptyNextLine?y+=2:y++)) { + // Start of line: Set start of line defaults + + // Hold Mosaics mode: Remember last mosaic char/charset + // for next spacing code + bool HoldMosaics=false; + unsigned char HoldMosaicChar=' '; + enumCharsets HoldMosaicCharset=FirstG0; + + enumSizeMode Size=sizeNormal; + // Font size modification + bool SecondCharset=false; + // Use primary or secondary G0 charset + bool GraphicCharset=false; + // Graphics charset used? + bool SeparateGraphics=false; + // Use separated vs. contiguous graphics charset + bool NoNextChar=false; + // Skip display of next char, for double-width + EmptyNextLine=false; + // Skip next line, for double-height + + cTeletextChar c; + // auto.initialized to everything off + c.SetFGColor(ttcWhite); + c.SetBGColor(ttcBlack); + c.SetCharset(FirstG0); + + if (y==0 && (flags&0x10)) { + if (!inkeying ) c.SetBoxedOut(true); + + } + if (flags&0x60) { + if (!(inkeying && y==0) ) c.SetBoxedOut(true); + + } + if (y==0) { + + for (x=0;x<8;x++) { + cTeletextChar c2=c; + + if (x>=3 && x<6){ + c2.SetChar(keyindigits[x-3]); + + } + else + c2.SetChar(' '); + setChar(x,0,c2); + } + } + + // Pre-scan for double-height and double-size codes + for (x=0;x<40;x++) { + if (y==0 && x<8) x=8; + if ((curpage[y][x] & 0x7f)==0x0D || (curpage[y][x] & 0x7f)==0x0F) + EmptyNextLine=true; + } + + // Move through line + for (x=0;x<40;x++) { + unsigned char ttc=curpage[y][x] & 0x7f; + // skip parity check + + if (y==0 && x<8) continue; + if (y==0 && x<31 && renderfirstlineonly && gotcha && !inkeying) continue; + // no displayable data here... + +/* // Debug only: Output line data and spacing codes + if (y==6) { + if (ttc<0x20) + printf("%s ",names[ttc]); + else + printf("%02x ",ttc); + if (x==39) printf("\n"); + } +*/ + + // Handle all 'Set-At' spacing codes + switch (ttc) { + case 0x09: // Steady + c.SetBlink(false); + break; + case 0x0C: // Normal Size + if (Size!=sizeNormal) { + Size=sizeNormal; + HoldMosaicChar=' '; + HoldMosaicCharset=FirstG0; + } + break; + case 0x18: // Conceal + c.SetConceal(true); + break; + case 0x19: // Contiguous Mosaic Graphics + SeparateGraphics=false; + if (GraphicCharset) + c.SetCharset(CHARSET_GRAPHICS_G1); + break; + case 0x1A: // Separated Mosaic Graphics + SeparateGraphics=true; + if (GraphicCharset) + c.SetCharset(CHARSET_GRAPHICS_G1_SEP); + break; + case 0x1C: // Black Background + c.SetBGColor(ttcBlack); + break; + case 0x1D: // New Background + c.SetBGColor(c.GetFGColor()); + break; + case 0x1E: // Hold Mosaic + HoldMosaics=true; + break; + } + + // temporary copy of character data: + cTeletextChar c2=c; + // c2 will be text character or space character or hold mosaic + // c2 may also have temporary flags or charsets + + if (ttc<0x20) { + // Spacing code, display space or hold mosaic + if (HoldMosaics) { + c2.SetChar(HoldMosaicChar); + c2.SetCharset(HoldMosaicCharset); + } else { + c2.SetChar(' '); + } + } else { + // Character code + c2.SetChar(ttc); + if (GraphicCharset) { + if (ttc&0x20) { + // real graphics code, remember for HoldMosaics + HoldMosaicChar=ttc; + HoldMosaicCharset=c.GetCharset(); + } else { + // invalid code, pass-through to G0 + c2.SetCharset(SecondCharset?SecondG0:FirstG0); + } + } + } + + // Handle double-height and double-width extremes + if (y>=23) { + if (Size==sizeDoubleHeight) Size=sizeNormal; + if (Size==sizeDoubleSize) Size=sizeDoubleWidth; + } + if (x>=38) { + if (Size==sizeDoubleWidth) Size=sizeNormal; + if (Size==sizeDoubleSize) Size=sizeDoubleHeight; + } + + // Now set character code + + if (NoNextChar) { + // Suppress this char due to double width last char + NoNextChar=false; + } else { + switch (Size) { + case sizeNormal: + // Normal sized + setChar(x,y,c2); + if (EmptyNextLine && y<23) { + // Clean up next line + setChar(x,y+1,c2.ToChar(' ').ToCharset(FirstG0)); + } + break; + case sizeDoubleWidth: + // Double width + setChar(x,y,c2.ToDblWidth(dblw_Left)); + setChar(x+1,y,c2.ToDblWidth(dblw_Right)); + if (EmptyNextLine && y<23) { + // Clean up next line + setChar(x ,y+1,c2.ToChar(' ').ToCharset(FirstG0)); + setChar(x+1,y+1,c2.ToChar(' ').ToCharset(FirstG0)); + } + NoNextChar=true; + break; + case sizeDoubleHeight: + // Double height + setChar(x,y,c2.ToDblHeight(dblh_Top)); + setChar(x,y+1,c2.ToDblHeight(dblh_Bottom)); + break; + case sizeDoubleSize: + // Double Size + setChar(x , y,c2.ToDblHeight(dblh_Top ).ToDblWidth(dblw_Left )); + setChar(x+1, y,c2.ToDblHeight(dblh_Top ).ToDblWidth(dblw_Right)); + setChar(x ,y+1,c2.ToDblHeight(dblh_Bottom).ToDblWidth(dblw_Left )); + setChar(x+1,y+1,c2.ToDblHeight(dblh_Bottom).ToDblWidth(dblw_Right)); + NoNextChar=true; + break; + } + } + + // Handle all 'Set-After' spacing codes + if (ttc>=0x00 && ttc<=0x07) { // Set FG color + if (GraphicCharset) { + // Actual switch from graphics charset + HoldMosaicChar=' '; + HoldMosaicCharset=FirstG0; + } + c.SetFGColor((enumTeletextColor)ttc); + c.SetCharset(SecondCharset?SecondG0:FirstG0); + GraphicCharset=false; + c.SetConceal(false); + } else if (ttc==0x08) { + c.SetBlink(true); + } else if (ttc==0x0A) { + c.SetBoxedOut(true); + } else if (ttc==0x0B) { + // Start Box + c.SetBoxedOut(false); + } else if (ttc==0x0D) { + if (Size!=sizeDoubleHeight) { + Size=sizeDoubleHeight; + HoldMosaicChar=' '; + HoldMosaicCharset=FirstG0; + } + } else if (ttc==0x0E) { + if (Size!=sizeDoubleWidth) { + Size=sizeDoubleWidth; + HoldMosaicChar=' '; + HoldMosaicCharset=FirstG0; + } + } else if (ttc==0x0E) { + if (Size!=sizeDoubleSize) { + Size=sizeDoubleSize; + HoldMosaicChar=' '; + HoldMosaicCharset=FirstG0; + } + } else if (ttc>=0x10 && ttc<=0x17) { + if (!GraphicCharset) { + // Actual switch to graphics charset + HoldMosaicChar=' '; + HoldMosaicCharset=FirstG0; + } + c.SetFGColor((enumTeletextColor)(ttc-0x10)); + c.SetCharset(SeparateGraphics?CHARSET_GRAPHICS_G1_SEP:CHARSET_GRAPHICS_G1); + GraphicCharset=true; + c.SetConceal(false); + } else if (ttc==0x1B) { + SecondCharset=!SecondCharset; + if (!GraphicCharset) c.SetCharset(SecondCharset?SecondG0:FirstG0); + } else if (ttc==0x1F) { + HoldMosaics=false; + } + + } // end for x + if (renderfirstlineonly) break; + } // end for y + + for (x=0;x<40;x++) { + // Clean out last line + cTeletextChar c; + c.SetFGColor(ttcWhite); + c.SetBGColor(ttcBlack); + c.SetCharset(FirstG0); + c.SetChar(' '); + if (flags&0x60) { + c.SetBoxedOut(true); + } + setChar(x,24,c); + } + for (y=0;y<25;y++) { + for (x=0;x<40;x++) { + if (isDirty(x,y)) { + dirty=true; + break; + } + if (dirty) break; + } + if (dirty) break; + } + + if (dirty && txtview!=NULL ) { + + if ( !renderfirstlineonly) { + Message* m= new Message(); + m->message = Message::TELETEXTUPDATE; + m->to = txtview; + m->from = this; + m->parameter = 0; + Command::getInstance()->postMessageFromOuterSpace(m); + } else if (firstlineupdate==10) { + Message* m= new Message(); + m->message = Message::TELETEXTUPDATEFIRSTLINE; + m->to = txtview; + m->from = this; + m->parameter = 0; + Command::getInstance()->postMessageFromOuterSpace(m); + firstlineupdate=0; + } else firstlineupdate++; + + + } + +} diff --git a/tfeed.cc b/tfeed.cc index d876a5d..040fe09 100644 --- a/tfeed.cc +++ b/tfeed.cc @@ -1,78 +1,54 @@ -/* - Copyright 2008 Marten Richter - - This file is part of VOMP. - +/* + Copyright 2008 Marten Richter + 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. - + (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. - + 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 "tfeed.h" - -#include "log.h" -#include "demuxer.h" -#include "callback.h" - -TFeed::TFeed(Callback* tcb) -: cb(*tcb) -{ - teletextEnabled = 1; -} - -int TFeed::init() -{ - return 1; -} - -int TFeed::shutdown() -{ - // FIXME - return 1; -} - -void TFeed::disable() -{ - teletextEnabled = 0; -} - +*/ + +#include "tfeed.h" +#include "log.h" #include "demuxer.h" #include "callback.h" + +TFeed::TFeed(Callback* tcb) : cb(*tcb) { teletextEnabled = 1; } + +int TFeed::init() { return 1; } + +int TFeed::shutdown() { + // FIXME return 1; } + +void TFeed::disable() +{ teletextEnabled = 0; +} + void TFeed::enable() -{ - teletextEnabled = 1; -} - +{ teletextEnabled = 1; +} + int TFeed::start() { teletextEnabled = 1; return threadStart(); -} - +} + void TFeed::stop() { threadCancel(); -} - +} + void TFeed::threadMethod() { - bool tlen; - - while(1) - { + bool tlen; + while(1) { threadCheckExit(); - - - tlen = Demuxer::getInstance()->writeTeletext(); - + tlen = Demuxer::getInstance()->writeTeletext(); if (tlen) { cb.call(this); @@ -83,6 +59,5 @@ void TFeed::threadMethod() //MILLISLEEP(100); MILLISLEEP(20); //Performance Issue Marten } - } -} +} \ No newline at end of file diff --git a/tfeed.h b/tfeed.h index 9206a64..1c153f1 100644 --- a/tfeed.h +++ b/tfeed.h @@ -1,55 +1,21 @@ -/* - Copyright 2008 Marten Richter - - This file is part of VOMP. - +/* Copyright 2008 Marten Richter + 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. - + (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. - + 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 TFEED_H -#define TFEED_H - -#include -#include - -#ifdef WIN32 -#include "threadwin.h" -#else -#include "threadp.h" -#endif - -class Callback; - -class TFeed : public Thread_TYPE -{ - public: - TFeed(Callback* tcb); - - int init(); - int shutdown(); - - int start(); - void stop(); - void enable(); - void disable(); - - private: - void threadMethod(); - void threadPostStopCleanup() {}; - int teletextEnabled; - Callback& cb; -}; - -#endif +*/ +#ifndef TFEED_H #define TFEED_H +#include #include +#include "threadsystem.h" +class Callback; +class TFeed: public Thread_TYPE { public: TFeed(Callback* tcb); int init(); int shutdown(); int start(); void stop(); void enable(); void disable(); private: void threadMethod(); void threadPostStopCleanup() { }; int teletextEnabled; Callback& cb; }; + +#endif diff --git a/threadp.cc b/threadp.cc index d383760..fdd7df1 100644 --- a/threadp.cc +++ b/threadp.cc @@ -51,6 +51,7 @@ void ThreadP::threadStop() this->threadPostStopCleanup(); } + void ThreadP::threadCancel() { threadActive = 0; @@ -59,6 +60,7 @@ void ThreadP::threadCancel() this->threadPostStopCleanup(); } + void ThreadP::threadCheckExit() { if (!threadActive) pthread_exit(NULL); @@ -102,6 +104,7 @@ void ThreadP::threadSetKillable() pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); } + pthread_t ThreadP::getThreadID() // returns the ID of this thread { return pthread; diff --git a/threadp.h b/threadp.h index 86f6b04..0fc2d34 100644 --- a/threadp.h +++ b/threadp.h @@ -42,6 +42,7 @@ class ThreadP : public Thread void threadSignalNoLock(); // same as above but without locking guarantees. probably not a good idea. // Methods to use from inside the thread + void threadSetKillable(); // allows threadCancel() to work void threadCheckExit(); // terminates thread if threadStop() has been called void threadWaitForSignal(); // pauses thread until threadSignal() is called diff --git a/timerreceiver.h b/timerreceiver.h old mode 100755 new mode 100644 diff --git a/timers.cc b/timers.cc old mode 100755 new mode 100644 diff --git a/timers.h b/timers.h old mode 100755 new mode 100644 index 34ab673..b879592 --- a/timers.h +++ b/timers.h @@ -80,11 +80,7 @@ Big Mutex. #include #include -#ifndef WIN32 -#include "threadp.h" -#else -#include "threadwin.h" -#endif +#include "threadsystem.h" #include "defines.h" diff --git a/udp.cc b/udp.cc old mode 100755 new mode 100644 index 086e4ab..0a775ea --- a/udp.cc +++ b/udp.cc @@ -84,7 +84,7 @@ void UDP::threadMethod() int retval; while(1) { - retval = ds->waitforMessage(0); + retval = ds->waitforMessage(1); if (retval == 0) { @@ -93,12 +93,14 @@ void UDP::threadMethod() } else if (retval == 1) { + threadCheckExit(); continue; } else { processRequest((UCHAR*)ds->getData(), ds->getDataLength()); } + threadCheckExit(); } } diff --git a/udp.h b/udp.h old mode 100755 new mode 100644 index cac6647..36b3043 --- a/udp.h +++ b/udp.h @@ -27,11 +27,7 @@ #include "defines.h" -#ifdef WIN32 -#include "threadwin.h" -#else -#include "threadp.h" -#endif +#include "threadsystem.h" class DatagramSocket; class MessageQueue; diff --git a/vchannellist.cc b/vchannellist.cc index a663fcc..3ec7713 100644 --- a/vchannellist.cc +++ b/vchannellist.cc @@ -64,6 +64,7 @@ VChannelList::VChannelList(ULONG type) VChannelList::~VChannelList() { + if (chanList) { for (UINT i = 0; i < chanList->size(); i++) @@ -236,7 +237,15 @@ int VChannelList::handleCommand(int command) void VChannelList::processMessage(Message* m) { - if (m->message == Message::MOUSE_MOVE) + /* if (m->message == Message::MOUSE_MOVE) { + if (sl.mouseAndroidScroll((m->tag >> 16),(m->tag & 0xFFFF), + (m->parameter >> 16),(m->parameter & 0xFFFF))) { + sl.draw(); + doShowingBar(); + boxstack->update(this); + } + } + else */if (m->message == Message::MOUSE_MOVE) { if (sl.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) { diff --git a/vcolourtuner.cc b/vcolourtuner.cc index bba1e1e..d79f7fd 100644 --- a/vcolourtuner.cc +++ b/vcolourtuner.cc @@ -1,283 +1,289 @@ -/* - Copyright 2004-2005 Chris Tallon, Andreas Vogel - - 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 "vcolourtuner.h" - -#include "wsymbol.h" -#include "remote.h" -#include "colour.h" -#include "video.h" -#include "vinfo.h" -#include "boxstack.h" -#include "i18n.h" -#include "log.h" -#include "mediaoptions.h" - -#define PICTUREFILE "/colourtest.jpg" - -int VColourTuner::rfactor=100; -int VColourTuner::gfactor=100; -int VColourTuner::bfactor=100; - -VColourTuner::VColourTuner() -{ - int sw= Video::getInstance()->getScreenWidth(); - int sh= Video::getInstance()->getScreenHeight(); - setSize(sw-80,sh-40); - setPosition((sw-area.w)/2, (sh-area.h)/2); - createBuffer(); - setTitleBarOn(0); - picture.setPosition(160,60); - add(&picture); - drawPicture=true; - vrfactor=rfactor; - vbfactor=bfactor; - vgfactor=gfactor; - hasChanged=false; - Log::getInstance()->log("VColourTuner",Log::DEBUG,"created %p",this); -} - -VColourTuner::~VColourTuner() -{ - Log::getInstance()->log("VColourTuner",Log::DEBUG,"deleted %p",this); -} - -void VColourTuner::drawBox(int x, int y, int w, int h, Colour &c) { - for (int row=y;rowdrawPixel(col,row,c); - } -} - - -void VColourTuner::draw() -{ - //do not call base classes draw to avoid drawing the picture... - Log::getInstance()->log("VColourTuner::draw",Log::DEBUG,"dp %s, rf=%d, gf=%d, bf=%d", - (drawPicture?"true":"false"),vrfactor,vgfactor,vbfactor); - char valbuf[20]; - int x=20; - int y=20; - int bw=50; - int bh=50; - int picx=picture.getX(); - Colour bc=Colour(140,140,140); - fillColour(bc); - bc=Colour(255,255,255); - drawText(tr("Colour Tuning"), x+20, y+5, Colour::LIGHTTEXT); - drawBox(x, y+50, bw, bh, Colour::RED); - drawBox(x, y+130, bw, bh, Colour::GREEN); - drawBox(x, y+190, bw, bh, Colour::BLUE); - drawBox(x, y+270, bw, bh, bc); - sprintf(valbuf,"%03d%%",vrfactor); - drawText(valbuf,x+bw+x,y+50, Colour::LIGHTTEXT); - drawText("<1 2>",x+bw+x,y+74, Colour::LIGHTTEXT); - sprintf(valbuf,"%03d%%",vgfactor); - drawText(valbuf,x+bw+x,y+120, Colour::LIGHTTEXT); - drawText("<4 5>",x+bw+x,y+144, Colour::LIGHTTEXT); - sprintf(valbuf,"%03d%%",vbfactor); - drawText(valbuf,x+bw+x,y+190, Colour::LIGHTTEXT); - drawText("<7 8>",x+bw+x,y+214, Colour::LIGHTTEXT); - sprintf(valbuf,"%03d%%",(vbfactor+vgfactor+vrfactor)/3); - drawText(valbuf,x+bw+x,y+270, Colour::LIGHTTEXT); - drawText("<3 6>",x+bw+x,y+294, Colour::LIGHTTEXT); - drawText("9 norm",x+bw+x,y+318, Colour::LIGHTTEXT); - drawText(tr("OK to save, BACK to cancel"), x+20, area.h - 50, Colour::LIGHTTEXT); - if (drawPicture) { - hasChanged=false; - picture.init(PICTUREFILE); - picture.draw(); - drawPicture=false; - } - int picy=picture.getY(); - int pich=picture.getHeight(); - if (hasChanged) drawText(tr("0 to draw picture"), picx+30, picy+pich+10, Colour::LIGHTTEXT); -} - -int VColourTuner::handleCommand(int command) -{ - int rt=0; - switch(command) { - case Remote::ONE: - updateFactor(1,-1); - rt=2; - hasChanged=true; - break; - case Remote::TWO: - updateFactor(1,1); - rt=2; - hasChanged=true; - break; - case Remote::FOUR: - updateFactor(2,-1); - rt=2; - hasChanged=true; - break; - case Remote::FIVE: - updateFactor(2,1); - rt=2; - hasChanged=true; - break; - case Remote::SEVEN: - updateFactor(3,-1); - rt=2; - hasChanged=true; - break; - case Remote::EIGHT: - updateFactor(3,1); - hasChanged=true; - rt=2; - break; - case Remote::THREE: - updateFactor(4,-1); - hasChanged=true; - rt=2; - break; - case Remote::SIX: - updateFactor(4,1); - hasChanged=true; - rt=2; - break; - case Remote::NINE: - updateFactor(5,0); - hasChanged=true; - rt=2; - break; - case Remote::ZERO: - drawPicture=true; - rt=2; - break; - case Remote::BACK: - vrfactor=rfactor; - vgfactor=gfactor; - vbfactor=bfactor; -#ifndef WIN32 -#ifndef _MIPS_ARCH - ((Surface_TYPE *)surface)->initConversionTables(vrfactor,vgfactor,vbfactor); -#endif -#endif - rt=4; - break; - case Remote::OK: - rfactor=vrfactor; - gfactor=vgfactor; - bfactor=vbfactor; - MediaOptions::getInstance()->setIntOption("FactorRed",rfactor); - MediaOptions::getInstance()->setIntOption("FactorGreen",gfactor); - MediaOptions::getInstance()->setIntOption("FactorBlue",bfactor); - rt=4; - break; - } - if (rt == 2) { -#ifndef WIN32 -#ifndef _MIPS_ARCH - ((Surface_TYPE *)surface)->initConversionTables(vrfactor,vgfactor,vbfactor); -#endif -#endif - bool updateAll=drawPicture; - draw(); - if (updateAll) { - BoxStack::getInstance()->update(this); - } - else { - Region r; - r.x=0; - r.w=picture.getX()-1; - r.y=0; - r.h=area.h; - BoxStack::getInstance()->update(this,&r); - r.x=picture.getX(); - r.y=picture.getY(); - r.h=area.h-r.y; - r.w=area.w-picture.getX(); - BoxStack::getInstance()->update(this,&r); - } - } - return rt; -} - - - -void VColourTuner::processMessage(Message* m) -{ - if (m->message == Message::MOUSE_MOVE) - { - - } - else if (m->message == Message::MOUSE_LBDOWN) - { - //check if press is outside this view! then simulate cancel - int x=(m->parameter>>16)-getScreenX(); - int y=(m->parameter&0xFFFF)-getScreenY(); - if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight()) - { - BoxStack::getInstance()->handleCommand(Remote::BACK); //simulate cancel press - } - else if (y>=(int)area.h-24 && y<=(int)area.h-6) - { - ; - } - } -} - -void VColourTuner::updateFactor(int color, int amount) { - switch (color) { - case 1: - vrfactor+=amount; - if (vrfactor < 20 ) vrfactor=20; - if (vrfactor > 200) vrfactor=200; - break; - case 2: - vgfactor+=amount; - if (vgfactor < 20 ) vgfactor=20; - if (vgfactor > 200) vgfactor=200; - break; - case 3: - vbfactor+=amount; - if (vbfactor < 20 ) vbfactor=20; - if (vbfactor > 200) vbfactor=200; - break; - case 4: - updateFactor(1,amount); - updateFactor(2,amount); - updateFactor(3,amount); - break; - case 5: - while ((vrfactor+vbfactor+vgfactor) > 300) updateFactor(4,-1); - while ((vrfactor+vbfactor+vgfactor) < 300) updateFactor(4,1); - } -} - -void VColourTuner::initFactors(){ - MediaOptions * options=MediaOptions::getInstance(); - int rf=options->getIntOption("FactorRed"); - int gf=options->getIntOption("FactorGreen"); - int bf=options->getIntOption("FactorBlue"); - if (rf >= 20 && bf >= 20 && gf >= 20) - rfactor=rf; - gfactor=gf; - bfactor=bf; - Log::getInstance()->log("VColourTuner",Log::DEBUG,"setting initial factors r=%d,g=%d,b=%d",rf,gf,bf); -#ifndef WIN32 -#ifndef _MIPS_ARCH - Surface_TYPE::initConversionTables(rfactor,gfactor,bfactor); -#endif -#endif -} +/* + Copyright 2004-2005 Chris Tallon, Andreas Vogel + + 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 "vcolourtuner.h" + +#include "wsymbol.h" +#include "remote.h" +#include "colour.h" +#include "video.h" +#include "vinfo.h" +#include "boxstack.h" +#include "i18n.h" +#include "log.h" +#include "mediaoptions.h" + +#define PICTUREFILE "/colourtest.jpg" + +int VColourTuner::rfactor=100; +int VColourTuner::gfactor=100; +int VColourTuner::bfactor=100; + +VColourTuner::VColourTuner() +{ + int sw= Video::getInstance()->getScreenWidth(); + int sh= Video::getInstance()->getScreenHeight(); + setSize(sw-80,sh-40); + setPosition((sw-area.w)/2, (sh-area.h)/2); + createBuffer(); + setTitleBarOn(0); + picture.setPosition(160,60); + add(&picture); + drawPicture=true; + vrfactor=rfactor; + vbfactor=bfactor; + vgfactor=gfactor; + hasChanged=false; + Log::getInstance()->log("VColourTuner",Log::DEBUG,"created %p",this); +} + +VColourTuner::~VColourTuner() +{ + Log::getInstance()->log("VColourTuner",Log::DEBUG,"deleted %p",this); +} + +void VColourTuner::drawBox(int x, int y, int w, int h, Colour &c) { + for (int row=y;rowdrawPixel(col,row,c); + } +} + + +void VColourTuner::draw() +{ + //do not call base classes draw to avoid drawing the picture... + Log::getInstance()->log("VColourTuner::draw",Log::DEBUG,"dp %s, rf=%d, gf=%d, bf=%d", + (drawPicture?"true":"false"),vrfactor,vgfactor,vbfactor); + char valbuf[20]; + int x=20; + int y=20; + int bw=50; + int bh=50; + int picx=picture.getX(); + Colour bc=Colour(140,140,140); + fillColour(bc); + bc=Colour(255,255,255); + drawText(tr("Colour Tuning"), x+20, y+5, Colour::LIGHTTEXT); + drawBox(x, y+50, bw, bh, Colour::RED); + drawBox(x, y+130, bw, bh, Colour::GREEN); + drawBox(x, y+190, bw, bh, Colour::BLUE); + drawBox(x, y+270, bw, bh, bc); + sprintf(valbuf,"%03d%%",vrfactor); + drawText(valbuf,x+bw+x,y+50, Colour::LIGHTTEXT); + drawText("<1 2>",x+bw+x,y+74, Colour::LIGHTTEXT); + sprintf(valbuf,"%03d%%",vgfactor); + drawText(valbuf,x+bw+x,y+120, Colour::LIGHTTEXT); + drawText("<4 5>",x+bw+x,y+144, Colour::LIGHTTEXT); + sprintf(valbuf,"%03d%%",vbfactor); + drawText(valbuf,x+bw+x,y+190, Colour::LIGHTTEXT); + drawText("<7 8>",x+bw+x,y+214, Colour::LIGHTTEXT); + sprintf(valbuf,"%03d%%",(vbfactor+vgfactor+vrfactor)/3); + drawText(valbuf,x+bw+x,y+270, Colour::LIGHTTEXT); + drawText("<3 6>",x+bw+x,y+294, Colour::LIGHTTEXT); + drawText("9 norm",x+bw+x,y+318, Colour::LIGHTTEXT); + drawText(tr("OK to save, BACK to cancel"), x+20, area.h - 50, Colour::LIGHTTEXT); + if (drawPicture) { + hasChanged=false; + picture.init(PICTUREFILE); + picture.draw(); + drawPicture=false; + } + int picy=picture.getY(); + int pich=picture.getHeight(); + if (hasChanged) drawText(tr("0 to draw picture"), picx+30, picy+pich+10, Colour::LIGHTTEXT); +} + +int VColourTuner::handleCommand(int command) +{ + int rt=0; + switch(command) { + case Remote::ONE: + updateFactor(1,-1); + rt=2; + hasChanged=true; + break; + case Remote::TWO: + updateFactor(1,1); + rt=2; + hasChanged=true; + break; + case Remote::FOUR: + updateFactor(2,-1); + rt=2; + hasChanged=true; + break; + case Remote::FIVE: + updateFactor(2,1); + rt=2; + hasChanged=true; + break; + case Remote::SEVEN: + updateFactor(3,-1); + rt=2; + hasChanged=true; + break; + case Remote::EIGHT: + updateFactor(3,1); + hasChanged=true; + rt=2; + break; + case Remote::THREE: + updateFactor(4,-1); + hasChanged=true; + rt=2; + break; + case Remote::SIX: + updateFactor(4,1); + hasChanged=true; + rt=2; + break; + case Remote::NINE: + updateFactor(5,0); + hasChanged=true; + rt=2; + break; + case Remote::ZERO: + drawPicture=true; + rt=2; + break; + case Remote::BACK: + vrfactor=rfactor; + vgfactor=gfactor; + vbfactor=bfactor; +#ifndef WIN32 +#ifndef _MIPS_ARCH +#ifndef __ANDROID__ + ((Surface_TYPE *)surface)->initConversionTables(vrfactor,vgfactor,vbfactor); +#endif +#endif +#endif + rt=4; + break; + case Remote::OK: + rfactor=vrfactor; + gfactor=vgfactor; + bfactor=vbfactor; + MediaOptions::getInstance()->setIntOption("FactorRed",rfactor); + MediaOptions::getInstance()->setIntOption("FactorGreen",gfactor); + MediaOptions::getInstance()->setIntOption("FactorBlue",bfactor); + rt=4; + break; + } + if (rt == 2) { +#ifndef WIN32 +#ifndef _MIPS_ARCH +#ifndef __ANDROID__ + ((Surface_TYPE *)surface)->initConversionTables(vrfactor,vgfactor,vbfactor); +#endif +#endif +#endif + bool updateAll=drawPicture; + draw(); + if (updateAll) { + BoxStack::getInstance()->update(this); + } + else { + Region r; + r.x=0; + r.w=picture.getX()-1; + r.y=0; + r.h=area.h; + BoxStack::getInstance()->update(this,&r); + r.x=picture.getX(); + r.y=picture.getY(); + r.h=area.h-r.y; + r.w=area.w-picture.getX(); + BoxStack::getInstance()->update(this,&r); + } + } + return rt; +} + + + +void VColourTuner::processMessage(Message* m) +{ + if (m->message == Message::MOUSE_MOVE) + { + + } + else if (m->message == Message::MOUSE_LBDOWN) + { + //check if press is outside this view! then simulate cancel + int x=(m->parameter>>16)-getScreenX(); + int y=(m->parameter&0xFFFF)-getScreenY(); + if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight()) + { + BoxStack::getInstance()->handleCommand(Remote::BACK); //simulate cancel press + } + else if (y>=(int)area.h-24 && y<=(int)area.h-6) + { + ; + } + } +} + +void VColourTuner::updateFactor(int color, int amount) { + switch (color) { + case 1: + vrfactor+=amount; + if (vrfactor < 20 ) vrfactor=20; + if (vrfactor > 200) vrfactor=200; + break; + case 2: + vgfactor+=amount; + if (vgfactor < 20 ) vgfactor=20; + if (vgfactor > 200) vgfactor=200; + break; + case 3: + vbfactor+=amount; + if (vbfactor < 20 ) vbfactor=20; + if (vbfactor > 200) vbfactor=200; + break; + case 4: + updateFactor(1,amount); + updateFactor(2,amount); + updateFactor(3,amount); + break; + case 5: + while ((vrfactor+vbfactor+vgfactor) > 300) updateFactor(4,-1); + while ((vrfactor+vbfactor+vgfactor) < 300) updateFactor(4,1); + } +} + +void VColourTuner::initFactors(){ + MediaOptions * options=MediaOptions::getInstance(); + int rf=options->getIntOption("FactorRed"); + int gf=options->getIntOption("FactorGreen"); + int bf=options->getIntOption("FactorBlue"); + if (rf >= 20 && bf >= 20 && gf >= 20) + rfactor=rf; + gfactor=gf; + bfactor=bf; + Log::getInstance()->log("VColourTuner",Log::DEBUG,"setting initial factors r=%d,g=%d,b=%d",rf,gf,bf); +#ifndef __ANDROID__ +#ifndef WIN32 +#ifndef _MIPS_ARCH + Surface_TYPE::initConversionTables(rfactor,gfactor,bfactor); +#endif +#endif +#endif +} diff --git a/vconnect.cc b/vconnect.cc index 9666156..7c5e427 100644 --- a/vconnect.cc +++ b/vconnect.cc @@ -143,6 +143,7 @@ void VConnect::threadMethod() Wol::getInstance()->setWakeUpIP(servers[selectedServer].ip); vdr->setServerIP(servers[selectedServer].ip); + // Clear the serverIPs vector for(UINT k = 0; k < servers.size(); k++) { @@ -151,6 +152,7 @@ void VConnect::threadMethod() } servers.clear(); + setOneLiner(tr("Connecting to VDR")); draw(); boxstack->update(this); @@ -158,7 +160,7 @@ void VConnect::threadMethod() success = vdr->connect(); if (success) { - logger->log("Command", Log::DEBUG, "Connected ok, doing login"); + logger->log("VConnect", Log::DEBUG, "Connected ok, doing login"); success = vdr->doLogin(); if (!success) @@ -180,6 +182,7 @@ void VConnect::threadMethod() } while(!success); + Message* m = new Message(); // Must be done after this thread ends m->from = this; m->to = Command::getInstance(); diff --git a/vdr.cc b/vdr.cc index 3073697..b18cac0 100644 --- a/vdr.cc +++ b/vdr.cc @@ -1,1442 +1,1497 @@ -/* - Copyright 2004-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 "vdr.h" - -#include "recman.h" -#include "tcp.h" -#include "log.h" -#include "recinfo.h" -#include "dsock.h" -#include "channel.h" -#include "event.h" -#include "wol.h" -#include "vdrrequestpacket.h" -#include "vdrresponsepacket.h" -#include "command.h" -#include "media.h" -#include "mediaprovider.h" -#include "mediaproviderids.h" -#include "vdrcommand.h" -#include "video.h" - -VDR* VDR::instance = NULL; -//prepare a request -//will create a request package from a command variable and fill this -//caller has to destroy buffer -static SerializeBuffer * prepareRequest(VDR_Command *cmd) { - SerializeBuffer *buf=new SerializeBuffer(512,false,true); - if (cmd->serialize(buf) != 0) { - delete buf; - return NULL; - } - return buf; -} - -//handle request/response -//TODO avoid copy of buffer (needs extension of requestpacket) -//TODO avoid command 2x (needs some more restructuring) -SerializeBuffer * VDR::doRequestResponse(SerializeBuffer *rq,int cmd) { - VDR_RequestPacket *rt=new VDR_RequestPacket; - if (! rt) { - delete rq; - return NULL; - } - if (! rt->init(cmd,true,rq->getCurrent()-rq->getStart())) { - delete rq; - delete rt; - return NULL; - } - if (! rt->copyin(rq->getStart(),(ULONG)(rq->getCurrent()-rq->getStart()))) { - delete rq; - delete rt; - return NULL; - } - delete rq; - VDR_ResponsePacket *rp=RequestResponse(rt); - logger->log("doRequestResponse",Log::DEBUG,"got response %p",rp); - if ( !rp) { - delete rt; - return NULL; - } - SerializeBuffer *buf=new SerializeBuffer(rp->getUserData(),rp->getUserDataLength(),true,true,false); - delete rp; - return buf; -} - - - -//deserialize a received response -//delete the package -//return !=0 on error -static int decodeResponse(SerializeBuffer *rp,VDR_Command *c) { - ULONG expected=c->command; - if (c->deserialize(rp) != 0) { - delete rp; - Log::getInstance()->log("VDR", Log::ERR, "decodeResponse unable to deserialize for command %lu",expected); - return -1; - } - delete rp; - if (c->command != expected) { - Log::getInstance()->log("VDR", Log::ERR, "decodeResponse unexpected response received 0x%lx, expected 0x%lx",c->command,expected); - return -1; - } - Log::getInstance()->log("VDR", Log::DEBUG, "decodeResponse successfully decoded command 0x%lx",expected); - return 0; -} - - - -VDR::VDR() -{ - if (instance) return; - instance = this; - initted = 0; - findingServer = 0; - tcp = NULL; - connected = false; - maxChannelNumber = 0; - channelNumberWidth = 1; - TEMP_SINGLE_VDR_PR = NULL; - providerId=MPROVIDERID_VDR; - subRange=MPROVIDERRANGE_VDR; - MediaPlayerRegister::getInstance()->registerMediaProvider(this,MPROVIDERID_VDR,MPROVIDERRANGE_VDR); -} - -VDR::~VDR() -{ - instance = NULL; - if (initted) shutdown(); -} - -VDR* VDR::getInstance() -{ - return instance; -} - -int VDR::init(int tport) -{ - if (initted) return 0; - initted = 1; - port = tport; - logger = Log::getInstance(); - return 1; -} - -int VDR::shutdown() -{ - if (!initted) return 0; - initted = 0; - disconnect(); - return 1; -} - -void VDR::findServers(vector& servers) -{ - Wol* wol = Wol::getInstance(); - findingServer = 1; - char* message = "VOMP"; - - DatagramSocket ds(port); - int haveAtLeastOne = 0; - int retval; - int waitType = 1; - bool firstloop = true; - while(findingServer) - { - if (waitType == 1) - { - ds.shutdown(); - ds.init(); - logger->log("VDR", Log::NOTICE, "Broadcasting for server"); - ds.send("255.255.255.255", 3024, message, strlen(message)); - if(!firstloop) wol->doWakeUp(); - } - retval = ds.waitforMessage(waitType); - - if (retval == 2) // we got a reply - { - if (!strcmp(ds.getData(), "VOMP")) // echo..... - { - waitType = 2; - } - else - { - VDRServer newServer; - newServer.ip = new char[16]; - strcpy(newServer.ip, ds.getFromIPA()); - - if (ds.getDataLength() == 0) - { - newServer.name = new char[1]; - newServer.name[0] = '\0'; - } - else - { - newServer.name = new char[strlen(ds.getData())+1]; - strcpy(newServer.name, ds.getData()); - } - - servers.push_back(newServer); - waitType = 2; - haveAtLeastOne = 1; - } - } - else - { - if (haveAtLeastOne) break; - waitType = 1; - firstloop = false; - } - } - sort(servers.begin(), servers.end(), ServerSorter()); -} - -void VDR::cancelFindingServer() -{ - findingServer = 0; -} - -void VDR::setServerIP(char* newIP) -{ - strcpy(serverIP, newIP); -} - -int VDR::connect() -{ - maxChannelNumber = 0; - channelNumberWidth = 1; - - if (tcp) delete tcp; - tcp = new TCP(); - if (tcp->connectTo(serverIP, 3024)) - { - connected = true; - threadStart(); - return 1; - } - else - { - return 0; - } -} - -void VDR::disconnect() -{ - threadCancel(); - if (tcp) delete tcp; - tcp = NULL; - connected = false; - logger->log("VDR", Log::DEBUG, "Disconnect"); -} - -void VDR::setReceiveWindow(size_t size) -{ - if (connected) tcp->setReceiveWindow(size); -} - -/////////////////////////////////////////////////////// - -void VDR::threadMethod() -{ - logger->log("VDR", Log::DEBUG, "VDR RUN"); - - threadSetKillable(); // FIXME - change this to deal with the EDRs - - ULONG channelID; - - ULONG requestID; - ULONG userDataLength; - UCHAR* userData; - - ULONG streamID; - ULONG flag; - - VDR_ResponsePacket* vresp; - - ULONG timeNow = 0; - ULONG lastKAsent = 0; - ULONG lastKArecv = time(NULL); - int readSuccess; - - while(1) - { - timeNow = time(NULL); - - readSuccess = tcp->readData((UCHAR*)&channelID, sizeof(ULONG)); // 2s timeout atm - - if (!readSuccess) - { - //logger->log("VDR", Log::DEBUG, "Net read timeout"); - if (!tcp->isConnected()) { connectionDied(); return; } // return to stop this thread - } - - // Error or timeout. - - if (!lastKAsent) // have not sent a KA - { - if (lastKArecv < (timeNow - 5)) - { - logger->log("VDR", Log::DEBUG, "Sending KA packet"); - if (!sendKA(timeNow)) - { - logger->log("VDR", Log::DEBUG, "Could not send KA, calling connectionDied"); - connectionDied(); - return; - } - lastKAsent = timeNow; - } - } - else - { - if (lastKAsent <= (timeNow - 10)) - { - logger->log("VDR", Log::DEBUG, "lastKA over 10s ago, calling connectionDied"); - connectionDied(); - return; - } - } - - if (!readSuccess) continue; // no data was read but the connection is ok. - - // Data was read - - channelID = ntohl(channelID); - - if (channelID == CHANNEL_REQUEST_RESPONSE) - { - if (!tcp->readData((UCHAR*)&requestID, sizeof(ULONG))) break; - requestID = ntohl(requestID); - if (!tcp->readData((UCHAR*)&userDataLength, sizeof(ULONG))) break; - userDataLength = ntohl(userDataLength); - if (userDataLength > 5000000) break; // how big can these packets get? - userData = NULL; - if (userDataLength > 0) - { - userData = (UCHAR*)malloc(userDataLength); - if (!userData) break; - if (!tcp->readData(userData, userDataLength)) break; - } - - vresp = new VDR_ResponsePacket(); - vresp->setResponse(requestID, userData, userDataLength); - logger->log("VDR", Log::DEBUG, "Rxd a response packet, requestID=%lu, len=%lu", requestID, userDataLength); - - if (!edFindAndCall(vresp)) // makes ED lock, find receiver for vresp (using ed_cb_find() ) and then call (using ed_cb_call() ) - { - // If edFindAndCall returns true, edr was called and vresp was handed off. - // else, delete vresp here. - delete vresp; - } - } - else if (channelID == CHANNEL_STREAM) - { - if (!tcp->readData((UCHAR*)&streamID, sizeof(ULONG))) break; - streamID = ntohl(streamID); - - if (!tcp->readData((UCHAR*)&flag, sizeof(ULONG))) break; - flag = ntohl(flag); - - if (!tcp->readData((UCHAR*)&userDataLength, sizeof(ULONG))) break; - userDataLength = ntohl(userDataLength); - userData = NULL; - if (userDataLength > 0) - { - userData = (UCHAR*)malloc(userDataLength); - if (!userData) break; - if (!tcp->readData(userData, userDataLength)) break; - } - - vresp = new VDR_ResponsePacket(); - vresp->setStream(streamID, flag, userData, userDataLength); -// logger->log("VDR", Log::DEBUG, "Rxd a stream packet, streamID=%lu, flag=%lu, len=%lu", streamID, flag, userDataLength); - - if (!edFindAndCall(vresp)) // makes ED lock, find receiver for vresp (using ed_cb_find() ) and then call (using ed_cb_call() ) - { - // If edFindAndCall returns true, edr was called and vresp was handed off. - // else, delete vresp here. - delete vresp; - } - } - else if (channelID == CHANNEL_KEEPALIVE) - { - ULONG KAreply = 0; - if (!tcp->readData((UCHAR*)&KAreply, sizeof(ULONG))) break; - KAreply = (ULONG)ntohl(KAreply); - if (KAreply == lastKAsent) // successful KA response - { - lastKAsent = 0; - lastKArecv = KAreply; - logger->log("VDR", Log::DEBUG, "Rxd correct KA reply"); - } - } - else - { - logger->log("VDR", Log::ERR, "Rxd a response packet on channel %lu !!", channelID); - break; - } - - // Who deletes vresp? - // If RR, the individual protocol functions must delete vresp. - // If stream, the data and length is taken out in ed_cb_call and vresp is deleted there. - } - - connectionDied(); -} - -void VDR::connectionDied() -{ - // Called from within threadMethod to do cleanup if it decides the connection has died - - connected = false; // though actually it could still be connected until someone calls vdr->disconnect - - // Need to wake up any waiting channel 1 request-response threads - // Normally this is done by a packet coming in with channelid and requestid - // Instead, go through the list and for each channel 1 edr, make an empty vresp - // An empty vresp will have userData == NULL, which means vresp->noResponse() == true - - // If it's a stream receiver, generate a stream packet with flag == connection_lost - - edLock(); - VDR_PacketReceiver* vdrpr; - VDR_ResponsePacket* vresp; - while(receivers.size()) - { - vdrpr = (VDR_PacketReceiver*) *(receivers.begin()); - if (vdrpr->receiverChannel == CHANNEL_REQUEST_RESPONSE) - { - vresp = new VDR_ResponsePacket(); - vresp->setResponse(vdrpr->requestSerialNumber, NULL, 0); - logger->log("VDR", Log::DEBUG, "Timeouts: created blank response packet for request serial %lu", vdrpr->requestSerialNumber); - edUnlock(); - if (!edFindAndCall(vresp)) // makes ED lock, find receiver for vresp (using ed_cb_find() ) and then call (using ed_cb_call() ) - { - // If edFindAndCall returns true, edr was called and vresp was handed off. - // else, delete vresp here. - logger->log("VDR", Log::ERR, "Timeouts: no waiting thread found for request serial %lu !!!", vdrpr->requestSerialNumber); - delete vresp; - } - edLock(); - } - else if (vdrpr->receiverChannel == CHANNEL_STREAM) - { - vresp = new VDR_ResponsePacket(); - vresp->setStream(vdrpr->streamID, 2 /* connection-lost flag */ , NULL, 0); - logger->log("VDR", Log::DEBUG, "Timeouts: created blank response packet for streamid %lu", vdrpr->streamID); - edUnlock(); - if (!edFindAndCall(vresp)) // makes ED lock, find receiver for vresp (using ed_cb_find() ) and then call (using ed_cb_call() ) - { - // If edFindAndCall returns true, edr was called and vresp was handed off. - // else, delete vresp here. - logger->log("VDR", Log::ERR, "Timeouts: no waiting stream receiver found for streamid %lu !!!", vdrpr->streamID); - delete vresp; - } - edLock(); - - for(EDRL::iterator i = receivers.begin(); i != receivers.end(); i++) - if ((VDR_PacketReceiver*)*i == vdrpr) { receivers.erase(i); break; } - } - } - edUnlock(); - // Ok, all event receviers should be dealt with. just in case there weren't any, inform command - logger->log("VDR", Log::DEBUG, "edUnlock at end of connectionDied"); - - Command::getInstance()->connectionLost(); -} - -bool VDR::ed_cb_find(EDReceiver* edr, void* userTag) -{ - // edr is a VDR_PacketReceiver object made in VDR::RequestResponse - // userTag is a VDR_ResponsePacket made in threadMethod - - VDR_PacketReceiver* vdrpr = (VDR_PacketReceiver*)edr; - VDR_ResponsePacket* vresp = (VDR_ResponsePacket*)userTag; - - // Is vresp for vdrpr ? - - ULONG packetChannel = vresp->getChannelID(); - if (vdrpr->receiverChannel != packetChannel) return false; - - if (packetChannel == CHANNEL_REQUEST_RESPONSE) - { - if (vdrpr->requestSerialNumber == vresp->getRequestID()) return true; - } - else if (packetChannel == CHANNEL_STREAM) - { - if (vdrpr->streamID == vresp->getStreamID()) return true; - } - - return false; -} - -VDR_ResponsePacket* VDR::RequestResponse(VDR_RequestPacket* vrp) -{ - //logger->log("VDR", Log::DEBUG, "RR %lu", vrp->getOpcode()); - - if (!connected) - { - logger->log("VDR", Log::DEBUG, "RR when !connected"); - VDR_ResponsePacket* vresp = new VDR_ResponsePacket(); - return vresp; // "no-response" return - } - - // ED make new VDR and register - // make a VDR_PacketReceiver - // - init with serial number of request packet - - VDR_PacketReceiver vdrpr; -// vdrpr.requestTime = time(NULL); - vdrpr.receiverChannel = VDR::CHANNEL_REQUEST_RESPONSE; - vdrpr.requestSerialNumber = vrp->getSerial(); - - edRegister(&vdrpr); - - edLock(); - - if ((ULONG)tcp->sendData(vrp->getPtr(), vrp->getLen()) != vrp->getLen()) - { - edUnlock(); - edUnregister(&vdrpr); - VDR_ResponsePacket* vresp = new VDR_ResponsePacket(); - return vresp; // "no-response" return - } - - // Sleep and block this thread. The sleep unlocks the mutex - logger->log("VDR", Log::DEBUG, "RR sleep - opcode %lu", vrp->getOpcode()); - edSleepThisReceiver(&vdrpr); - logger->log("VDR", Log::DEBUG, "RR unsleep"); - - // Woken because a response packet has arrived, mutex will be locked - - edUnlock(); - return vdrpr.save_vresp; -} - -bool VDR::sendKA(ULONG timeStamp) -{ - char buffer[8]; - *(ULONG*)&buffer[0] = htonl(CHANNEL_KEEPALIVE); - *(ULONG*)&buffer[4] = htonl(timeStamp); - if ((ULONG)tcp->sendData(buffer, 8) != 8) return false; - return true; -} - -///////////////////////////////////////////////////////////////////////////// - -// Here VDR takes a break for the VDR_PacketReceiver helper class - -bool VDR_PacketReceiver::call(void* userTag) -{ - if (receiverChannel == VDR::CHANNEL_REQUEST_RESPONSE) - { - // It's a RR. Save vresp and, signal the waiting thread and return. - // VDR::RequestResponse will be blocking waiting for this to happen. - // That function has a pointer to this object and can read save_vresp. - save_vresp = (VDR_ResponsePacket*)userTag; - return true; // Signals ED to remove edr from receivers and wake up edr thread - } - - if (receiverChannel == VDR::CHANNEL_STREAM) - { - // It's a stream packet. - VDR_ResponsePacket* vresp = (VDR_ResponsePacket*)userTag; - streamReceiver->streamReceive(vresp->getFlag(), vresp->getUserData(), vresp->getUserDataLength()); - delete vresp; - return false; - } - - abort(); // unknown receiverChannel, should not happen -} - -///////////////////////////////////////////////////////////////////////////// - -int VDR::doLogin() -{ - VDR_RequestPacket vrp; - if (!vrp.init(VDR_LOGIN, true, 6)) return 0; - - char* mactemp[6]; - tcp->getMAC((char*)mactemp); - if (!vrp.copyin((UCHAR*)mactemp, 6)) return 0; - - VDR_ResponsePacket* vresp = RequestResponse(&vrp); - if (vresp->noResponse()) { delete vresp; return 0; } - - ULONG vdrTime = vresp->extractULONG(); - logger->log("VDR", Log::DEBUG, "vdrtime = %lu", vdrTime); - long vdrTimeOffset = vresp->extractLONG(); - logger->log("VDR", Log::DEBUG, "offset = %i", vdrTimeOffset); - - delete vresp; - - // Set the time and zone on the MVP - -#ifndef WIN32 - struct timespec currentTime; - currentTime.tv_sec = vdrTime; - currentTime.tv_nsec = 0; - int b = clock_settime(CLOCK_REALTIME, ¤tTime); - - logger->log("VDR", Log::DEBUG, "set clock = %u", b); - - // now make a TZ variable and set it - char sign; - int hours; - int minutes; - if (vdrTimeOffset > 0) sign = '-'; - else sign = '+'; - - vdrTimeOffset = abs(vdrTimeOffset); - - hours = (int)vdrTimeOffset / 3600; - minutes = vdrTimeOffset % 3600; - - logger->log("VDR", Log::DEBUG, "%c %i %i", sign, hours, minutes); - - minutes = (int)minutes / 60; - - logger->log("VDR", Log::DEBUG, "%c %i %i", sign, hours, minutes); - - char newTZ[30]; - sprintf(newTZ, "MVP%c%i:%i", sign, hours, minutes); - setenv("TZ", newTZ, 1); - - logger->log("VDR", Log::DEBUG, "Timezone data: %s", newTZ); -#endif - - return 1; -} - -bool VDR::networkLog(const char* logString) -{ - if (!connected) return false; - int stringLength = strlen(logString); - int packetLength = stringLength + 8; - char *buffer=new char[packetLength + 1]; - *(ULONG*)&buffer[0] = htonl(CHANNEL_NETLOG); - *(ULONG*)&buffer[4] = htonl(stringLength); - strcpy(&buffer[8], logString); - - if ((ULONG)tcp->sendData(buffer, packetLength) != packetLength) { - delete [] buffer; - return false; - } - delete [] buffer; - return true; -} - -bool VDR::getRecordingsList(RecMan* recman) -{ - VDR_RequestPacket vrp; - if (!vrp.init(VDR_GETRECORDINGLIST, true, 0)) return false; - - VDR_ResponsePacket* vresp = RequestResponse(&vrp); - if (vresp->noResponse()) { delete vresp; return false; } - - ULONG totalSpace = vresp->extractULONG(); - ULONG freeSpace = vresp->extractULONG(); - ULONG percent = vresp->extractULONG(); - recman->setStats(totalSpace, freeSpace, percent); - - ULONG start; - char* name; - char* fileName; - - while (!vresp->end()) - { - start = vresp->extractULONG(); - name = vresp->extractString(); - fileName = vresp->extractString(); - - recman->addEntry(start, name, fileName); - - delete[] name; - delete[] fileName; - } - - delete vresp; - - return true; -} - -int VDR::deleteRecording(char* fileName) -{ - VDR_RequestPacket vrp; - if (!vrp.init(VDR_DELETERECORDING, true, strlen(fileName) + 1)) return 0; - if (!vrp.addString(fileName)) return 0; - - VDR_ResponsePacket* vresp = RequestResponse(&vrp); - if (vresp->noResponse()) { delete vresp; return 0; } - - int toReturn = (int)vresp->extractULONG(); - delete vresp; - - return toReturn; -} - -char* VDR::moveRecording(char* fileName, char* newPath) -{ - VDR_RequestPacket vrp; - if (!vrp.init(VDR_MOVERECORDING, true, strlen(fileName) + 1 + strlen(newPath) + 1)) return NULL; - if (!vrp.addString(fileName)) return NULL; - if (!vrp.addString(newPath)) return NULL; - - VDR_ResponsePacket* vresp = RequestResponse(&vrp); - if (vresp->noResponse()) { delete vresp; return NULL; } - - char* toReturn = NULL; - int success = (int)vresp->extractULONG(); - if (success == 1) - { - toReturn = vresp->extractString(); - } - - delete vresp; - - return toReturn; -} - -ChannelList* VDR::getChannelsList(ULONG type) -{ - VDR_RequestPacket vrp; - if (!vrp.init(VDR_GETCHANNELLIST, true, 0)) return NULL; - - VDR_ResponsePacket* vresp = RequestResponse(&vrp); - if (vresp->noResponse()) { delete vresp; return NULL; } - - ChannelList* chanList = new ChannelList(); - - bool h264support=Video::getInstance()->supportsh264(); - - while (!vresp->end()) - { - Channel* chan = new Channel(); - chan->number = vresp->extractULONG(); - chan->type = vresp->extractULONG(); - chan->name = vresp->extractString(); - chan->vstreamtype = vresp->extractULONG(); - - if (chan->type == type && (chan->vstreamtype!=0x1b || h264support)) - { - chanList->push_back(chan); - logger->log("VDR", Log::DEBUG, "Have added a channel to list. %lu %lu %s", chan->number, chan->type, chan->name); - if (chan->number > maxChannelNumber) maxChannelNumber = chan->number; - } - else - { - delete chan; - } - } - - delete vresp; - - if (maxChannelNumber > 99999) - channelNumberWidth = 6; - else if (maxChannelNumber > 9999) - channelNumberWidth = 5; - else if (maxChannelNumber > 999) - channelNumberWidth = 4; - else if (maxChannelNumber > 99) - channelNumberWidth = 3; - else if (maxChannelNumber > 9) - channelNumberWidth = 2; - else - channelNumberWidth = 1; - - return chanList; -} - -int VDR::streamChannel(ULONG number, StreamReceiver* tstreamReceiver) -{ - VDR_RequestPacket vrp; - if (!vrp.init(VDR_STREAMCHANNEL, true, sizeof(ULONG))) return 0; - if (!vrp.addULONG(number)) return 0; - - - VDR_PacketReceiver* vdrpr = new VDR_PacketReceiver(); - vdrpr->receiverChannel = VDR::CHANNEL_STREAM; - vdrpr->streamID = vrp.getSerial(); - vdrpr->streamReceiver = tstreamReceiver; - edRegister(vdrpr); - TEMP_SINGLE_VDR_PR = vdrpr; - - VDR_ResponsePacket* vresp = RequestResponse(&vrp); - if (vresp->noResponse()) - { - delete vresp; - edUnregister(vdrpr); - delete vdrpr; - return 0; - } - - int toReturn = (int)vresp->extractULONG(); - logger->log("VDR", Log::DEBUG, "VDR said %lu to start streaming request", toReturn); - delete vresp; - - return toReturn; -} - -int VDR::stopStreaming() -{ - VDR_RequestPacket vrp; - if (!vrp.init(VDR_STOPSTREAMING, true, 0)) return 0; - - if (TEMP_SINGLE_VDR_PR) // this block only needs to be done if it was a live stream - // TEMP_SINGLE_VDR_PR will not be set unless we are streaming a channel - { - edUnregister(TEMP_SINGLE_VDR_PR); - delete TEMP_SINGLE_VDR_PR; - TEMP_SINGLE_VDR_PR = NULL; - } - - VDR_ResponsePacket* vresp = RequestResponse(&vrp); - if (vresp->noResponse()) { delete vresp; return 0; } - - int toReturn = (int)vresp->extractULONG(); - delete vresp; - - return toReturn; -} - -UCHAR* VDR::getBlock(ULLONG position, UINT maxAmount, UINT* amountReceived) -{ - VDR_RequestPacket vrp; - if (!vrp.init(VDR_GETBLOCK, true, sizeof(ULLONG) + sizeof(ULONG))) return NULL; - if (!vrp.addULLONG(position)) return NULL; - if (!vrp.addULONG(maxAmount)) return NULL; - - VDR_ResponsePacket* vresp = RequestResponse(&vrp); - if (vresp->noResponse()) { delete vresp; return NULL; } - - if (vresp->serverError()) - { - logger->log("VDR", Log::DEBUG, "Detected getblock 0"); - delete vresp; - return NULL; - } - - // Special handling for getblock - UCHAR* toReturn = vresp->getUserData(); - *amountReceived = vresp->getUserDataLength(); - - delete vresp; - - return toReturn; -} - -ULLONG VDR::streamRecording(char* fileName, ULONG* totalFrames, bool* IsPesRecording) -{ - VDR_RequestPacket vrp; - if (!vrp.init(VDR_STREAMRECORDING, true, strlen(fileName) + 1)) return 0; - if (!vrp.addString(fileName)) return 0; - - VDR_ResponsePacket* vresp = RequestResponse(&vrp); - if (vresp->noResponse()) { delete vresp; return 0; } - - ULLONG lengthBytes = vresp->extractULLONG(); - ULONG lengthFrames = vresp->extractULONG(); - UCHAR isPesRecording = vresp->extractUCHAR(); - delete vresp; - - *totalFrames = lengthFrames; - *IsPesRecording = (isPesRecording);//convert Uchar to bool - - logger->log("VDR", Log::DEBUG, "VDR said length is: %llu %lu, IsPesRecording %x", lengthBytes, lengthFrames, *IsPesRecording); - - return lengthBytes; -} - -ULLONG VDR::positionFromFrameNumber(ULONG frameNumber) -{ - VDR_RequestPacket vrp; - if (!vrp.init(VDR_POSFROMFRAME, true, sizeof(ULONG))) return 0; - if (!vrp.addULONG(frameNumber)) return 0; - - VDR_ResponsePacket* vresp = RequestResponse(&vrp); - if (vresp->noResponse()) { delete vresp; return 0; } - - ULLONG position = vresp->extractULLONG(); - delete vresp; - - logger->log("VDR", Log::DEBUG, "VDR said new position is: %llu", position); - - return position; -} - -ULONG VDR::frameNumberFromPosition(ULLONG position) -{ - VDR_RequestPacket vrp; - if (!vrp.init(VDR_FRAMEFROMPOS, true, sizeof(ULLONG))) return 0; - if (!vrp.addULLONG(position)) return 0; - - VDR_ResponsePacket* vresp = RequestResponse(&vrp); - if (vresp->noResponse()) { delete vresp; return 0; } - - ULONG framenumber = vresp->extractULONG(); - delete vresp; - - logger->log("VDR", Log::DEBUG, "VDR said new framenumber is: %u", framenumber); - - return framenumber; -} - -bool VDR::getNextIFrame(ULONG frameNumber, ULONG direction, ULLONG* rfilePosition, ULONG* rframeNumber, ULONG* rframeLength) -{ - VDR_RequestPacket vrp; - if (!vrp.init(VDR_GETNEXTIFRAME, true, sizeof(ULONG)*2)) return false; - if (!vrp.addULONG(frameNumber)) return false; - if (!vrp.addULONG(direction)) return false; - - VDR_ResponsePacket* vresp = RequestResponse(&vrp); - if (vresp->noResponse()) { delete vresp; return false; } - - if (vresp->serverError()) - { - logger->log("VDR", Log::DEBUG, "Detected getNextIFrame error"); - delete vresp; - return false; - } - - *rfilePosition = vresp->extractULLONG(); - *rframeNumber = vresp->extractULONG(); - *rframeLength = vresp->extractULONG(); - - delete vresp; - - logger->log("VDR", Log::DEBUG, "VDR GNIF said %llu %lu %lu", *rfilePosition, *rframeNumber, *rframeLength); - - return true; -} - -EventList* VDR::getChannelSchedule(ULONG number) -{ - time_t now; - time(&now); - return getChannelSchedule(number, now, 24 * 60 * 60); -} - -EventList* VDR::getChannelSchedule(ULONG number, time_t start, ULONG duration) -{ -// retrieve event list (vector of events) from vdr within filter window. duration is in seconds - - VDR_RequestPacket vrp; - if (!vrp.init(VDR_GETCHANNELSCHEDULE, true, sizeof(ULONG)*3)) return NULL; - if (!vrp.addULONG(number)) return NULL; - if (!vrp.addULONG(start)) return NULL; - if (!vrp.addULONG(duration)) return NULL; - - VDR_ResponsePacket* vresp = RequestResponse(&vrp); - if (vresp->noResponse()) { delete vresp; return NULL; } - - // received a ulong(0) - schedules error in the plugin - if (vresp->serverError()) - { - delete vresp; - return NULL; - } - - EventList* eventList = new EventList(); - - while (!vresp->end()) - { - Event* event = new Event(); - event->id = vresp->extractULONG(); - event->time = vresp->extractULONG(); - event->duration = vresp->extractULONG(); - event->title = vresp->extractString(); - event->subtitle = vresp->extractString(); - event->description = vresp->extractString(); - eventList->push_back(event); - } - - delete vresp; - - logger->log("VDR", Log::DEBUG, "Success got to end of getChannelSchedule"); - return eventList; -} - -int VDR::configSave(const char* section, const char* key, const char* value) -{ - VDR_RequestPacket vrp; - if (!vrp.init(VDR_CONFIGSAVE, false, 0)) return 0; - if (!vrp.addString(section)) return 0; - if (!vrp.addString(key)) return 0; - if (!vrp.addString(value)) return 0; - - VDR_ResponsePacket* vresp = RequestResponse(&vrp); - if (vresp->noResponse()) { delete vresp; return 0; } - - int toReturn = (int)vresp->extractULONG(); - delete vresp; - - return toReturn; -} - -char* VDR::configLoad(const char* section, const char* key) -{ - VDR_RequestPacket vrp; - if (!vrp.init(VDR_CONFIGLOAD, false, 0)) return NULL; - if (!vrp.addString(section)) return NULL; - if (!vrp.addString(key)) return NULL; - - VDR_ResponsePacket* vresp = RequestResponse(&vrp); - if (vresp->noResponse()) { delete vresp; return NULL; } - - char* toReturn = vresp->extractString(); - delete vresp; - - return toReturn; -} - -RecTimerList* VDR::getRecTimersList() -{ - VDR_RequestPacket vrp; - if (!vrp.init(VDR_GETTIMERS, true, 0)) return NULL; - - VDR_ResponsePacket* vresp = RequestResponse(&vrp); - if (vresp->noResponse()) { delete vresp; return NULL; } - - RecTimerList* recTimerList = new RecTimerList(); - - ULONG numTimers = vresp->extractULONG(); - if (numTimers > 0) - { - RecTimer* newRecTimer; - char* tempString; - - while (!vresp->end()) - { - newRecTimer = new RecTimer(); - newRecTimer->active = vresp->extractULONG(); - newRecTimer->recording = vresp->extractULONG(); - newRecTimer->pending = vresp->extractULONG(); - newRecTimer->priority = vresp->extractULONG(); - newRecTimer->lifeTime = vresp->extractULONG(); - newRecTimer->channelNumber = vresp->extractULONG(); - newRecTimer->startTime = vresp->extractULONG(); - newRecTimer->stopTime = vresp->extractULONG(); - newRecTimer->day = vresp->extractULONG(); - newRecTimer->weekDays = vresp->extractULONG(); - - tempString = vresp->extractString(); - newRecTimer->setFile(tempString); - delete[] tempString; - - recTimerList->push_back(newRecTimer); - logger->log("VDR", Log::DEBUG, "TL: %lu %lu %lu %lu %lu %lu %lu %lu %s", - newRecTimer->active, newRecTimer->recording, newRecTimer->pending, newRecTimer->priority, newRecTimer->lifeTime, - newRecTimer->channelNumber, newRecTimer->startTime, newRecTimer->stopTime, newRecTimer->getFile()); - } - } - - delete vresp; - - sort(recTimerList->begin(), recTimerList->end(), RecTimerSorter()); - - return recTimerList; -} - -ULONG VDR::setEventTimer(char* timerString) -{ - VDR_RequestPacket vrp; - if (!vrp.init(VDR_SETTIMER, true, strlen(timerString) + 1)) return 0; - if (!vrp.addString(timerString)) return 0; - - VDR_ResponsePacket* vresp = RequestResponse(&vrp); - if (vresp->noResponse()) { delete vresp; return 0; } - - ULONG toReturn = vresp->extractULONG(); - delete vresp; - - return toReturn; -} - -RecInfo* VDR::getRecInfo(char* fileName) -{ - VDR_RequestPacket vrp; - if (!vrp.init(VDR_GETRECINFO, true, strlen(fileName) + 1)) return NULL; - if (!vrp.addString(fileName)) return NULL; - - VDR_ResponsePacket* vresp = RequestResponse(&vrp); - if (vresp->noResponse()) { delete vresp; return NULL; } - - if (vresp->serverError()) - { - logger->log("VDR", Log::DEBUG, "Could not get rec info"); - delete vresp; - return NULL; - } - - RecInfo* recInfo = new RecInfo(); - - recInfo->timerStart = vresp->extractULONG(); - recInfo->timerEnd = vresp->extractULONG(); - recInfo->resumePoint = vresp->extractULONG(); - recInfo->summary = vresp->extractString(); - - ULONG numComponents = vresp->extractULONG(); - if (numComponents) - { - recInfo->setNumComponents(numComponents); - for (ULONG i = 0; i < numComponents; i++) - { - recInfo->streams[i] = vresp->extractUCHAR(); - recInfo->types[i] = vresp->extractUCHAR(); - recInfo->languages[i] = vresp->extractString(); - recInfo->descriptions[i] = vresp->extractString(); - } - } - recInfo->fps=vresp->extractdouble(); - - - recInfo->print(); - - delete vresp; - return recInfo; -} - -// FIXME obselete -ULLONG VDR::rescanRecording(ULONG* totalFrames) -{ - VDR_RequestPacket vrp; - if (!vrp.init(VDR_RESCANRECORDING, true, 0)) return 0; - - VDR_ResponsePacket* vresp = RequestResponse(&vrp); - if (vresp->noResponse()) { delete vresp; return 0; } - - ULLONG lengthBytes = vresp->extractULLONG(); - ULONG lengthFrames = vresp->extractULONG(); - delete vresp; - - logger->log("VDR", Log::DEBUG, "VDR said length is: %llu %lu", lengthBytes, lengthFrames); - - *totalFrames = lengthFrames; - return lengthBytes; -} - -MarkList* VDR::getMarks(char* fileName) -{ - VDR_RequestPacket vrp; - if (!vrp.init(VDR_GETMARKS, true, strlen(fileName) + 1)) return NULL; - if (!vrp.addString(fileName)) return NULL; - - VDR_ResponsePacket* vresp = RequestResponse(&vrp); - if (vresp->noResponse()) { delete vresp; return NULL; } - - if (vresp->serverError()) - { - delete vresp; - return NULL; - } - - MarkList* markList = new MarkList(); - - while (!vresp->end()) - { - Mark* mark = new Mark(); - mark->pos = vresp->extractULONG(); - - markList->push_back(mark); - logger->log("VDR", Log::DEBUG, "Have added a mark to list. %lu", mark->pos); - } - - delete vresp; - - return markList; -} - -void VDR::getChannelPids(Channel* channel) -{ - VDR_RequestPacket vrp; - if (!vrp.init(VDR_GETCHANNELPIDS, true, sizeof(ULONG))) return ; - if (!vrp.addULONG(channel->number)) return ; - - VDR_ResponsePacket* vresp = RequestResponse(&vrp); - if (vresp->noResponse()) { delete vresp; return ; } - - // Format of response - // vpid - // number of apids - // { - // apid - // lang string - // } - - channel->vpid = vresp->extractULONG(); - channel->vstreamtype = vresp->extractULONG(); - channel->numAPids = vresp->extractULONG(); - - for (ULONG i = 0; i < channel->numAPids; i++) - { - apid newapid; - newapid.pid = vresp->extractULONG(); - newapid.name = vresp->extractString(); - channel->apids.push_back(newapid); - } - - channel->numDPids = vresp->extractULONG(); - - for (ULONG i = 0; i < channel->numDPids; i++) - { - apid newdpid; - newdpid.pid = vresp->extractULONG(); - newdpid.name = vresp->extractString(); - channel->dpids.push_back(newdpid); - } - - channel->numSPids = vresp->extractULONG(); - - for (ULONG i = 0; i < channel->numSPids; i++) - { - apid newspid; - newspid.pid = vresp->extractULONG(); - newspid.name = vresp->extractString(); - channel->spids.push_back(newspid); - } - channel->tpid = vresp->extractULONG(); - - delete vresp; - - return ; -} - - -MediaList * VDR::getRootList() { - return getMediaList(NULL); -} -/** - * media List Request: - * mediaURI - * Media List response: - * mediaList -*/ -MediaList* VDR::getMediaList(const MediaURI * root) -{ - logger->log("VDR", Log::DEBUG, "getMediaList %s,d=%s, prov=%d", (root?root->getName():"NULL"), - ((root && root->hasDisplayName())?root->getDisplayName():"NULL"), - (root?root->getProvider():providerId)); - MediaURI remoteURI(root); - VDR_GetMediaListRequest request(&remoteURI); - SerializeBuffer *vrp=prepareRequest(&request); - if (!vrp) { - logger->log("VDR", Log::ERR, "getMediaList unable to create command"); - return NULL; - } - - SerializeBuffer* vresp = doRequestResponse(vrp,request.command); - if (!vresp) { - Command::getInstance()->connectionLost(); - return NULL; - } - - MediaList *rt=new MediaList(NULL); - ULONG rtflags=0; - VDR_GetMediaListResponse resp(&rtflags,rt); - if (decodeResponse(vresp,&resp) != 0) { - return NULL; - } - return rt; -} - -/** - * get image Request: - * uri,x,y, channel - * get media response: - * 4 flags - * 8 len of image -*/ -int VDR::openMedium(ULONG channel,const MediaURI *uri, ULLONG * size, ULONG x, ULONG y) -{ - MediaURI remoteURI(uri); - VDR_OpenMediumRequest request(&channel,&remoteURI,&x,&y); - *size=0; - SerializeBuffer *vrp=prepareRequest(&request); - if (!vrp) { - logger->log("VDR", Log::ERR, "openMedium unable to create command"); - return -1; - } - SerializeBuffer* vresp = doRequestResponse(vrp,request.command); - if (!vresp) { - Command::getInstance()->connectionLost(); - return -1; - } - ULONG flags=0; - VDR_OpenMediumResponse response(&flags,size); - if (decodeResponse(vresp,&response) != 0) { - return -1; - } - logger->log("VDR", Log::DEBUG, "openMedia len=%llu", *size); - return 0; -} - -/** - * getMediaBlock - no separate response class - simple data block - * resp - * packet - */ -int VDR::getMediaBlock(ULONG channel, ULLONG position, ULONG maxAmount, ULONG* amountReceived, unsigned char **buffer) -{ - *amountReceived=0; - VDR_GetMediaBlockRequest request(&channel,&position,&maxAmount); - SerializeBuffer *vrp=prepareRequest(&request); - if (!vrp) { - logger->log("VDR", Log::ERR, "getMediaBlock unable to create command"); - return -1; - } - SerializeBuffer* vresp = doRequestResponse(vrp,request.command); - if (!vresp) { - Command::getInstance()->connectionLost(); - return -1; - } - - // Special handling for getblock - *amountReceived = (ULONG)(vresp->getEnd()-vresp->getStart()); - *buffer = vresp->steelBuffer(); - delete vresp; - return 0; -} - -/** - * VDR_GETMEDIAINFO - * channel - * rt - * flags - * info - */ - -int VDR::getMediaInfo(ULONG channel, MediaInfo * result) { - if (! result) return -1; - VDR_GetMediaInfoRequest request(&channel); - SerializeBuffer *vrp=prepareRequest(&request); - if (!vrp) { - logger->log("VDR", Log::ERR, "getMediaInfo unable to create command"); - return -1; - } - SerializeBuffer* vresp = doRequestResponse(vrp,request.command); - if (!vresp) { - Command::getInstance()->connectionLost(); - return -1; - } - - ULONG flags=0; - VDR_GetMediaInfoResponse response(&flags,result); - if (decodeResponse(vresp,&response) != 0) { - return -1; - } - return 0; -} - -/** - * VDR_CLOSECHANNEL - * channel - * rt - * flags - */ - -int VDR::closeMediaChannel(ULONG channel) { - VDR_CloseMediaChannelRequest request(&channel); - SerializeBuffer *vrp=prepareRequest(&request); - if (!vrp) { - logger->log("VDR", Log::ERR, "closeMediaChannel unable to create command"); - return -1; - } - SerializeBuffer* vresp = doRequestResponse(vrp,request.command); - if (!vresp) { - Command::getInstance()->connectionLost(); - return -1; - } - ULONG flags; - VDR_CloseMediaChannelResponse response(&flags); - if (decodeResponse(vresp,&response) != 0) return -1; - return (flags != 0)?-1:0; -} - - - - -int VDR::deleteTimer(RecTimer* delTimer) -{ - logger->log("VDR", Log::DEBUG, "Delete timer called"); - - VDR_RequestPacket vrp; - if (!vrp.init(VDR_DELETETIMER, false, 0)) return 0; - if (!vrp.addULONG(delTimer->channelNumber)) return 0; - if (!vrp.addULONG(delTimer->weekDays)) return 0; - if (!vrp.addULONG(delTimer->day)) return 0; - if (!vrp.addULONG(delTimer->startTime)) return 0; - if (!vrp.addULONG(delTimer->stopTime)) return 0; - - VDR_ResponsePacket* vresp = RequestResponse(&vrp); - if (vresp->noResponse()) { delete vresp; return 0; } - - int toReturn = (int)vresp->extractULONG(); - delete vresp; - - return toReturn; -} - -I18n::lang_code_list VDR::getLanguageList() -{ - I18n::lang_code_list CodeList; - CodeList["en"] = "English"; // Default entry - VDR_RequestPacket vrp; - if (!vrp.init(VDR_GETLANGUAGELIST, false, 0)) return CodeList; - VDR_ResponsePacket* vresp = RequestResponse(&vrp); - if (vresp->noResponse() || vresp->end()) - { - delete vresp; - return CodeList; - } - CodeList.clear(); - while (!vresp->end()) - { - char* c_code = vresp->extractString(); - char* c_name = vresp->extractString(); - string code = c_code; - string name = c_name; - CodeList[code] = name; - delete[] c_code; - delete[] c_name; - } - delete vresp; - return CodeList; -} - -int VDR::getLanguageContent(const std::string code, I18n::trans_table& texts) -{ - VDR_RequestPacket vrp; - if (!vrp.init(VDR_GETLANGUAGECONTENT, false, 0)) return 0; - if (!vrp.addString(code.c_str())) return 0; - VDR_ResponsePacket* vresp = RequestResponse(&vrp); - if (vresp->noResponse()) { delete vresp; return 0; } - texts.clear(); - while (!vresp->end()) - { - char* c_key = vresp->extractString(); - char* c_text = vresp->extractString(); - string key = c_key; - string text = c_text; - texts[key] = text; - delete[] c_key; - delete[] c_text; - } - delete vresp; - return 1; -} +/* + Copyright 2004-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 "vdr.h" + +#include "recman.h" +#include "tcp.h" +#include "log.h" +#include "recinfo.h" +#include "dsock.h" +#include "channel.h" +#include "event.h" +#include "wol.h" +#include "vdrrequestpacket.h" +#include "vdrresponsepacket.h" +#include "command.h" +#include "media.h" +#include "mediaprovider.h" +#include "mediaproviderids.h" +#include "vdrcommand.h" +#include "video.h" +#include "osd.h" + +VDR* VDR::instance = NULL; +//prepare a request +//will create a request package from a command variable and fill this +//caller has to destroy buffer +static SerializeBuffer * prepareRequest(VDR_Command *cmd) { + SerializeBuffer *buf=new SerializeBuffer(512,false,true); + if (cmd->serialize(buf) != 0) { + delete buf; + return NULL; + } + return buf; +} + +//handle request/response +//TODO avoid copy of buffer (needs extension of requestpacket) +//TODO avoid command 2x (needs some more restructuring) +SerializeBuffer * VDR::doRequestResponse(SerializeBuffer *rq,int cmd) { + VDR_RequestPacket *rt=new VDR_RequestPacket; + if (! rt) { + delete rq; + return NULL; + } + if (! rt->init(cmd,true,rq->getCurrent()-rq->getStart())) { + delete rq; + delete rt; + return NULL; + } + if (! rt->copyin(rq->getStart(),(ULONG)(rq->getCurrent()-rq->getStart()))) { + delete rq; + delete rt; + return NULL; + } + delete rq; + VDR_ResponsePacket *rp=RequestResponse(rt); + logger->log("doRequestResponse",Log::DEBUG,"got response %p",rp); + if ( !rp) { + delete rt; + return NULL; + } + SerializeBuffer *buf=new SerializeBuffer(rp->getUserData(),rp->getUserDataLength(),true,true,false); + delete rp; + return buf; +} + + + +//deserialize a received response +//delete the package +//return !=0 on error +static int decodeResponse(SerializeBuffer *rp,VDR_Command *c) { + ULONG expected=c->command; + if (c->deserialize(rp) != 0) { + delete rp; + Log::getInstance()->log("VDR", Log::ERR, "decodeResponse unable to deserialize for command %lu",expected); + return -1; + } + delete rp; + if (c->command != expected) { + Log::getInstance()->log("VDR", Log::ERR, "decodeResponse unexpected response received 0x%lx, expected 0x%lx",c->command,expected); + return -1; + } + Log::getInstance()->log("VDR", Log::DEBUG, "decodeResponse successfully decoded command 0x%lx",expected); + return 0; +} + + + +VDR::VDR() +{ + if (instance) return; + instance = this; + initted = 0; + findingServer = 0; + tcp = NULL; + connected = false; + maxChannelNumber = 0; + channelNumberWidth = 1; + TEMP_SINGLE_VDR_PR = NULL; + providerId=MPROVIDERID_VDR; + subRange=MPROVIDERRANGE_VDR; + MediaPlayerRegister::getInstance()->registerMediaProvider(this,MPROVIDERID_VDR,MPROVIDERRANGE_VDR); +} + +VDR::~VDR() +{ + instance = NULL; + if (initted) shutdown(); +} + +VDR* VDR::getInstance() +{ + return instance; +} + +int VDR::init(int tport) +{ + if (initted) return 0; + initted = 1; + port = tport; + logger = Log::getInstance(); + return 1; +} + +int VDR::shutdown() +{ + if (!initted) return 0; + initted = 0; + disconnect(); + return 1; +} + +void VDR::findServers(vector& servers) +{ + Wol* wol = Wol::getInstance(); + findingServer = 1; + char* message = "VOMP"; + + DatagramSocket ds(port); + int haveAtLeastOne = 0; + int retval; + int waitType = 1; + bool firstloop = true; + while(findingServer) + { + if (waitType == 1) + { + ds.shutdown(); + ds.init(); + logger->log("VDR", Log::NOTICE, "Broadcasting for server"); + ds.send("255.255.255.255", 3024, message, strlen(message)); + if(!firstloop) wol->doWakeUp(); + } + retval = ds.waitforMessage(waitType); + + if (retval == 2) // we got a reply + { + if (!strcmp(ds.getData(), "VOMP")) // echo..... + { + waitType = 2; + } + else + { + VDRServer newServer; + newServer.ip = new char[16]; + strcpy(newServer.ip, ds.getFromIPA()); + + if (ds.getDataLength() == 0) + { + newServer.name = new char[1]; + newServer.name[0] = '\0'; + } + else + { + newServer.name = new char[strlen(ds.getData())+1]; + strcpy(newServer.name, ds.getData()); + } + + servers.push_back(newServer); + waitType = 2; + haveAtLeastOne = 1; + } + } + else + { + if (haveAtLeastOne) break; + waitType = 1; + firstloop = false; +/* For DEBUGGING * + { VDRServer newServer; + newServer.ip = new char[16]; + strcpy(newServer.ip, "192.168.1.7"); + newServer.name = new char[6]; + strcpy(newServer.name,"debug"); + servers.push_back(newServer); + waitType = 2; + haveAtLeastOne = 1;}/**/ + + + + } + } + logger->log("VDR", Log::NOTICE, "END loop"); + sort(servers.begin(), servers.end(), ServerSorter()); +} + +void VDR::cancelFindingServer() +{ + findingServer = 0; +} + +void VDR::setServerIP(char* newIP) +{ + strcpy(serverIP, newIP); +} + +int VDR::connect() +{ + maxChannelNumber = 0; + channelNumberWidth = 1; + + if (tcp) delete tcp; + tcp = new TCP(); + if (tcp->connectTo(serverIP, 3024)) + { + connected = true; + threadStart(); + return 1; + } + else + { + return 0; + } +} + +void VDR::disconnect() +{ + threadCancel(); + if (tcp) delete tcp; + tcp = NULL; + connected = false; + logger->log("VDR", Log::DEBUG, "Disconnect"); +} + +void VDR::setReceiveWindow(size_t size) +{ + if (connected) tcp->setReceiveWindow(size); +} + +/////////////////////////////////////////////////////// + +void VDR::threadMethod() +{ + logger->log("VDR", Log::DEBUG, "VDR RUN"); + + threadSetKillable(); // FIXME - change this to deal with the EDRs + + ULONG channelID; + + ULONG requestID; + ULONG userDataLength; + UCHAR* userData; + + ULONG streamID; + ULONG flag; + + VDR_ResponsePacket* vresp; + + ULONG timeNow = 0; + ULONG lastKAsent = 0; + ULONG lastKArecv = time(NULL); + int readSuccess; + + while(1) + { + timeNow = time(NULL); + + readSuccess = tcp->readData((UCHAR*)&channelID, sizeof(ULONG)); // 2s timeout atm + + if (!readSuccess) + { + //logger->log("VDR", Log::DEBUG, "Net read timeout"); + if (!tcp->isConnected()) { connectionDied(); return; } // return to stop this thread + } + + // Error or timeout. + + if (!lastKAsent) // have not sent a KA + { + if (lastKArecv < (timeNow - 5)) + { + logger->log("VDR", Log::DEBUG, "Sending KA packet"); + if (!sendKA(timeNow)) + { + logger->log("VDR", Log::DEBUG, "Could not send KA, calling connectionDied"); + connectionDied(); + return; + } + lastKAsent = timeNow; + } + } + else + { + if (lastKAsent <= (timeNow - 10)) + { + logger->log("VDR", Log::DEBUG, "lastKA over 10s ago, calling connectionDied"); + connectionDied(); + return; + } + } + + if (!readSuccess) continue; // no data was read but the connection is ok. + + // Data was read + + channelID = ntohl(channelID); + + if (channelID == CHANNEL_REQUEST_RESPONSE) + { + if (!tcp->readData((UCHAR*)&requestID, sizeof(ULONG))) break; + requestID = ntohl(requestID); + if (!tcp->readData((UCHAR*)&userDataLength, sizeof(ULONG))) break; + userDataLength = ntohl(userDataLength); + if (userDataLength > 5000000) break; // how big can these packets get? + userData = NULL; + if (userDataLength > 0) + { + userData = (UCHAR*)malloc(userDataLength); + if (!userData) break; + if (!tcp->readData(userData, userDataLength)) break; + } + + vresp = new VDR_ResponsePacket(); + vresp->setResponse(requestID, userData, userDataLength); + logger->log("VDR", Log::DEBUG, "Rxd a response packet, requestID=%lu, len=%lu", requestID, userDataLength); + + if (!edFindAndCall(vresp)) // makes ED lock, find receiver for vresp (using ed_cb_find() ) and then call (using ed_cb_call() ) + { + // If edFindAndCall returns true, edr was called and vresp was handed off. + // else, delete vresp here. + delete vresp; + } + } + else if (channelID == CHANNEL_STREAM) + { + if (!tcp->readData((UCHAR*)&streamID, sizeof(ULONG))) break; + streamID = ntohl(streamID); + + if (!tcp->readData((UCHAR*)&flag, sizeof(ULONG))) break; + flag = ntohl(flag); + + if (!tcp->readData((UCHAR*)&userDataLength, sizeof(ULONG))) break; + userDataLength = ntohl(userDataLength); + userData = NULL; + if (userDataLength > 0) + { + userData = (UCHAR*)malloc(userDataLength); + if (!userData) break; + if (!tcp->readData(userData, userDataLength)) break; + } + + vresp = new VDR_ResponsePacket(); + vresp->setStream(streamID, flag, userData, userDataLength); +// logger->log("VDR", Log::DEBUG, "Rxd a stream packet, streamID=%lu, flag=%lu, len=%lu", streamID, flag, userDataLength); + + if (!edFindAndCall(vresp)) // makes ED lock, find receiver for vresp (using ed_cb_find() ) and then call (using ed_cb_call() ) + { + // If edFindAndCall returns true, edr was called and vresp was handed off. + // else, delete vresp here. + delete vresp; + } + } + else if (channelID == CHANNEL_KEEPALIVE) + { + ULONG KAreply = 0; + if (!tcp->readData((UCHAR*)&KAreply, sizeof(ULONG))) break; + KAreply = (ULONG)ntohl(KAreply); + if (KAreply == lastKAsent) // successful KA response + { + lastKAsent = 0; + lastKArecv = KAreply; + logger->log("VDR", Log::DEBUG, "Rxd correct KA reply"); + } + } + else + { + logger->log("VDR", Log::ERR, "Rxd a response packet on channel %lu !!", channelID); + break; + } + threadCheckExit(); + + + // Who deletes vresp? + // If RR, the individual protocol functions must delete vresp. + // If stream, the data and length is taken out in ed_cb_call and vresp is deleted there. + } + + connectionDied(); +} + +void VDR::connectionDied() +{ + // Called from within threadMethod to do cleanup if it decides the connection has died + + connected = false; // though actually it could still be connected until someone calls vdr->disconnect + + // Need to wake up any waiting channel 1 request-response threads + // Normally this is done by a packet coming in with channelid and requestid + // Instead, go through the list and for each channel 1 edr, make an empty vresp + // An empty vresp will have userData == NULL, which means vresp->noResponse() == true + + // If it's a stream receiver, generate a stream packet with flag == connection_lost + + edLock(); + VDR_PacketReceiver* vdrpr; + VDR_ResponsePacket* vresp; + while(receivers.size()) + { + vdrpr = (VDR_PacketReceiver*) *(receivers.begin()); + if (vdrpr->receiverChannel == CHANNEL_REQUEST_RESPONSE) + { + vresp = new VDR_ResponsePacket(); + vresp->setResponse(vdrpr->requestSerialNumber, NULL, 0); + logger->log("VDR", Log::DEBUG, "Timeouts: created blank response packet for request serial %lu", vdrpr->requestSerialNumber); + edUnlock(); + if (!edFindAndCall(vresp)) // makes ED lock, find receiver for vresp (using ed_cb_find() ) and then call (using ed_cb_call() ) + { + // If edFindAndCall returns true, edr was called and vresp was handed off. + // else, delete vresp here. + logger->log("VDR", Log::ERR, "Timeouts: no waiting thread found for request serial %lu !!!", vdrpr->requestSerialNumber); + delete vresp; + } + edLock(); + } + else if (vdrpr->receiverChannel == CHANNEL_STREAM) + { + vresp = new VDR_ResponsePacket(); + vresp->setStream(vdrpr->streamID, 2 /* connection-lost flag */ , NULL, 0); + logger->log("VDR", Log::DEBUG, "Timeouts: created blank response packet for streamid %lu", vdrpr->streamID); + edUnlock(); + if (!edFindAndCall(vresp)) // makes ED lock, find receiver for vresp (using ed_cb_find() ) and then call (using ed_cb_call() ) + { + // If edFindAndCall returns true, edr was called and vresp was handed off. + // else, delete vresp here. + logger->log("VDR", Log::ERR, "Timeouts: no waiting stream receiver found for streamid %lu !!!", vdrpr->streamID); + delete vresp; + } + edLock(); + + for(EDRL::iterator i = receivers.begin(); i != receivers.end(); i++) + if ((VDR_PacketReceiver*)*i == vdrpr) { receivers.erase(i); break; } + } + } + edUnlock(); + // Ok, all event receviers should be dealt with. just in case there weren't any, inform command + logger->log("VDR", Log::DEBUG, "edUnlock at end of connectionDied"); + + Command::getInstance()->connectionLost(); +} + +bool VDR::ed_cb_find(EDReceiver* edr, void* userTag) +{ + // edr is a VDR_PacketReceiver object made in VDR::RequestResponse + // userTag is a VDR_ResponsePacket made in threadMethod + + VDR_PacketReceiver* vdrpr = (VDR_PacketReceiver*)edr; + VDR_ResponsePacket* vresp = (VDR_ResponsePacket*)userTag; + + // Is vresp for vdrpr ? + + ULONG packetChannel = vresp->getChannelID(); + if (vdrpr->receiverChannel != packetChannel) return false; + + if (packetChannel == CHANNEL_REQUEST_RESPONSE) + { + if (vdrpr->requestSerialNumber == vresp->getRequestID()) return true; + } + else if (packetChannel == CHANNEL_STREAM) + { + if (vdrpr->streamID == vresp->getStreamID()) return true; + } + + return false; +} + +VDR_ResponsePacket* VDR::RequestResponse(VDR_RequestPacket* vrp) +{ + //logger->log("VDR", Log::DEBUG, "RR %lu", vrp->getOpcode()); + + if (!connected) + { + logger->log("VDR", Log::DEBUG, "RR when !connected"); + VDR_ResponsePacket* vresp = new VDR_ResponsePacket(); + return vresp; // "no-response" return + } + + // ED make new VDR and register + // make a VDR_PacketReceiver + // - init with serial number of request packet + + VDR_PacketReceiver vdrpr; +// vdrpr.requestTime = time(NULL); + vdrpr.receiverChannel = VDR::CHANNEL_REQUEST_RESPONSE; + vdrpr.requestSerialNumber = vrp->getSerial(); + + edRegister(&vdrpr); + + edLock(); + + if ((ULONG)tcp->sendData(vrp->getPtr(), vrp->getLen()) != vrp->getLen()) + { + edUnlock(); + edUnregister(&vdrpr); + VDR_ResponsePacket* vresp = new VDR_ResponsePacket(); + return vresp; // "no-response" return + } + + // Sleep and block this thread. The sleep unlocks the mutex + logger->log("VDR", Log::DEBUG, "RR sleep - opcode %lu", vrp->getOpcode()); + edSleepThisReceiver(&vdrpr); + logger->log("VDR", Log::DEBUG, "RR unsleep"); + + // Woken because a response packet has arrived, mutex will be locked + + edUnlock(); + return vdrpr.save_vresp; +} + +bool VDR::sendKA(ULONG timeStamp) +{ + char buffer[8]; + + int pos=0; + ULONG ul=CHANNEL_KEEPALIVE; + buffer[pos++]=(ul>>24)&0xff; + buffer[pos++]=(ul>>16)&0xff; + buffer[pos++]=(ul>>8)&0xff; + buffer[pos++]=ul &0xff; + ul=timeStamp; + buffer[pos++]=(ul>>24)&0xff; + buffer[pos++]=(ul>>16)&0xff; + buffer[pos++]=(ul>>8)&0xff; + buffer[pos++]=ul &0xff; + if ((ULONG)tcp->sendData(buffer, 8) != 8) return false; + return true; +} + +///////////////////////////////////////////////////////////////////////////// + +// Here VDR takes a break for the VDR_PacketReceiver helper class + +bool VDR_PacketReceiver::call(void* userTag) +{ + if (receiverChannel == VDR::CHANNEL_REQUEST_RESPONSE) + { + // It's a RR. Save vresp and, signal the waiting thread and return. + // VDR::RequestResponse will be blocking waiting for this to happen. + // That function has a pointer to this object and can read save_vresp. + save_vresp = (VDR_ResponsePacket*)userTag; + return true; // Signals ED to remove edr from receivers and wake up edr thread + } + + if (receiverChannel == VDR::CHANNEL_STREAM) + { + // It's a stream packet. + VDR_ResponsePacket* vresp = (VDR_ResponsePacket*)userTag; + streamReceiver->streamReceive(vresp->getFlag(), vresp->getUserData(), vresp->getUserDataLength()); + delete vresp; + return false; + } + + abort(); // unknown receiverChannel, should not happen +} + +///////////////////////////////////////////////////////////////////////////// + +int VDR::doLogin() +{ + VDR_RequestPacket vrp; + if (!vrp.init(VDR_LOGIN, true, 6)) return 0; + + char* mactemp[6]; + tcp->getMAC((char*)mactemp); + if (!vrp.copyin((UCHAR*)mactemp, 6)) return 0; + + VDR_ResponsePacket* vresp = RequestResponse(&vrp); + if (vresp->noResponse()) { delete vresp; return 0; } + + ULONG vdrTime = vresp->extractULONG(); + logger->log("VDR", Log::DEBUG, "vdrtime = %lu", vdrTime); + long vdrTimeOffset = vresp->extractLONG(); + logger->log("VDR", Log::DEBUG, "offset = %i", vdrTimeOffset); + + delete vresp; + + // Set the time and zone on the MVP only + +#if !defined(WIN32) && !defined(__ANDROID__) + struct timespec currentTime; + currentTime.tv_sec = vdrTime; + currentTime.tv_nsec = 0; + + int b = clock_settime(CLOCK_REALTIME, ¤tTime); + + logger->log("VDR", Log::DEBUG, "set clock = %u", b); + + // now make a TZ variable and set it + char sign; + int hours; + int minutes; + if (vdrTimeOffset > 0) sign = '-'; + else sign = '+'; + + vdrTimeOffset = abs(vdrTimeOffset); + + hours = (int)vdrTimeOffset / 3600; + minutes = vdrTimeOffset % 3600; + + logger->log("VDR", Log::DEBUG, "%c %i %i", sign, hours, minutes); + + minutes = (int)minutes / 60; + + logger->log("VDR", Log::DEBUG, "%c %i %i", sign, hours, minutes); + + char newTZ[30]; + sprintf(newTZ, "MVP%c%i:%i", sign, hours, minutes); + setenv("TZ", newTZ, 1); + + logger->log("VDR", Log::DEBUG, "Timezone data: %s", newTZ); +#endif + + setCharset(Osd::getInstance()->charSet()); + + return 1; +} + +bool VDR::LogExtern(const char* logString) +{ + if (!connected) return false; + int stringLength = strlen(logString); + int packetLength = stringLength + 8; + char *buffer=new char[packetLength + 1]; + int pos=0; + ULONG ul=CHANNEL_NETLOG; + buffer[pos++]=(ul>>24)&0xff; + buffer[pos++]=(ul>>16)&0xff; + buffer[pos++]=(ul>>8)&0xff; + buffer[pos++]=ul &0xff; + ul=stringLength; + buffer[pos++]=(ul>>24)&0xff; + buffer[pos++]=(ul>>16)&0xff; + buffer[pos++]=(ul>>8)&0xff; + buffer[pos++]=ul &0xff; + + strcpy(&buffer[8], logString); + + if ((ULONG)tcp->sendData(buffer, packetLength) != packetLength) { + delete [] buffer; + return false; + } + delete [] buffer; + return true; +} + +bool VDR::setCharset(int charset) +{ + VDR_RequestPacket vrp; + if (!vrp.init(VDR_SETCHARSET, true, sizeof(ULONG))) return false; + if (!vrp.addULONG(charset)) return false; + + VDR_ResponsePacket* vresp = RequestResponse(&vrp); + if (vresp->noResponse()) { delete vresp; return false; } + + ULONG success = vresp->extractULONG(); + delete vresp; + + if (!success) return false; + + return true; +} + +bool VDR::getRecordingsList(RecMan* recman) +{ + VDR_RequestPacket vrp; + if (!vrp.init(VDR_GETRECORDINGLIST, true, 0)) return false; + + VDR_ResponsePacket* vresp = RequestResponse(&vrp); + if (vresp->noResponse()) { delete vresp; return false; } + + ULONG totalSpace = vresp->extractULONG(); + ULONG freeSpace = vresp->extractULONG(); + ULONG percent = vresp->extractULONG(); + + recman->setStats(totalSpace, freeSpace, percent); + + ULONG start; + char* name; + char* fileName; + + while (!vresp->end()) + { + + start = vresp->extractULONG(); + name = vresp->extractString(); + fileName = vresp->extractString(); + recman->addEntry(start, name, fileName); + delete[] name; + delete[] fileName; + } + delete vresp; + + return true; +} + +int VDR::deleteRecording(char* fileName) +{ + VDR_RequestPacket vrp; + if (!vrp.init(VDR_DELETERECORDING, true, strlen(fileName) + 1)) return 0; + if (!vrp.addString(fileName)) return 0; + + VDR_ResponsePacket* vresp = RequestResponse(&vrp); + if (vresp->noResponse()) { delete vresp; return 0; } + + int toReturn = (int)vresp->extractULONG(); + delete vresp; + + return toReturn; +} + +char* VDR::moveRecording(char* fileName, char* newPath) +{ + VDR_RequestPacket vrp; + if (!vrp.init(VDR_MOVERECORDING, true, strlen(fileName) + 1 + strlen(newPath) + 1)) return NULL; + if (!vrp.addString(fileName)) return NULL; + if (!vrp.addString(newPath)) return NULL; + + VDR_ResponsePacket* vresp = RequestResponse(&vrp); + if (vresp->noResponse()) { delete vresp; return NULL; } + + char* toReturn = NULL; + int success = (int)vresp->extractULONG(); + if (success == 1) + { + toReturn = vresp->extractString(); + } + + delete vresp; + + return toReturn; +} + +ChannelList* VDR::getChannelsList(ULONG type) +{ + VDR_RequestPacket vrp; + if (!vrp.init(VDR_GETCHANNELLIST, true, 0)) return NULL; + + VDR_ResponsePacket* vresp = RequestResponse(&vrp); + if (vresp->noResponse()) { delete vresp; return NULL; } + + ChannelList* chanList = new ChannelList(); + + bool h264support=Video::getInstance()->supportsh264(); + + while (!vresp->end()) + { + Channel* chan = new Channel(); + chan->number = vresp->extractULONG(); + chan->type = vresp->extractULONG(); + chan->name = vresp->extractString(); + chan->vstreamtype = vresp->extractULONG(); + + if (chan->type == type && (chan->vstreamtype!=0x1b || h264support)) + { + chanList->push_back(chan); + logger->log("VDR", Log::DEBUG, "Have added a channel to list. %lu %lu %s", chan->number, chan->type, chan->name); + if (chan->number > maxChannelNumber) maxChannelNumber = chan->number; + } + else + { + delete chan; + } + } + + delete vresp; + + if (maxChannelNumber > 99999) + channelNumberWidth = 6; + else if (maxChannelNumber > 9999) + channelNumberWidth = 5; + else if (maxChannelNumber > 999) + channelNumberWidth = 4; + else if (maxChannelNumber > 99) + channelNumberWidth = 3; + else if (maxChannelNumber > 9) + channelNumberWidth = 2; + else + channelNumberWidth = 1; + + return chanList; +} + +int VDR::streamChannel(ULONG number, StreamReceiver* tstreamReceiver) +{ + VDR_RequestPacket vrp; + if (!vrp.init(VDR_STREAMCHANNEL, true, sizeof(ULONG))) return 0; + if (!vrp.addULONG(number)) return 0; + + + VDR_PacketReceiver* vdrpr = new VDR_PacketReceiver(); + vdrpr->receiverChannel = VDR::CHANNEL_STREAM; + vdrpr->streamID = vrp.getSerial(); + vdrpr->streamReceiver = tstreamReceiver; + edRegister(vdrpr); + TEMP_SINGLE_VDR_PR = vdrpr; + + VDR_ResponsePacket* vresp = RequestResponse(&vrp); + if (vresp->noResponse()) + { + delete vresp; + edUnregister(vdrpr); + delete vdrpr; + return 0; + } + + int toReturn = (int)vresp->extractULONG(); + logger->log("VDR", Log::DEBUG, "VDR said %lu to start streaming request", toReturn); + delete vresp; + + return toReturn; +} + +int VDR::stopStreaming() +{ + VDR_RequestPacket vrp; + if (!vrp.init(VDR_STOPSTREAMING, true, 0)) return 0; + + if (TEMP_SINGLE_VDR_PR) // this block only needs to be done if it was a live stream + // TEMP_SINGLE_VDR_PR will not be set unless we are streaming a channel + { + edUnregister(TEMP_SINGLE_VDR_PR); + delete TEMP_SINGLE_VDR_PR; + TEMP_SINGLE_VDR_PR = NULL; + } + + VDR_ResponsePacket* vresp = RequestResponse(&vrp); + if (vresp->noResponse()) { delete vresp; return 0; } + + int toReturn = (int)vresp->extractULONG(); + delete vresp; + + return toReturn; +} + +UCHAR* VDR::getBlock(ULLONG position, UINT maxAmount, UINT* amountReceived) +{ + VDR_RequestPacket vrp; + if (!vrp.init(VDR_GETBLOCK, true, sizeof(ULLONG) + sizeof(ULONG))) return NULL; + if (!vrp.addULLONG(position)) return NULL; + if (!vrp.addULONG(maxAmount)) return NULL; + + VDR_ResponsePacket* vresp = RequestResponse(&vrp); + if (vresp->noResponse()) { delete vresp; return NULL; } + + if (vresp->serverError()) + { + logger->log("VDR", Log::DEBUG, "Detected getblock 0"); + delete vresp; + return NULL; + } + + // Special handling for getblock + UCHAR* toReturn = vresp->getUserData(); + *amountReceived = vresp->getUserDataLength(); + + delete vresp; + + return toReturn; +} + +ULLONG VDR::streamRecording(char* fileName, ULONG* totalFrames, bool* IsPesRecording) +{ + VDR_RequestPacket vrp; + if (!vrp.init(VDR_STREAMRECORDING, true, strlen(fileName) + 1)) return 0; + if (!vrp.addString(fileName)) return 0; + + VDR_ResponsePacket* vresp = RequestResponse(&vrp); + if (vresp->noResponse()) { delete vresp; return 0; } + + ULLONG lengthBytes = vresp->extractULLONG(); + ULONG lengthFrames = vresp->extractULONG(); + UCHAR isPesRecording = vresp->extractUCHAR(); + delete vresp; + + *totalFrames = lengthFrames; + *IsPesRecording = (isPesRecording);//convert Uchar to bool + + logger->log("VDR", Log::DEBUG, "VDR said length is: %llu %lu, IsPesRecording %x", lengthBytes, lengthFrames, *IsPesRecording); + + return lengthBytes; +} + +ULLONG VDR::positionFromFrameNumber(ULONG frameNumber) +{ + VDR_RequestPacket vrp; + if (!vrp.init(VDR_POSFROMFRAME, true, sizeof(ULONG))) return 0; + if (!vrp.addULONG(frameNumber)) return 0; + + VDR_ResponsePacket* vresp = RequestResponse(&vrp); + if (vresp->noResponse()) { delete vresp; return 0; } + + ULLONG position = vresp->extractULLONG(); + delete vresp; + + logger->log("VDR", Log::DEBUG, "VDR said new position is: %llu", position); + + return position; +} + +ULONG VDR::frameNumberFromPosition(ULLONG position) +{ + VDR_RequestPacket vrp; + if (!vrp.init(VDR_FRAMEFROMPOS, true, sizeof(ULLONG))) return 0; + if (!vrp.addULLONG(position)) return 0; + + VDR_ResponsePacket* vresp = RequestResponse(&vrp); + if (vresp->noResponse()) { delete vresp; return 0; } + + ULONG framenumber = vresp->extractULONG(); + delete vresp; + + logger->log("VDR", Log::DEBUG, "VDR said new framenumber is: %u", framenumber); + + return framenumber; +} + +bool VDR::getNextIFrame(ULONG frameNumber, ULONG direction, ULLONG* rfilePosition, ULONG* rframeNumber, ULONG* rframeLength) +{ + VDR_RequestPacket vrp; + if (!vrp.init(VDR_GETNEXTIFRAME, true, sizeof(ULONG)*2)) return false; + if (!vrp.addULONG(frameNumber)) return false; + if (!vrp.addULONG(direction)) return false; + + VDR_ResponsePacket* vresp = RequestResponse(&vrp); + if (vresp->noResponse()) { delete vresp; return false; } + + if (vresp->serverError()) + { + logger->log("VDR", Log::DEBUG, "Detected getNextIFrame error"); + delete vresp; + return false; + } + + *rfilePosition = vresp->extractULLONG(); + *rframeNumber = vresp->extractULONG(); + *rframeLength = vresp->extractULONG(); + + delete vresp; + + logger->log("VDR", Log::DEBUG, "VDR GNIF said %llu %lu %lu", *rfilePosition, *rframeNumber, *rframeLength); + + return true; +} + +EventList* VDR::getChannelSchedule(ULONG number) +{ + time_t now; + time(&now); + return getChannelSchedule(number, now, 24 * 60 * 60); +} + +EventList* VDR::getChannelSchedule(ULONG number, time_t start, ULONG duration) +{ +// retrieve event list (vector of events) from vdr within filter window. duration is in seconds + + VDR_RequestPacket vrp; + if (!vrp.init(VDR_GETCHANNELSCHEDULE, true, sizeof(ULONG)*3)) return NULL; + if (!vrp.addULONG(number)) return NULL; + if (!vrp.addULONG(start)) return NULL; + if (!vrp.addULONG(duration)) return NULL; + + VDR_ResponsePacket* vresp = RequestResponse(&vrp); + if (vresp->noResponse()) { delete vresp; return NULL; } + + // received a ulong(0) - schedules error in the plugin + if (vresp->serverError()) + { + delete vresp; + return NULL; + } + + EventList* eventList = new EventList(); + + while (!vresp->end()) + { + Event* event = new Event(); + event->id = vresp->extractULONG(); + event->time = vresp->extractULONG(); + event->duration = vresp->extractULONG(); + event->title = vresp->extractString(); + event->subtitle = vresp->extractString(); + event->description = vresp->extractString(); + eventList->push_back(event); + } + + delete vresp; + + logger->log("VDR", Log::DEBUG, "Success got to end of getChannelSchedule"); + return eventList; +} + +int VDR::configSave(const char* section, const char* key, const char* value) +{ + VDR_RequestPacket vrp; + if (!vrp.init(VDR_CONFIGSAVE, false, 0)) return 0; + if (!vrp.addString(section)) return 0; + if (!vrp.addString(key)) return 0; + if (!vrp.addString(value)) return 0; + + VDR_ResponsePacket* vresp = RequestResponse(&vrp); + if (vresp->noResponse()) { delete vresp; return 0; } + + int toReturn = (int)vresp->extractULONG(); + delete vresp; + + return toReturn; +} + +char* VDR::configLoad(const char* section, const char* key) +{ + VDR_RequestPacket vrp; + if (!vrp.init(VDR_CONFIGLOAD, false, 0)) return NULL; + if (!vrp.addString(section)) return NULL; + if (!vrp.addString(key)) return NULL; + + VDR_ResponsePacket* vresp = RequestResponse(&vrp); + if (vresp->noResponse()) { delete vresp; return NULL; } + + char* toReturn = vresp->extractString(); + delete vresp; + + return toReturn; +} + +RecTimerList* VDR::getRecTimersList() +{ + VDR_RequestPacket vrp; + if (!vrp.init(VDR_GETTIMERS, true, 0)) return NULL; + + VDR_ResponsePacket* vresp = RequestResponse(&vrp); + if (vresp->noResponse()) { delete vresp; return NULL; } + + RecTimerList* recTimerList = new RecTimerList(); + + ULONG numTimers = vresp->extractULONG(); + if (numTimers > 0) + { + RecTimer* newRecTimer; + char* tempString; + + while (!vresp->end()) + { + newRecTimer = new RecTimer(); + newRecTimer->active = vresp->extractULONG(); + newRecTimer->recording = vresp->extractULONG(); + newRecTimer->pending = vresp->extractULONG(); + newRecTimer->priority = vresp->extractULONG(); + newRecTimer->lifeTime = vresp->extractULONG(); + newRecTimer->channelNumber = vresp->extractULONG(); + newRecTimer->startTime = vresp->extractULONG(); + newRecTimer->stopTime = vresp->extractULONG(); + newRecTimer->day = vresp->extractULONG(); + newRecTimer->weekDays = vresp->extractULONG(); + + tempString = vresp->extractString(); + newRecTimer->setFile(tempString); + delete[] tempString; + + recTimerList->push_back(newRecTimer); + logger->log("VDR", Log::DEBUG, "TL: %lu %lu %lu %lu %lu %lu %lu %lu %s", + newRecTimer->active, newRecTimer->recording, newRecTimer->pending, newRecTimer->priority, newRecTimer->lifeTime, + newRecTimer->channelNumber, newRecTimer->startTime, newRecTimer->stopTime, newRecTimer->getFile()); + } + } + + delete vresp; + + sort(recTimerList->begin(), recTimerList->end(), RecTimerSorter()); + + return recTimerList; +} + +ULONG VDR::setEventTimer(char* timerString) +{ + VDR_RequestPacket vrp; + if (!vrp.init(VDR_SETTIMER, true, strlen(timerString) + 1)) return 0; + if (!vrp.addString(timerString)) return 0; + + VDR_ResponsePacket* vresp = RequestResponse(&vrp); + if (vresp->noResponse()) { delete vresp; return 0; } + + ULONG toReturn = vresp->extractULONG(); + delete vresp; + + return toReturn; +} + +RecInfo* VDR::getRecInfo(char* fileName) +{ + VDR_RequestPacket vrp; + if (!vrp.init(VDR_GETRECINFO, true, strlen(fileName) + 1)) return NULL; + if (!vrp.addString(fileName)) return NULL; + VDR_ResponsePacket* vresp = RequestResponse(&vrp); + + if (vresp->noResponse()) { delete vresp; return NULL; } + + if (vresp->serverError()) + { + logger->log("VDR", Log::DEBUG, "Could not get rec info"); + delete vresp; + return NULL; + } + + RecInfo* recInfo = new RecInfo(); + + recInfo->timerStart = vresp->extractULONG(); + recInfo->timerEnd = vresp->extractULONG(); + recInfo->resumePoint = vresp->extractULONG(); + recInfo->summary = vresp->extractString(); + + ULONG numComponents = vresp->extractULONG(); + if (numComponents) + { + recInfo->setNumComponents(numComponents); + for (ULONG i = 0; i < numComponents; i++) + { + recInfo->streams[i] = vresp->extractUCHAR(); + recInfo->types[i] = vresp->extractUCHAR(); + recInfo->languages[i] = vresp->extractString(); + recInfo->descriptions[i] = vresp->extractString(); + } + } + recInfo->fps=vresp->extractdouble(); + + + recInfo->print(); + + delete vresp; + return recInfo; +} + +// FIXME obselete +ULLONG VDR::rescanRecording(ULONG* totalFrames) +{ + VDR_RequestPacket vrp; + if (!vrp.init(VDR_RESCANRECORDING, true, 0)) return 0; + + VDR_ResponsePacket* vresp = RequestResponse(&vrp); + if (vresp->noResponse()) { delete vresp; return 0; } + + ULLONG lengthBytes = vresp->extractULLONG(); + ULONG lengthFrames = vresp->extractULONG(); + delete vresp; + + logger->log("VDR", Log::DEBUG, "VDR said length is: %llu %lu", lengthBytes, lengthFrames); + + *totalFrames = lengthFrames; + return lengthBytes; +} + +MarkList* VDR::getMarks(char* fileName) +{ + VDR_RequestPacket vrp; + if (!vrp.init(VDR_GETMARKS, true, strlen(fileName) + 1)) return NULL; + if (!vrp.addString(fileName)) return NULL; + + VDR_ResponsePacket* vresp = RequestResponse(&vrp); + if (vresp->noResponse()) { delete vresp; return NULL; } + + if (vresp->serverError()) + { + delete vresp; + return NULL; + } + + MarkList* markList = new MarkList(); + + while (!vresp->end()) + { + Mark* mark = new Mark(); + mark->pos = vresp->extractULONG(); + + markList->push_back(mark); + logger->log("VDR", Log::DEBUG, "Have added a mark to list. %lu", mark->pos); + } + + delete vresp; + + return markList; +} + +void VDR::getChannelPids(Channel* channel) +{ + VDR_RequestPacket vrp; + if (!vrp.init(VDR_GETCHANNELPIDS, true, sizeof(ULONG))) return ; + if (!vrp.addULONG(channel->number)) return ; + + VDR_ResponsePacket* vresp = RequestResponse(&vrp); + if (vresp->noResponse()) { delete vresp; return ; } + + // Format of response + // vpid + // number of apids + // { + // apid + // lang string + // } + + channel->vpid = vresp->extractULONG(); + channel->vstreamtype = vresp->extractULONG(); + channel->numAPids = vresp->extractULONG(); + + for (ULONG i = 0; i < channel->numAPids; i++) + { + apid newapid; + newapid.pid = vresp->extractULONG(); + newapid.name = vresp->extractString(); + channel->apids.push_back(newapid); + } + + channel->numDPids = vresp->extractULONG(); + + for (ULONG i = 0; i < channel->numDPids; i++) + { + apid newdpid; + newdpid.pid = vresp->extractULONG(); + newdpid.name = vresp->extractString(); + channel->dpids.push_back(newdpid); + } + + channel->numSPids = vresp->extractULONG(); + + for (ULONG i = 0; i < channel->numSPids; i++) + { + apid newspid; + newspid.pid = vresp->extractULONG(); + newspid.name = vresp->extractString(); + channel->spids.push_back(newspid); + } + channel->tpid = vresp->extractULONG(); + + delete vresp; + + return ; +} + + +MediaList * VDR::getRootList() { + return getMediaList(NULL); +} +/** + * media List Request: + * mediaURI + * Media List response: + * mediaList +*/ +MediaList* VDR::getMediaList(const MediaURI * root) +{ + logger->log("VDR", Log::DEBUG, "getMediaList %s,d=%s, prov=%d", (root?root->getName():"NULL"), + ((root && root->hasDisplayName())?root->getDisplayName():"NULL"), + (root?root->getProvider():providerId)); + MediaURI remoteURI(root); + VDR_GetMediaListRequest request(&remoteURI); + SerializeBuffer *vrp=prepareRequest(&request); + if (!vrp) { + logger->log("VDR", Log::ERR, "getMediaList unable to create command"); + return NULL; + } + + SerializeBuffer* vresp = doRequestResponse(vrp,request.command); + if (!vresp) { + Command::getInstance()->connectionLost(); + return NULL; + } + + MediaList *rt=new MediaList(NULL); + ULONG rtflags=0; + VDR_GetMediaListResponse resp(&rtflags,rt); + if (decodeResponse(vresp,&resp) != 0) { + return NULL; + } + return rt; +} + +/** + * get image Request: + * uri,x,y, channel + * get media response: + * 4 flags + * 8 len of image +*/ +int VDR::openMedium(ULONG channel,const MediaURI *uri, ULLONG * size, ULONG x, ULONG y) +{ + MediaURI remoteURI(uri); + VDR_OpenMediumRequest request(&channel,&remoteURI,&x,&y); + *size=0; + SerializeBuffer *vrp=prepareRequest(&request); + if (!vrp) { + logger->log("VDR", Log::ERR, "openMedium unable to create command"); + return -1; + } + SerializeBuffer* vresp = doRequestResponse(vrp,request.command); + if (!vresp) { + Command::getInstance()->connectionLost(); + return -1; + } + ULONG flags=0; + VDR_OpenMediumResponse response(&flags,size); + if (decodeResponse(vresp,&response) != 0) { + return -1; + } + logger->log("VDR", Log::DEBUG, "openMedia len=%llu", *size); + return 0; +} + +/** + * getMediaBlock - no separate response class - simple data block + * resp + * packet + */ +int VDR::getMediaBlock(ULONG channel, ULLONG position, ULONG maxAmount, ULONG* amountReceived, unsigned char **buffer) +{ + *amountReceived=0; + VDR_GetMediaBlockRequest request(&channel,&position,&maxAmount); + SerializeBuffer *vrp=prepareRequest(&request); + if (!vrp) { + logger->log("VDR", Log::ERR, "getMediaBlock unable to create command"); + return -1; + } + SerializeBuffer* vresp = doRequestResponse(vrp,request.command); + if (!vresp) { + Command::getInstance()->connectionLost(); + return -1; + } + + // Special handling for getblock + *amountReceived = (ULONG)(vresp->getEnd()-vresp->getStart()); + *buffer = vresp->steelBuffer(); + delete vresp; + return 0; +} + +/** + * VDR_GETMEDIAINFO + * channel + * rt + * flags + * info + */ + +int VDR::getMediaInfo(ULONG channel, MediaInfo * result) { + if (! result) return -1; + VDR_GetMediaInfoRequest request(&channel); + SerializeBuffer *vrp=prepareRequest(&request); + if (!vrp) { + logger->log("VDR", Log::ERR, "getMediaInfo unable to create command"); + return -1; + } + SerializeBuffer* vresp = doRequestResponse(vrp,request.command); + if (!vresp) { + Command::getInstance()->connectionLost(); + return -1; + } + + ULONG flags=0; + VDR_GetMediaInfoResponse response(&flags,result); + if (decodeResponse(vresp,&response) != 0) { + return -1; + } + return 0; +} + +/** + * VDR_CLOSECHANNEL + * channel + * rt + * flags + */ + +int VDR::closeMediaChannel(ULONG channel) { + VDR_CloseMediaChannelRequest request(&channel); + SerializeBuffer *vrp=prepareRequest(&request); + if (!vrp) { + logger->log("VDR", Log::ERR, "closeMediaChannel unable to create command"); + return -1; + } + SerializeBuffer* vresp = doRequestResponse(vrp,request.command); + if (!vresp) { + Command::getInstance()->connectionLost(); + return -1; + } + ULONG flags; + VDR_CloseMediaChannelResponse response(&flags); + if (decodeResponse(vresp,&response) != 0) return -1; + return (flags != 0)?-1:0; +} + + + + +int VDR::deleteTimer(RecTimer* delTimer) +{ + logger->log("VDR", Log::DEBUG, "Delete timer called"); + + VDR_RequestPacket vrp; + if (!vrp.init(VDR_DELETETIMER, false, 0)) return 0; + if (!vrp.addULONG(delTimer->channelNumber)) return 0; + if (!vrp.addULONG(delTimer->weekDays)) return 0; + if (!vrp.addULONG(delTimer->day)) return 0; + if (!vrp.addULONG(delTimer->startTime)) return 0; + if (!vrp.addULONG(delTimer->stopTime)) return 0; + + VDR_ResponsePacket* vresp = RequestResponse(&vrp); + if (vresp->noResponse()) { delete vresp; return 0; } + + int toReturn = (int)vresp->extractULONG(); + delete vresp; + + return toReturn; +} + +I18n::lang_code_list VDR::getLanguageList() +{ + I18n::lang_code_list CodeList; + CodeList["en"] = "English"; // Default entry + VDR_RequestPacket vrp; + if (!vrp.init(VDR_GETLANGUAGELIST, false, 0)) return CodeList; + VDR_ResponsePacket* vresp = RequestResponse(&vrp); + if (vresp->noResponse() || vresp->end()) + { + delete vresp; + return CodeList; + } + CodeList.clear(); + while (!vresp->end()) + { + char* c_code = vresp->extractString(); + char* c_name = vresp->extractString(); + string code = c_code; + string name = c_name; + CodeList[code] = name; + delete[] c_code; + delete[] c_name; + } + delete vresp; + return CodeList; +} + +int VDR::getLanguageContent(const std::string code, I18n::trans_table& texts) +{ + VDR_RequestPacket vrp; + if (!vrp.init(VDR_GETLANGUAGECONTENT, false, 0)) return 0; + if (!vrp.addString(code.c_str())) return 0; + VDR_ResponsePacket* vresp = RequestResponse(&vrp); + if (vresp->noResponse()) { delete vresp; return 0; } + texts.clear(); + while (!vresp->end()) + { + char* c_key = vresp->extractString(); + char* c_text = vresp->extractString(); + string key = c_key; + string text = c_text; + texts[key] = text; + delete[] c_key; + delete[] c_text; + } + delete vresp; + return 1; +} diff --git a/vdr.h b/vdr.h index c3ff0f1..01f6b0f 100644 --- a/vdr.h +++ b/vdr.h @@ -1,292 +1,292 @@ -/* - 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. -*/ - - -// FIXME - This and the protocol are overly complicated now. Sorry. -// I'll clean it up in a couple of releases time... - - -#ifndef VDR_H -#define VDR_H - -#include -#include -#include -#include - -#ifdef WIN32 -#include "threadwin.h" -#else -#include "threadp.h" -#endif -#include "defines.h" -#include "rectimer.h" -#include "mark.h" -#include "mediaprovider.h" -#include "eventdispatcher.h" -#include "i18n.h" - -class TCP; -class Log; -class RecInfo; -class Event; -class Channel; -class VDR_RequestPacket; -class VDR_ResponsePacket; -class SerializeBuffer; - -using namespace std; - -typedef vector EventList; -typedef vector ChannelList; -typedef vector RecTimerList; - -struct VDRServer -{ - char* ip; - char* name; -}; - -struct RecTimerSorter // : public binary_function -{ - bool operator() (const RecTimer* a, const RecTimer* b) - { - return a->startTime < b->startTime; - } -}; - -struct ServerSorter -{ - bool operator() (const VDRServer& a, const VDRServer& b) - { - if (strcmp(b.name, a.name) > 0) return true; - return false; - } -}; - -class RecMan; - -class StreamReceiver -{ - public: - virtual void streamReceive(ULONG, void*, ULONG)=0; -}; - -class VDR_PacketReceiver : public EDReceiver // implementation in vdr.cc -{ - public: - virtual bool call(void* userTag); - - friend class VDR; - protected: -// ULONG requestTime; - ULONG receiverChannel; - - // If receiverChannel == 1: - ULONG requestSerialNumber; // set by RequestResponse, used in ed_cb_find - VDR_ResponsePacket* save_vresp; // set by ed_cb_call, used in RequestResponse - - // If receiverChannel == 2: - ULONG streamID; - StreamReceiver* streamReceiver; -}; - -class VDR : public Thread_TYPE, public EventDispatcher, public MediaProvider -{ - - public: - const static ULONG VIDEO = 1; - const static ULONG RADIO = 2; - - const static ULONG CHANNEL_REQUEST_RESPONSE = 1; - const static ULONG CHANNEL_STREAM = 2; - const static ULONG CHANNEL_KEEPALIVE = 3; - const static ULONG CHANNEL_NETLOG = 4; - - VDR(); - ~VDR(); - static VDR* getInstance(); - - int init(int port); - int shutdown(); - - void findServers(vector& servers); - void cancelFindingServer(); - void setServerIP(char*); - void setReceiveWindow(size_t size); - int connect(); - void disconnect(); - bool isConnected() { return connected; } - ULONG getChannelNumberWidth() { return channelNumberWidth; } - - // protocol functions - // for the following, if result == false then the connection has died - // doLogin - // getRecordingList - // getChannelsList - // getChannelSchedule - // getRecTimersList - // isConnected can be called after the following to determine if still ok - // deleteRecording - // streamRecording - // positionFromFrameNumber - // streamChannel - // getBlock - // stopStreaming - // configLoad - // configSave - // setEventTimer - - int doLogin(); - bool getRecordingsList(RecMan* recman); - RecInfo* getRecInfo(char* fileName); - int deleteRecording(char* fileName); - char* moveRecording(char* fileName, char* newPath); - ULLONG streamRecording(char* fileName, ULONG* lengthFrames, bool* IsPesRecording); - ULLONG positionFromFrameNumber(ULONG frameNumber); - ULONG frameNumberFromPosition(ULLONG position); - bool getNextIFrame(ULONG frameNumber, ULONG direction, ULLONG* rfilePosition, ULONG* rframeNumber, ULONG* rframeLength); - // Direction: 0=backwards, 1=forwards - MarkList* getMarks(char* fileName); - int deleteTimer(RecTimer* delTimer); - ChannelList* getChannelsList(ULONG type); - int streamChannel(ULONG number, StreamReceiver*); - int streamChannel(ULONG number); - void getChannelPids(Channel* channel); - UCHAR* getBlock(ULLONG position, UINT maxAmount, UINT* amountReceived); - //get image blocks separate - we can do this in parallel - int stopStreaming(); - EventList* getChannelSchedule(ULONG number); - EventList* getChannelSchedule(ULONG number, time_t start, ULONG duration); - int configSave(const char* section, const char* key, const char* value); - char* configLoad(const char* section, const char* key); - ULONG setEventTimer(char* timerString); - RecTimerList* getRecTimersList(); - bool networkLog(const char* buffer); - - /** - * the MediaProvider functions - * - */ - virtual MediaList* getRootList(); - virtual MediaList* getMediaList(const MediaURI * parent); - virtual int openMedium(ULONG channel,const MediaURI *uri,ULLONG * size, ULONG xsize,ULONG ysize); - virtual int getMediaBlock(ULONG channel, unsigned long long offset, unsigned long len, unsigned long * outlen, - unsigned char ** buffer); - virtual int getMediaInfo(ULONG channel, struct MediaInfo * result); - virtual int closeMediaChannel(ULONG channel); - - - I18n::lang_code_list getLanguageList(); - int getLanguageContent(const string code, I18n::trans_table&); - - // end protocol functions - - - // obselete - ULLONG rescanRecording(ULONG* lengthFrames); // FIXME obselete - - - - private: - static VDR* instance; - - VDR_ResponsePacket* RequestResponse(VDR_RequestPacket* request); - UCHAR* getBlock(ULLONG position, UINT maxAmount, UINT* amountReceived, ULONG cmd); - - void connectionDied(); - bool sendKA(ULONG timeStamp); - - Log* logger; - int initted; - int findingServer; - TCP* tcp; - int port; - char serverIP[16]; - bool connected; - ULONG maxChannelNumber; - ULONG channelNumberWidth; - VDR_PacketReceiver* TEMP_SINGLE_VDR_PR; - - - ULONG providerId; - ULONG subRange; - SerializeBuffer * doRequestResponse(SerializeBuffer *in,int cmd); - protected: - - // Thread - void threadMethod(); - void threadPostStopCleanup() {}; - - // EventDispatcher - virtual bool ed_cb_find(EDReceiver* edr, void* userTag); -}; - -#endif - -/* - -index.vdr file format for video: - -For every video frame: -{ - File offset 4 bytes - Picture type 1 byte - File number 1 byte - Zero 2 bytes -} - -Picture types: - -#define NO_PICTURE 0 -#define I_FRAME 1 -#define P_FRAME 2 -#define B_FRAME 3 - - - -Packet formats - -Packet format for an RR channel request: - -4 bytes = channel ID = 1 (request/response channel) -4 bytes = request ID (from serialNumber) -4 bytes = opcode -4 bytes = length of the rest of the packet -? bytes = rest of packet. depends on packet - - -Packet format for an RR channel response: - -4 bytes = channel ID = 1 (request/response channel) -4 bytes = request ID (from serialNumber) -4 bytes = length of the rest of the packet -? bytes = rest of packet. depends on packet - - -Packet format for a stream packet: - -4 bytes = channel ID = 2 (stream channel) -4 bytes = stream ID (from requestID) -4 bytes = length of the stream data (rest of packet) -? bytes = stream data - -*/ - +/* + 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. +*/ + + +// FIXME - This and the protocol are overly complicated now. Sorry. +// I'll clean it up in a couple of releases time... + + +#ifndef VDR_H +#define VDR_H + +#include +#include +#include +#include + +#include "threadsystem.h" + +#include "defines.h" +#include "rectimer.h" +#include "mark.h" +#include "mediaprovider.h" +#include "eventdispatcher.h" +#include "i18n.h" +#include "log.h" + +class TCP; +class Log; +class RecInfo; +class Event; +class Channel; +class VDR_RequestPacket; +class VDR_ResponsePacket; +class SerializeBuffer; + +using namespace std; + +typedef vector EventList; +typedef vector ChannelList; +typedef vector RecTimerList; + +struct VDRServer +{ + char* ip; + char* name; +}; + +struct RecTimerSorter // : public binary_function +{ + bool operator() (const RecTimer* a, const RecTimer* b) + { + return a->startTime < b->startTime; + } +}; + +struct ServerSorter +{ + bool operator() (const VDRServer& a, const VDRServer& b) + { + if (strcmp(b.name, a.name) > 0) return true; + return false; + } +}; + +class RecMan; + +class StreamReceiver +{ + public: + virtual void streamReceive(ULONG, void*, ULONG)=0; +}; + +class VDR_PacketReceiver : public EDReceiver // implementation in vdr.cc +{ + public: + virtual bool call(void* userTag); + + friend class VDR; + protected: +// ULONG requestTime; + ULONG receiverChannel; + + // If receiverChannel == 1: + ULONG requestSerialNumber; // set by RequestResponse, used in ed_cb_find + VDR_ResponsePacket* save_vresp; // set by ed_cb_call, used in RequestResponse + + // If receiverChannel == 2: + ULONG streamID; + StreamReceiver* streamReceiver; +}; + +class VDR : public Thread_TYPE, public EventDispatcher, public MediaProvider, public ExternLogger +{ + + public: + const static ULONG VIDEO = 1; + const static ULONG RADIO = 2; + + const static ULONG CHANNEL_REQUEST_RESPONSE = 1; + const static ULONG CHANNEL_STREAM = 2; + const static ULONG CHANNEL_KEEPALIVE = 3; + const static ULONG CHANNEL_NETLOG = 4; + + VDR(); + ~VDR(); + static VDR* getInstance(); + + int init(int port); + int shutdown(); + + void findServers(vector& servers); + void cancelFindingServer(); + void setServerIP(char*); + void setReceiveWindow(size_t size); + int connect(); + void disconnect(); + bool isConnected() { return connected; } + ULONG getChannelNumberWidth() { return channelNumberWidth; } + + // protocol functions + // for the following, if result == false then the connection has died + // doLogin + // getRecordingList + // getChannelsList + // getChannelSchedule + // getRecTimersList + // isConnected can be called after the following to determine if still ok + // deleteRecording + // streamRecording + // positionFromFrameNumber + // streamChannel + // getBlock + // stopStreaming + // configLoad + // configSave + // setEventTimer + + int doLogin(); + bool getRecordingsList(RecMan* recman); + RecInfo* getRecInfo(char* fileName); + int deleteRecording(char* fileName); + char* moveRecording(char* fileName, char* newPath); + ULLONG streamRecording(char* fileName, ULONG* lengthFrames, bool* IsPesRecording); + ULLONG positionFromFrameNumber(ULONG frameNumber); + ULONG frameNumberFromPosition(ULLONG position); + bool getNextIFrame(ULONG frameNumber, ULONG direction, ULLONG* rfilePosition, ULONG* rframeNumber, ULONG* rframeLength); + // Direction: 0=backwards, 1=forwards + MarkList* getMarks(char* fileName); + int deleteTimer(RecTimer* delTimer); + ChannelList* getChannelsList(ULONG type); + int streamChannel(ULONG number, StreamReceiver*); + int streamChannel(ULONG number); + void getChannelPids(Channel* channel); + UCHAR* getBlock(ULLONG position, UINT maxAmount, UINT* amountReceived); + //get image blocks separate - we can do this in parallel + int stopStreaming(); + EventList* getChannelSchedule(ULONG number); + EventList* getChannelSchedule(ULONG number, time_t start, ULONG duration); + int configSave(const char* section, const char* key, const char* value); + char* configLoad(const char* section, const char* key); + ULONG setEventTimer(char* timerString); + RecTimerList* getRecTimersList(); + bool LogExtern(const char* buffer); + + bool setCharset(int charset); // 1 latin 2 UTF-8 + + /** + * the MediaProvider functions + * + */ + virtual MediaList* getRootList(); + virtual MediaList* getMediaList(const MediaURI * parent); + virtual int openMedium(ULONG channel,const MediaURI *uri,ULLONG * size, ULONG xsize,ULONG ysize); + virtual int getMediaBlock(ULONG channel, unsigned long long offset, unsigned long len, unsigned long * outlen, + unsigned char ** buffer); + virtual int getMediaInfo(ULONG channel, struct MediaInfo * result); + virtual int closeMediaChannel(ULONG channel); + + + I18n::lang_code_list getLanguageList(); + int getLanguageContent(const string code, I18n::trans_table&); + + // end protocol functions + + + // obselete + ULLONG rescanRecording(ULONG* lengthFrames); // FIXME obselete + + + + private: + static VDR* instance; + + VDR_ResponsePacket* RequestResponse(VDR_RequestPacket* request); + UCHAR* getBlock(ULLONG position, UINT maxAmount, UINT* amountReceived, ULONG cmd); + + void connectionDied(); + bool sendKA(ULONG timeStamp); + + Log* logger; + int initted; + int findingServer; + TCP* tcp; + int port; + char serverIP[16]; + bool connected; + ULONG maxChannelNumber; + ULONG channelNumberWidth; + VDR_PacketReceiver* TEMP_SINGLE_VDR_PR; + + + ULONG providerId; + ULONG subRange; + SerializeBuffer * doRequestResponse(SerializeBuffer *in,int cmd); + protected: + + // Thread + void threadMethod(); + void threadPostStopCleanup() {}; + + // EventDispatcher + virtual bool ed_cb_find(EDReceiver* edr, void* userTag); +}; + +#endif + +/* + +index.vdr file format for video: + +For every video frame: +{ + File offset 4 bytes + Picture type 1 byte + File number 1 byte + Zero 2 bytes +} + +Picture types: + +#define NO_PICTURE 0 +#define I_FRAME 1 +#define P_FRAME 2 +#define B_FRAME 3 + + + +Packet formats + +Packet format for an RR channel request: + +4 bytes = channel ID = 1 (request/response channel) +4 bytes = request ID (from serialNumber) +4 bytes = opcode +4 bytes = length of the rest of the packet +? bytes = rest of packet. depends on packet + + +Packet format for an RR channel response: + +4 bytes = channel ID = 1 (request/response channel) +4 bytes = request ID (from serialNumber) +4 bytes = length of the rest of the packet +? bytes = rest of packet. depends on packet + + +Packet format for a stream packet: + +4 bytes = channel ID = 2 (stream channel) +4 bytes = stream ID (from requestID) +4 bytes = length of the stream data (rest of packet) +? bytes = stream data + +*/ + diff --git a/vdrcommand.h b/vdrcommand.h index 9065a5c..486f004 100644 --- a/vdrcommand.h +++ b/vdrcommand.h @@ -1,163 +1,164 @@ -/* - Copyright 2004-2005 Chris Tallon, Andreas Vogel - - 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 VDRCOMMAND_H -#define VDRCOMMAND_H - -#include "defines.h" -#include "serialize.h" -#include "media.h" - -/** - * data holder for VDR commands - * it's only important to add serializable objects - * in the same order on both sides - */ - -//until we really have response - commands we simply take -//the request+this flag for responses -//not really necessary but for checks it's better to have a command ID at least in some responses -const static ULONG VDR_RESPONSE_FLAG =0x1000000; - -//as this header is only included by vdr.cc the constants are this way private -//but can easily be used on the server side as well - -const static ULONG VDR_LOGIN = 1; -const static ULONG VDR_GETRECORDINGLIST = 2; -const static ULONG VDR_DELETERECORDING = 3; -const static ULONG VDR_GETCHANNELLIST = 5; -const static ULONG VDR_STREAMCHANNEL = 6; -const static ULONG VDR_GETBLOCK = 7; -const static ULONG VDR_STOPSTREAMING = 8; -const static ULONG VDR_STREAMRECORDING = 9; -const static ULONG VDR_GETCHANNELSCHEDULE = 10; -const static ULONG VDR_CONFIGSAVE = 11; -const static ULONG VDR_CONFIGLOAD = 12; -const static ULONG VDR_RESCANRECORDING = 13; // FIXME obselete -const static ULONG VDR_GETTIMERS = 14; -const static ULONG VDR_SETTIMER = 15; -const static ULONG VDR_POSFROMFRAME = 16; -const static ULONG VDR_FRAMEFROMPOS = 17; -const static ULONG VDR_MOVERECORDING = 18; -const static ULONG VDR_GETNEXTIFRAME = 19; -const static ULONG VDR_GETRECINFO = 20; -const static ULONG VDR_GETMARKS = 21; -const static ULONG VDR_GETCHANNELPIDS = 22; -const static ULONG VDR_DELETETIMER = 23; -const static ULONG VDR_GETLANGUAGELIST = 33; -const static ULONG VDR_GETLANGUAGECONTENT = 34; -const static ULONG VDR_GETMEDIALIST = 30; -const static ULONG VDR_OPENMEDIA = 31; -const static ULONG VDR_GETMEDIABLOCK = 32; -const static ULONG VDR_GETMEDIAINFO = 35; -const static ULONG VDR_CLOSECHANNEL = 36; - -class VDR_Command : public SerializableList { - public: - VDR_Command(const ULONG cmd) { - command=cmd; - addParam(&command); - } - virtual ~VDR_Command(){} - ULONG command; -}; - -class VDR_GetMediaListRequest : public VDR_Command { - public: - VDR_GetMediaListRequest(MediaURI *root) :VDR_Command(VDR_GETMEDIALIST) { - addParam(root); - } -}; - -class VDR_GetMediaListResponse : public VDR_Command { - public: - VDR_GetMediaListResponse(ULONG *flags,MediaList *m) : VDR_Command(VDR_GETMEDIALIST|VDR_RESPONSE_FLAG){ - addParam(flags); - addParam(m); - } -}; - -class VDR_OpenMediumRequest : public VDR_Command { - public: - VDR_OpenMediumRequest(ULONG *channel,MediaURI *u,ULONG *xsize, ULONG *ysize) : - VDR_Command(VDR_OPENMEDIA) { - addParam(channel); - addParam(u); - addParam(xsize); - addParam(ysize); - } -}; -class VDR_OpenMediumResponse : public VDR_Command { - public: - VDR_OpenMediumResponse(ULONG *flags,ULLONG *size) : - VDR_Command(VDR_OPENMEDIA|VDR_RESPONSE_FLAG) { - addParam(flags); - addParam(size); - } -}; -class VDR_GetMediaBlockRequest : public VDR_Command { - public: - VDR_GetMediaBlockRequest(ULONG * channel, ULLONG *pos, ULONG *max): - VDR_Command(VDR_GETMEDIABLOCK) { - addParam(channel); - addParam(pos); - addParam(max); - } -}; - -//no response class for GetMediaBlock - - -class VDR_CloseMediaChannelRequest : public VDR_Command { - public: - VDR_CloseMediaChannelRequest(ULONG * channel): - VDR_Command(VDR_CLOSECHANNEL) { - addParam(channel); - } -}; - -class VDR_CloseMediaChannelResponse : public VDR_Command { - public: - VDR_CloseMediaChannelResponse(ULONG * flags): - VDR_Command(VDR_CLOSECHANNEL|VDR_RESPONSE_FLAG) { - addParam(flags); - } -}; - -class VDR_GetMediaInfoRequest : public VDR_Command { - public: - VDR_GetMediaInfoRequest(ULONG * channel): - VDR_Command(VDR_GETMEDIAINFO) { - addParam(channel); - } -}; -class VDR_GetMediaInfoResponse : public VDR_Command { - public: - VDR_GetMediaInfoResponse(ULONG * flags,MediaInfo *info): - VDR_Command(VDR_GETMEDIAINFO|VDR_RESPONSE_FLAG) { - addParam(flags); - addParam(info); - } -}; - - - - -#endif +/* + Copyright 2004-2005 Chris Tallon, Andreas Vogel + + 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 VDRCOMMAND_H +#define VDRCOMMAND_H + +#include "defines.h" +#include "serialize.h" +#include "media.h" + +/** + * data holder for VDR commands + * it's only important to add serializable objects + * in the same order on both sides + */ + +//until we really have response - commands we simply take +//the request+this flag for responses +//not really necessary but for checks it's better to have a command ID at least in some responses +const static ULONG VDR_RESPONSE_FLAG =0x1000000; + +//as this header is only included by vdr.cc the constants are this way private +//but can easily be used on the server side as well + +const static ULONG VDR_LOGIN = 1; +const static ULONG VDR_GETRECORDINGLIST = 2; +const static ULONG VDR_DELETERECORDING = 3; +const static ULONG VDR_GETCHANNELLIST = 5; +const static ULONG VDR_STREAMCHANNEL = 6; +const static ULONG VDR_GETBLOCK = 7; +const static ULONG VDR_STOPSTREAMING = 8; +const static ULONG VDR_STREAMRECORDING = 9; +const static ULONG VDR_GETCHANNELSCHEDULE = 10; +const static ULONG VDR_CONFIGSAVE = 11; +const static ULONG VDR_CONFIGLOAD = 12; +const static ULONG VDR_RESCANRECORDING = 13; // FIXME obselete +const static ULONG VDR_GETTIMERS = 14; +const static ULONG VDR_SETTIMER = 15; +const static ULONG VDR_POSFROMFRAME = 16; +const static ULONG VDR_FRAMEFROMPOS = 17; +const static ULONG VDR_MOVERECORDING = 18; +const static ULONG VDR_GETNEXTIFRAME = 19; +const static ULONG VDR_GETRECINFO = 20; +const static ULONG VDR_GETMARKS = 21; +const static ULONG VDR_GETCHANNELPIDS = 22; +const static ULONG VDR_DELETETIMER = 23; +const static ULONG VDR_GETLANGUAGELIST = 33; +const static ULONG VDR_GETLANGUAGECONTENT = 34; +const static ULONG VDR_SETCHARSET = 37; +const static ULONG VDR_GETMEDIALIST = 30; +const static ULONG VDR_OPENMEDIA = 31; +const static ULONG VDR_GETMEDIABLOCK = 32; +const static ULONG VDR_GETMEDIAINFO = 35; +const static ULONG VDR_CLOSECHANNEL = 36; + +class VDR_Command : public SerializableList { + public: + VDR_Command(const ULONG cmd) { + command=cmd; + addParam(&command); + } + virtual ~VDR_Command(){} + ULONG command; +}; + +class VDR_GetMediaListRequest : public VDR_Command { + public: + VDR_GetMediaListRequest(MediaURI *root) :VDR_Command(VDR_GETMEDIALIST) { + addParam(root); + } +}; + +class VDR_GetMediaListResponse : public VDR_Command { + public: + VDR_GetMediaListResponse(ULONG *flags,MediaList *m) : VDR_Command(VDR_GETMEDIALIST|VDR_RESPONSE_FLAG){ + addParam(flags); + addParam(m); + } +}; + +class VDR_OpenMediumRequest : public VDR_Command { + public: + VDR_OpenMediumRequest(ULONG *channel,MediaURI *u,ULONG *xsize, ULONG *ysize) : + VDR_Command(VDR_OPENMEDIA) { + addParam(channel); + addParam(u); + addParam(xsize); + addParam(ysize); + } +}; +class VDR_OpenMediumResponse : public VDR_Command { + public: + VDR_OpenMediumResponse(ULONG *flags,ULLONG *size) : + VDR_Command(VDR_OPENMEDIA|VDR_RESPONSE_FLAG) { + addParam(flags); + addParam(size); + } +}; +class VDR_GetMediaBlockRequest : public VDR_Command { + public: + VDR_GetMediaBlockRequest(ULONG * channel, ULLONG *pos, ULONG *max): + VDR_Command(VDR_GETMEDIABLOCK) { + addParam(channel); + addParam(pos); + addParam(max); + } +}; + +//no response class for GetMediaBlock + + +class VDR_CloseMediaChannelRequest : public VDR_Command { + public: + VDR_CloseMediaChannelRequest(ULONG * channel): + VDR_Command(VDR_CLOSECHANNEL) { + addParam(channel); + } +}; + +class VDR_CloseMediaChannelResponse : public VDR_Command { + public: + VDR_CloseMediaChannelResponse(ULONG * flags): + VDR_Command(VDR_CLOSECHANNEL|VDR_RESPONSE_FLAG) { + addParam(flags); + } +}; + +class VDR_GetMediaInfoRequest : public VDR_Command { + public: + VDR_GetMediaInfoRequest(ULONG * channel): + VDR_Command(VDR_GETMEDIAINFO) { + addParam(channel); + } +}; +class VDR_GetMediaInfoResponse : public VDR_Command { + public: + VDR_GetMediaInfoResponse(ULONG * flags,MediaInfo *info): + VDR_Command(VDR_GETMEDIAINFO|VDR_RESPONSE_FLAG) { + addParam(flags); + addParam(info); + } +}; + + + + +#endif diff --git a/vdrrequestpacket.cc b/vdrrequestpacket.cc index bdf5f19..5b98ada 100644 --- a/vdrrequestpacket.cc +++ b/vdrrequestpacket.cc @@ -70,10 +70,27 @@ bool VDR_RequestPacket::init(ULONG topcode, bool setUserDataLength, ULONG userDa serialNumber = serialNumberCounter++; opcode = topcode; - *(ULONG*)&buffer[0] = htonl(channel); - *(ULONG*)&buffer[4] = htonl(serialNumber); + int pos=0; + buffer[pos++]=(channel>>24)&0xff; + buffer[pos++]=(channel>>16)&0xff; + buffer[pos++]=(channel>>8)&0xff; + buffer[pos++]=channel &0xff; + buffer[pos++]=(serialNumber>>24)&0xff; + buffer[pos++]=(serialNumber>>16)&0xff; + buffer[pos++]=(serialNumber>>8)&0xff; + buffer[pos++]=serialNumber &0xff; + buffer[pos++]=(opcode>>24)&0xff; + buffer[pos++]=(opcode>>16)&0xff; + buffer[pos++]=(opcode>>8)&0xff; + buffer[pos++]=opcode &0xff; + buffer[pos++]=(userDataLength>>24)&0xff; + buffer[pos++]=(userDataLength>>16)&0xff; + buffer[pos++]=(userDataLength>>8)&0xff; + buffer[pos++]=userDataLength &0xff; + + /**(ULONG*)&buffer[4] = htonl(serialNumber); *(ULONG*)&buffer[8] = htonl(opcode); - *(ULONG*)&buffer[userDataLenPos] = htonl(userDataLength); + *(ULONG*)&buffer[userDataLenPos] = htonl(userDataLength);*/ bufUsed = headerLength; return true; @@ -84,35 +101,77 @@ bool VDR_RequestPacket::copyin(const UCHAR* src, ULONG len) if (!checkExtend(len)) return false; memcpy(buffer + bufUsed, src, len); bufUsed += len; - if (!lengthSet) *(ULONG*)&buffer[userDataLenPos] = htonl(bufUsed - headerLength); + if (!lengthSet) { + int pos=userDataLenPos; + ULONG tocopy=bufUsed - headerLength; + buffer[pos++]=(tocopy>>24)&0xff; + buffer[pos++]=(tocopy>>16)&0xff; + buffer[pos++]=(tocopy>>8)&0xff; + buffer[pos++]=tocopy &0xff; + } return true; } + + bool VDR_RequestPacket::addString(const char* string) { ULONG len = strlen(string) + 1; if (!checkExtend(len)) return false; memcpy(buffer + bufUsed, string, len); bufUsed += len; - if (!lengthSet) *(ULONG*)&buffer[userDataLenPos] = htonl(bufUsed - headerLength); + if (!lengthSet) { + int pos=userDataLenPos; + ULONG tocopy=bufUsed - headerLength; + buffer[pos++]=(tocopy>>24)&0xff; + buffer[pos++]=(tocopy>>16)&0xff; + buffer[pos++]=(tocopy>>8)&0xff; + buffer[pos++]=tocopy &0xff; + } return true; } bool VDR_RequestPacket::addULONG(ULONG ul) { if (!checkExtend(sizeof(ULONG))) return false; - *(ULONG*)&buffer[bufUsed] = htonl(ul); - bufUsed += sizeof(ULONG); - if (!lengthSet) *(ULONG*)&buffer[userDataLenPos] = htonl(bufUsed - headerLength); +// *(ULONG*)&buffer[bufUsed] = htonl(ul); + + buffer[bufUsed++]=(ul>>24)&0xff; + buffer[bufUsed++]=(ul>>16)&0xff; + buffer[bufUsed++]=(ul>>8)&0xff; + buffer[bufUsed++]=ul &0xff; + + if (!lengthSet) { + int pos=userDataLenPos; + ULONG tocopy=bufUsed - headerLength; + buffer[pos++]=(tocopy>>24)&0xff; + buffer[pos++]=(tocopy>>16)&0xff; + buffer[pos++]=(tocopy>>8)&0xff; + buffer[pos++]=tocopy &0xff; + } return true; } bool VDR_RequestPacket::addULLONG(ULLONG ull) { if (!checkExtend(sizeof(ULLONG))) return false; - *(ULLONG*)&buffer[bufUsed] = htonll(ull); - bufUsed += sizeof(ULLONG); - if (!lengthSet) *(ULONG*)&buffer[userDataLenPos] = htonl(bufUsed - headerLength); + buffer[bufUsed++]=(ull>>56)&0xff; + buffer[bufUsed++]=(ull>>48)&0xff; + buffer[bufUsed++]=(ull>>40)&0xff; + buffer[bufUsed++]=(ull>>32)&0xff; + buffer[bufUsed++]=(ull>>24)&0xff; + buffer[bufUsed++]=(ull>>16)&0xff; + buffer[bufUsed++]=(ull>>8)&0xff; + buffer[bufUsed++]=ull &0xff; + + if (!lengthSet) { + int pos=userDataLenPos; + ULONG tocopy=bufUsed - headerLength; + buffer[pos++]=(tocopy>>24)&0xff; + buffer[pos++]=(tocopy>>16)&0xff; + buffer[pos++]=(tocopy>>8)&0xff; + buffer[pos++]=tocopy &0xff; + } return true; } diff --git a/vdrresponsepacket.cc b/vdrresponsepacket.cc index 7d156c0..8a14fd2 100644 --- a/vdrresponsepacket.cc +++ b/vdrresponsepacket.cc @@ -1,140 +1,150 @@ -/* - Copyright 2007 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 "vdrresponsepacket.h" - -#include "vdr.h" -#include "tcp.h" - -VDR_ResponsePacket::VDR_ResponsePacket() -{ - userDataLength = 0; - packetPos = 0; - userData = NULL; - ownBlock = true; - - channelID = 0; - - requestID = 0; - streamID = 0; - - flag = 0; -} - -VDR_ResponsePacket::~VDR_ResponsePacket() -{ - if (!ownBlock) return; // don't free if it's a getblock - - if (userData) free(userData); -} - -void VDR_ResponsePacket::setResponse(ULONG trequestID, UCHAR* tuserData, ULONG tuserDataLength) -{ - channelID = VDR::CHANNEL_REQUEST_RESPONSE; - requestID = trequestID; - userData = tuserData; - userDataLength = tuserDataLength; -} - -void VDR_ResponsePacket::setStream(ULONG tstreamID, ULONG tflag, UCHAR* tuserData, ULONG tuserDataLength) -{ - channelID = VDR::CHANNEL_STREAM; - streamID = tstreamID; - flag = tflag; - userData = tuserData; - userDataLength = tuserDataLength; -} - -bool VDR_ResponsePacket::end() -{ - return (packetPos >= userDataLength); -} - -void VDR_ResponsePacket::dumpUD() -{ - TCP::dump(userData, userDataLength); -} - -int VDR_ResponsePacket::serverError() -{ - if ((packetPos == 0) && (userDataLength == 4) && !ntohl(*(ULONG*)userData)) return 1; - else return 0; -} - -char* VDR_ResponsePacket::extractString() -{ - if (serverError()) return NULL; - - int length = strlen((char*)&userData[packetPos]); - if ((packetPos + length) > userDataLength) return NULL; - char* str = new char[length + 1]; - strcpy(str, (char*)&userData[packetPos]); - packetPos += length + 1; - return str; -} - -UCHAR VDR_ResponsePacket::extractUCHAR() -{ - if ((packetPos + sizeof(UCHAR)) > userDataLength) return 0; - UCHAR uc = userData[packetPos]; - packetPos += sizeof(UCHAR); - return uc; -} - -ULONG VDR_ResponsePacket::extractULONG() -{ - if ((packetPos + sizeof(ULONG)) > userDataLength) return 0; - ULONG ul = ntohl(*(ULONG*)&userData[packetPos]); - packetPos += sizeof(ULONG); - return ul; -} - -ULLONG VDR_ResponsePacket::extractULLONG() -{ - if ((packetPos + sizeof(ULLONG)) > userDataLength) return 0; - ULLONG ull = ntohll(*(ULLONG*)&userData[packetPos]); - packetPos += sizeof(ULLONG); - return ull; -} - -double VDR_ResponsePacket::extractdouble() -{ - if ((packetPos + sizeof(ULLONG)) > userDataLength) return 0; - ULLONG ull = ntohll(*(ULLONG*)&userData[packetPos]); - double d; - memcpy(&d,&ull,sizeof(double)); - packetPos += sizeof(ULLONG); - return d; -} - -long VDR_ResponsePacket::extractLONG() -{ - if ((packetPos + sizeof(long)) > userDataLength) return 0; - long l = ntohl(*(long*)&userData[packetPos]); - packetPos += sizeof(long); - return l; -} - -UCHAR* VDR_ResponsePacket::getUserData() -{ - ownBlock = false; - return userData; -} - +/* + Copyright 2007 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 "vdrresponsepacket.h" + +#include "vdr.h" +#include "tcp.h" + +VDR_ResponsePacket::VDR_ResponsePacket() +{ + userDataLength = 0; + packetPos = 0; + userData = NULL; + ownBlock = true; + + channelID = 0; + + requestID = 0; + streamID = 0; + + flag = 0; +} + +VDR_ResponsePacket::~VDR_ResponsePacket() +{ + if (!ownBlock) return; // don't free if it's a getblock + + if (userData) free(userData); +} + +void VDR_ResponsePacket::setResponse(ULONG trequestID, UCHAR* tuserData, ULONG tuserDataLength) +{ + channelID = VDR::CHANNEL_REQUEST_RESPONSE; + requestID = trequestID; + userData = tuserData; + userDataLength = tuserDataLength; +} + +void VDR_ResponsePacket::setStream(ULONG tstreamID, ULONG tflag, UCHAR* tuserData, ULONG tuserDataLength) +{ + channelID = VDR::CHANNEL_STREAM; + streamID = tstreamID; + flag = tflag; + userData = tuserData; + userDataLength = tuserDataLength; +} + +bool VDR_ResponsePacket::end() +{ + return (packetPos >= userDataLength); +} + +void VDR_ResponsePacket::dumpUD() +{ + TCP::dump(userData, userDataLength); +} + +int VDR_ResponsePacket::serverError() +{ + if ((packetPos == 0) && (userDataLength == 4) && !ntohl(*(ULONG*)userData)) return 1; + else return 0; +} + +char* VDR_ResponsePacket::extractString() +{ + if (serverError()) return NULL; + + int length = strlen((char*)&userData[packetPos]); + if ((packetPos + length) > userDataLength) return NULL; + char* str = new char[length + 1]; + strcpy(str, (char*)&userData[packetPos]); + packetPos += length + 1; + return str; +} + +UCHAR VDR_ResponsePacket::extractUCHAR() +{ + if ((packetPos + sizeof(UCHAR)) > userDataLength) return 0; + UCHAR uc = userData[packetPos]; + packetPos += sizeof(UCHAR); + return uc; +} + +ULONG VDR_ResponsePacket::extractULONG() +{ + if ((packetPos + sizeof(ULONG)) > userDataLength) return 0; + ULONG ul = userData[packetPos++]<<24; + ul|= userData[packetPos++]<<16; + ul|= userData[packetPos++]<<8; + ul|= userData[packetPos++]; + //packetPos += sizeof(ULONG); + return ul; +} + +ULLONG VDR_ResponsePacket::extractULLONG() +{ + if ((packetPos + sizeof(ULLONG)) > userDataLength) return 0; + ULLONG ull= ((ULLONG)userData[packetPos++])<<56; + ull|= ((ULLONG)userData[packetPos++])<<48; + ull|= ((ULLONG)userData[packetPos++])<<40; + ull|= ((ULLONG)userData[packetPos++])<<32; + ull|= ((ULLONG)userData[packetPos++])<<24; + ull|= ((ULLONG)userData[packetPos++])<<16; + ull|= ((ULLONG)userData[packetPos++])<<8; + ull|= ((ULLONG)userData[packetPos++]); + return ull; +} + +double VDR_ResponsePacket::extractdouble() +{ + if ((packetPos + sizeof(ULLONG)) > userDataLength) return 0; + ULLONG ull = extractULLONG(); + double d; + memcpy(&d,&ull,sizeof(double)); + return d; +} + +long VDR_ResponsePacket::extractLONG() +{ + if ((packetPos + sizeof(long)) > userDataLength) return 0; + long l = userData[packetPos++]<<24; + l|= userData[packetPos++]<<16; + l|= userData[packetPos++]<<8; + l|= userData[packetPos++]; + return l; +} + +UCHAR* VDR_ResponsePacket::getUserData() +{ + ownBlock = false; + return userData; +} + diff --git a/vepg.cc b/vepg.cc index d90a53b..5505eb8 100644 --- a/vepg.cc +++ b/vepg.cc @@ -1,824 +1,825 @@ -/* - Copyright 2005 Brian Walton - - 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. -*/ -/* - vepg presents a 2 dimensional electronic programme guide with channels down - the y axis and time along the x axis. - Programmes are layed on the x axis as alterate coloured blocks with as much - of the programme title as will fit inside the block shown as text. - Up and down commands step through the channels whilst left and right commands - move through the programmes of the currently selected channel. - When a programme is selected, it highlights in the grid and full programe details - (start time, title and description) are displayed in an area at te top left of the screen. - Any currently programmed timers will display in the grid and in the orogramme detail window as red - It is possible to select a programme to be recorded by pressing the record button. - The video stream currently being viewed is shown as quarter screen in the top right. -*/ - -#include "vepg.h" - -#include "remote.h" -#include "vchannellist.h" -#include "command.h" -#include "video.h" -#include "vepgsettimer.h" -#include "timers.h" -#include "wsymbol.h" -#include "message.h" -#include "colour.h" -#include "boxstack.h" -#include "channel.h" -#include "i18n.h" -#include "log.h" - -VEpg* VEpg::instance = NULL; - -VEpg::VEpg(void* tparent, UINT tcurrentChannelIndex, ULONG streamType) -{ - instance = this; - currentChannelIndex = tcurrentChannelIndex; - - // PAL / NTSC sizes ----------------------- - - int xsize, ysize; - int xpos, ypos; - int summaryLines, summaryLowerPadding; - int chanNameYpos; - //UINT gridRows; // is a class member - - if (Video::getInstance()->getFormat() == Video::PAL) - { - xsize = 632; - ysize = 541; - xpos = 60; - ypos = 16; - summaryLines = 8; - summaryLowerPadding = 18; - chanNameYpos = 244; - gridRows = 7; - } - else - { - xsize = 632; - ysize = 452; - xpos = 50; - ypos = 10; - summaryLines = 6; - summaryLowerPadding = 28; - chanNameYpos = 206; - gridRows = 5; - } - - // initialise variables and pointers - boxstack = BoxStack::getInstance(); - parent = tparent; - eventList = NULL; - chanList = VDR::getInstance()->getChannelsList(streamType); //TODO want to be able to display video and radio together - e = 0; - - for(UINT listIndex = 0; listIndex < gridRows; listIndex++) - { - // initialise array of pointers to eventlist structures - eventLista[listIndex] = NULL; - } - - // Create pallet on which to paint our epg view and position it in centre of screen. - // Need to reduce size to deal with overscanning TVs. - - setSize(xsize, ysize); - createBuffer(); - setPosition(xpos, ypos); - - // beautify -// Colour transparent = Colour(0, 0, 0, 0); -// setBackgroundColour(transparent); - -// progTitle.setSurface(surface); - progTitle.setPosition(0,0); - progTitle.setSize(300,(Surface::getFontHeight() + 4) * 2 + 16); //paragraph line seperation is 4 pixels - progTitle.setBackgroundColour(Colour::TITLEBARBACKGROUND); - progTitle.setTextPos(5, 16); - progTitle.setGap(4); - add(&progTitle); - -// progInfo.setSurface(surface); - progInfo.setBackgroundColour(Colour::VIEWBACKGROUND); - progInfo.setPosition(0, progTitle.getY2()); - progInfo.setSize(300,((Surface::getFontHeight() + 4) * summaryLines) + summaryLowerPadding); - progInfo.setGap(4); - add(&progInfo); - -// chanName.setSurface(surface); - chanName.setSize(510, (Surface::getFontHeight() + 4)); - chanName.setPosition(305, chanNameYpos); - Colour t1(0, 0, 0, 90); - chanName.setBackgroundColour(t1); - add(&chanName); - - // create area to display list of channels -// chanListbox.setSurface(surface); // add channel list - chanListbox.setPosition(0, progInfo.getY2() + Surface::getFontHeight() + 8); // position channel list - chanListbox.setSize(150, ((Surface::getFontHeight() + 2) * gridRows) + 5); //listbox line seperation is 2 pixels - chanListbox.setGap(2); - add(&chanListbox); - - // populate channel list - if (chanList) - { - Channel* chan; - int first = 1; - for (UINT i = 0; i < chanList->size(); i++) - { - chan = (*chanList)[i]; - if (i == currentChannelIndex) - first = 1; - chan->index = chanListbox.addOption(chan->name, 0, first); - first = 0; - } - chanName.setText((*chanList)[chanListbox.getCurrentOption()]->name); - } - - listTop = chanListbox.getTopOption(); - chanListbox.draw(); // doing this to allow chanListbox.getBottomOption() in updateEventList() to work - time(<ime); // set ltime to now - ltime = prevHour(<ime); // set ltime to previous hour TODO make this half hour? - time(&selTime); // set selTime to now - updateEventList(); // get list of programmes -} - -void VEpg::preDelete() -{ - Timers::getInstance()->cancelTimer(this, 1); -} - -VEpg::~VEpg() -{ - - instance = NULL; - - for(UINT listIndex = 0; listIndex < gridRows; listIndex++) - { - if (eventLista[listIndex]) - { - (eventLista)[listIndex]->clear(); - delete eventLista[listIndex]; - } - } - // delete [] eventLista; // FIXME - - // destroy dynamically allocated memory -} - -VEpg* VEpg::getInstance() -{ - return instance; -} - -void VEpg::setInfo(Event* event) -{ - time_t t; - struct tm* btime; // to hold programme start and end time - char timeString[9]; // to hold programme start and end time - int length = strlen(event->title); // calculate length of programme title string - char* title = new char[length + 15]; // create string to hold start time, end time and programme title - btime = localtime((time_t*)&event->time); //get programme start time -#ifndef _MSC_VER - strftime(timeString, 9, "%0H:%0M - ", btime); // and format it as hh:mm - -#else - strftime(timeString, 9, "%H:%M - ", btime); // and format it as hh:mm - -#endif - strcpy(title, timeString); // put it in our buffer - t = event->time + event->duration; //get programme end time - btime = localtime(&t); -#ifndef _MSC_VER - strftime(timeString, 7, "%0H:%0M ", btime); // and format it as hh:mm - -#else - strftime(timeString, 7, "%H:%M ", btime); // and format it as hh:mm - -#endif - strcat(title, timeString); // put it in our buffer - strcat(title, event->title); // then add the programme title - progTitle.setText(title); // sput this sring in our text box - length = strlen(event->description); - char* info = new char[length + 1]; // create programme detail string - strcpy(info, event->description); - progInfo.setText(info); // show programme detail string -// destroy dynamically allocated memory - delete[] info; - delete[] title; -} - -void VEpg::draw() -{ -// View::draw(); // draw pallet - // beautify - Colour transparent = Colour(0, 0, 0, 0); - fillColour(transparent); - - - // Moved all the dynamic data drawing to a seperate function - - // Display the status and key stuff at the bottom - - UINT keyx = chanListbox.getRootBoxOffsetX(); - UINT keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2; - Colour ref1 = Colour(100, 100, 100, 255); - rectangle(keyx, keyy, 605, Surface::getFontHeight() * 2 + 14, ref1); - - WSymbol w; - TEMPADD(&w); - - w.nextSymbol = WSymbol::LEFTARROW; - w.setPosition(keyx + 1, keyy + 20); - w.draw(); - - w.nextSymbol = WSymbol::UP; - w.setPosition(keyx + 26, keyy + 3); - w.draw(); - - w.nextSymbol = WSymbol::DOWN; - w.setPosition(keyx + 26, keyy + 36); - w.draw(); - - w.nextSymbol = WSymbol::RIGHTARROW; - w.setPosition(keyx + 50, keyy + 20); - w.draw(); - - drawText(tr("OK"), keyx + 18, keyy + 20, Colour::LIGHTTEXT); - - Colour ref2 = Colour(200, 0, 0, 255); - rectangle(keyx + 72, keyy + 4, 104, Surface::getFontHeight() + 2, ref2); - drawText(tr("Page up"), keyx + 74, keyy + 5, Colour::LIGHTTEXT); - - Colour ref3 = Colour(0, 200, 0, 255); - rectangle(keyx + 72, keyy + Surface::getFontHeight() + 8, 104, Surface::getFontHeight() + 2, ref3); - drawText(tr("Page down"), keyx + 74, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT); - - Colour ref4 = Colour(200, 200, 0, 255); - rectangle(keyx + 180, keyy + 4, 104, Surface::getFontHeight() + 2, ref4); - drawText(tr("-24 hours"), keyx + 182, keyy + 5, Colour::LIGHTTEXT); - - Colour ref5 = Colour(0, 0, 200, 255); - rectangle(keyx + 180, keyy + Surface::getFontHeight() + 8, 104, Surface::getFontHeight() + 2, ref5); - drawText(tr("+24 hours"), keyx + 182, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT); - - Colour ref6 = Colour(180, 180, 180, 255); - rectangle(keyx + 290, keyy + 4, 180, Surface::getFontHeight() + 2, ref6); - drawText(tr("Guide / Back: Close"), keyx + 292 , keyy + 5, Colour::LIGHTTEXT); - - Colour ref7 = Colour(180, 180, 180, 255); - rectangle(keyx + 290, keyy + Surface::getFontHeight() + 8, 180, Surface::getFontHeight() + 2, ref7); - Colour red = Colour(130, 0, 0); - drawText(tr("Rec: Set timer"), keyx + 292, keyy + Surface::getFontHeight() + 9, red); - - Colour ref8 = Colour(180, 180, 180, 255); - rectangle(keyx + 474, keyy + 4, 128, Surface::getFontHeight() + 2, ref8); - w.nextSymbol = WSymbol::PLAY; - w.setPosition(keyx + 476, keyy + 5); - w.draw(); - drawText(tr("Sel channel"), keyx + 496, keyy + 5, Colour::LIGHTTEXT); - - Colour ref9 = Colour(180, 180, 180, 255); - rectangle(keyx + 474, keyy + Surface::getFontHeight() + 8, 128, Surface::getFontHeight() + 2, ref9); - drawText(tr("Go: Preview"), keyx + 476, keyy + Surface::getFontHeight() + 9, Colour::LIGHTTEXT); - - - // Draw all the dynamic data - drawData(); -} - -void VEpg::drawData() -{ - // Not doing View::draw() every time causes - // things not to be cleared off the surface properly - // So, blank out the data area first - - rectangle( - chanListbox.getRootBoxOffsetX(), - chanListbox.getRootBoxOffsetY() - Surface::getFontHeight() - 3, - 155 + WINDOW_WIDTH * MINUTE_SCALE, - chanListbox.getHeight() + Surface::getFontHeight() + 4, - Colour::BLACK); - - chanListbox.draw(); - drawgrid(); - chanName.draw(); // TODO this should be dealt with by vvideolive - - progTitle.draw(); - progInfo.draw(); - - // Set timer to redraw to move the current time bar - time_t t, dt; - time(&t); - dt = 60 - (t % 60); - if (dt == 0) dt = 60; - dt += t; - Timers::getInstance()->setTimerT(this, 1, dt); -} - -void VEpg::timercall(int clientReference) -{ - drawData(); - boxstack->update(this); -} - -int VEpg::handleCommand(int command) -{ - switch(command) - { - case Remote::DF_UP: - case Remote::UP: - { // cursor up the channel list - chanListbox.up(); - drawData(); - boxstack->update(this); - return 2; - } - case Remote::DF_DOWN: - case Remote::DOWN: - { // cursor down the channel list - Log::getInstance()->log("VEPG", Log::DEBUG, "Down start"); - - chanListbox.down(); - drawData(); - boxstack->update(this); - Log::getInstance()->log("VEPG", Log::DEBUG, "Down end"); - - return 2; - } - case Remote::DF_LEFT: - case Remote::LEFT: - { // cursor left through time - selTime = thisEvent.time - 1; - drawData(); - boxstack->update(this); - return 2; - } - case Remote::DF_RIGHT: - case Remote::RIGHT: - { - // cursor right through time - selTime = thisEvent.time + thisEvent.duration; - drawData(); - boxstack->update(this); - return 2; - } - case Remote::RED: - { - // cursor up one page - chanListbox.pageUp(); - drawData(); - boxstack->update(this); - return 2; - } - case Remote::GREEN: - { - // cursor down one page - chanListbox.pageDown(); - drawData(); - boxstack->update(this); - return 2; - } - case Remote::BLUE: - { - // step forward 24 hours - selTime += 24 * 60 * 60; - drawData(); - boxstack->update(this); - return 2; - } - case Remote::YELLOW: - { - // step forward 24 hours - selTime -= 24 * 60 * 60; - drawData(); - boxstack->update(this); - return 2; - } - case Remote::RECORD: - { - if (!chanList) return 2; - Log::getInstance()->log("VEPG", Log::DEBUG, "ID %lu TIME %lu DURATION %lu TITLE %s", thisEvent.id, thisEvent.time, thisEvent.duration, thisEvent.title); - VEpgSetTimer* vs = new VEpgSetTimer(&thisEvent, (*chanList)[chanListbox.getCurrentOption()]); - vs->draw(); - boxstack->add(vs); - boxstack->update(vs); - return 2; - } - case Remote::PLAY: - case Remote::GO: - case Remote::OK: - { - if (!chanList) return 2; - - // select programme and display menu TODO currently just changes to selected channel - - currentChannelIndex = chanListbox.getCurrentOption(); - - if (parent) - { - Message* m = new Message(); // Must be done after this view deleted - m->from = this; - m->to = parent; - m->message = Message::CHANNEL_CHANGE; - m->parameter = (*chanList)[currentChannelIndex]->number; - Command::getInstance()->postMessageNoLock(m); - } - - setCurrentChannel(); - - if(command == Remote::GO) - return 2; - // GO just changes channel in preview, PLAY changes channel and returns to normal TV - } - case Remote::BACK: - case Remote::GUIDE: - { - // return to normal TV mode - if (parent) // ptr check done in case being tested from videorec - { - Message* m = new Message(); // Must be done after this view deleted - m->from = this; - m->to = parent; - m->message = Message::EPG_CLOSE; - Command::getInstance()->postMessageNoLock(m); - } - return 4; - } - case Remote::CHANNELUP: - { - if (currentChannelIndex == (chanList->size() - 1)) // at the end - currentChannelIndex = 0; - else - ++currentChannelIndex; - - if (parent) - { - Message* m = new Message(); // Must be done after this view deleted - m->from = this; - m->to = parent; - m->message = Message::CHANNEL_CHANGE; - m->parameter = (*chanList)[currentChannelIndex]->number; - Command::getInstance()->postMessageNoLock(m); - } - - setCurrentChannel(); - - return 2; - } - case Remote::CHANNELDOWN: - { - if (currentChannelIndex == 0) // at the start - currentChannelIndex = chanList->size() - 1; // so go to end - else - --currentChannelIndex; - - if (parent) - { - Message* m = new Message(); // Must be done after this view deleted - m->from = this; - m->to = parent; - m->message = Message::CHANNEL_CHANGE; - m->parameter = (*chanList)[currentChannelIndex]->number; - Command::getInstance()->postMessageNoLock(m); - } - - setCurrentChannel(); - - return 2; - } - } - // stop command getting to any more views - return 1; -} - -void VEpg::drawgrid() // redraws grid and select programme -{ - // draw the grid of programmes - char timeString[20]; - time_t t; - time(&t); // set t = now - if(selTime < t) - selTime = t; // don't allow cursor in the past - if(listTop != chanListbox.getTopOption()) - { - // chanListbox has scrolled TODO speed up by changing only rows that have changed - listTop = chanListbox.getTopOption(); - updateEventList(); - } - if ((selTime >= ltime + WINDOW_WIDTH * 60) || (selTime <= ltime)) - { - // we have cursored back before left time of window - //TODO check that this and above don't happen together - ltime = prevHour(&selTime); - updateEventList(); - } - // draw time scale - Colour white = Colour(255, 255, 255, 255); - - t = ltime; - struct tm* tms; - tms = localtime(&t); - strftime(timeString, 19, "%a %d %b", tms); - int timey = chanListbox.getRootBoxOffsetY() - Surface::getFontHeight() - 3; - int timex = 135; - drawTextRJ(timeString, timex - 10, timey, Colour::LIGHTTEXT); // print date - strftime(timeString, 19, "%H:%M", tms); - drawText(timeString, timex, timey, Colour::LIGHTTEXT); // print left time - rectangle(155, timey + Surface::getFontHeight(), 2, 7, white); - t = t + 3600; - tms = localtime(&t); - strftime(timeString, 19, "%H:%M", tms); - drawText(timeString, timex + 180, timey, Colour::LIGHTTEXT); // print middle time - rectangle(335, timey + Surface::getFontHeight(), 2, 7, white); - t = t + 3600; - tms = localtime(&t); - strftime(timeString, 19, "%H:%M", tms); - drawText(timeString, timex + 360, timey, Colour::LIGHTTEXT); // print right time - rectangle(515, timey + Surface::getFontHeight(), 2, 7, white); - // pointer to selTime - //rectangle(155 + (selTime - ltime) / 20, timey + Surface::getFontHeight(), 2, 7, Colour(255, 50, 50, 255)); - - // current time line - time(&t); - if ((t >= ltime) && (t < (ltime + 9000))) - { - rectangle(155 + (t - ltime) / 20, timey + Surface::getFontHeight(), 2, ((Surface::getFontHeight() + 2) * 7) + 7 + 2, Colour::RED); - } - - // TODO should the above two comditional statements be combined to avoid calling updateEventList() twice? - Event* event; - Event noevent; // an event to use if there are gaps in the epg - thisEvent.setdescription(tr("There are no programme details available for this period")); - thisEvent.duration = WINDOW_WIDTH * 60; - thisEvent.time = ltime; - thisEvent.settitle(tr("No programme details")); - thisEvent.id = 0; - bool swapColour = FALSE; // alternate cell colour - bool currentRow = FALSE; - int y = chanListbox.getRootBoxOffsetY() + 5; // vertical position of cell - Colour bg, fg; // background colour of cells in grid - // for each displayed channel, find programmes that fall in 2.5 hour time window - for(UINT listIndex = 0; listIndex < gridRows; listIndex++) - { - if (listTop + (int)listIndex >= chanListbox.getBottomOption()) - continue; // ensure nothing populates grid below last channel - currentRow = (listTop + (int)listIndex == chanListbox.getCurrentOption()); - noevent.time = ltime; - noevent.duration = WINDOW_WIDTH * 60; - noevent.settitle(""); - paintCell(&noevent, y, Colour::NOPROGRAMME, Colour::LIGHTTEXT); // fill row with no programme colour to be painted ove with valid programmes - if (currentRow) - { - thisEvent.setdescription(tr("There are no programme details available for this period")); - thisEvent.duration = WINDOW_WIDTH * 60; - thisEvent.time = ltime; - thisEvent.settitle(tr("No programme details")); - thisEvent.id = 0; - } - if (eventLista[listIndex]) - { - sort(eventLista[listIndex]->begin(), eventLista[listIndex]->end(), EventSorter()); - for(e = 0; e < (eventLista[listIndex])->size(); e++) // step through events for this channel - { - fg = Colour::LIGHTTEXT; - event = (*eventLista[listIndex])[e]; - if (event) - { - UINT end = event->time + event->duration; // programme end time - if(event->time >= UINT(ltime) + (WINDOW_WIDTH * 60)) // programme starts after RHS of window - continue; // that's enough of this channel's events - if(end <= UINT(ltime)) // programme ends before LHS of window - continue; // this event is before the window - let's try the next event - // this event is one we are interested in - bg = (swapColour)?Colour::PROGRAMMEA:Colour::PROGRAMMEB; // alternate cell colour - swapColour = !swapColour; // it wil be the other colour next time - if(event->time <= UINT(selTime) && end > UINT(selTime) && currentRow) - { - // this is the selected programme - thisEvent.setdescription(event->description); - thisEvent.duration = event->duration; - thisEvent.time = event->time; - thisEvent.settitle(event->title); - thisEvent.id = event->id; - if(thisEvent.id == 0) - thisEvent.id = 1; - bg = Colour::SELECTHIGHLIGHT; // highlight cell - fg = Colour::DARKTEXT; - } - else - { - if (currentRow && thisEvent.id == 0) - { - if (end <= UINT(selTime) && end > UINT(thisEvent.time)) - thisEvent.time = end; - if (event->time > UINT(selTime) && event->time < thisEvent.time + thisEvent.duration) - thisEvent.duration = event->time - thisEvent.time; - } - } - paintCell(event, y, bg, fg); - } - } - } - else - { - // no event list for this channel. Already painted noevent colour so just highlight if selected - if (currentRow) - { - bg = Colour::SELECTHIGHLIGHT; // highlight cell - fg = Colour::DARKTEXT; - paintCell(&thisEvent, y, bg, fg); - } - else - { - bg = Colour::NOPROGRAMME; - fg = Colour::LIGHTTEXT; - noevent.settitle(tr("No programme details")); - paintCell(&noevent, y, bg, fg); - } - } - y += Surface::getFontHeight() + 2; - } - setInfo(&thisEvent); -} - -void VEpg::updateEventList() -{ - if (!chanList) return; - Channel* chan; - for(UINT listIndex = 0; listIndex < gridRows; listIndex++) - { - if(listTop + listIndex >= UINT(chanListbox.getBottomOption())) - continue; - chan = (*chanList)[listTop + listIndex]; - - eventLista[listIndex] = VDR::getInstance()->getChannelSchedule(chan->number, ltime - 1, WINDOW_WIDTH * 60 + 2); // ltime - 1 to get prog before window (allows cursor left past ltime). + 2 to get prog after window - } -} - -void VEpg::setCurrentChannel() -{ - chanName.setText((*chanList)[currentChannelIndex]->name); - chanName.draw(); - Region r; - chanName.getRootBoxRegion(&r); - boxstack->update(this, &r); -} - -void VEpg::paintCell(Event* event, int yOffset, const Colour& bg, const Colour& fg) -{ - int w, x, y, h; - w = x = 0; // keep compiler happy - - y =yOffset; - h = Surface::getFontHeight(); // TODO if want border around text, need to increae this and wselectlist line height - UINT end = event->time + event->duration; // programme end time - if(event->time <= UINT(ltime) && end > UINT(ltime)) // spans start of displayed window - { - x = 155; // LHS of window - if (end > (UINT(ltime) + (WINDOW_WIDTH * 60))) - w = WINDOW_WIDTH * MINUTE_SCALE; // spans full 2 hour window - else - w = MINUTE_SCALE * (event->time + event->duration - ltime ) / 60; // get width of remaining programme - } - if((event->time >= UINT(ltime)) && (event->time <= UINT(ltime) + (WINDOW_WIDTH * 60))) // starts within window - { - x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60); - w = MINUTE_SCALE * event->duration / 60; - //if (w > 155 + MINUTE_SCALE * WINDOW_WIDTH -x) - // w = w + x - 155 - MINUTE_SCALE * WINDOW_WIDTH; // ends outside window - } - if (w > 155 + WINDOW_WIDTH * MINUTE_SCALE - x) - w = 155 + WINDOW_WIDTH * MINUTE_SCALE -x; // limit cells to RHS of window - rectangle(x, y, w, h, bg); - char* tt = new char[strlen(event->title) + 1]; - strcpy (tt, event->title); - int textWidth = 0; - for (UINT textPos = 0; textPos < strlen(tt); textPos++) - { - int thisCharWidth = surface->getCharWidth(tt[textPos]); - if (textWidth + thisCharWidth > w) // text will not fit in cell - { - textWidth = textPos; - break; - } - textWidth += thisCharWidth; - } - char* tT = new char[textWidth]; - if(textWidth > 1) - { - strncpy(tT, tt, textWidth - 1); - tT[textWidth - 1] = '\0'; - surface->drawText(tT, x+2, y, fg.rgba()); - } - delete tT; - -} - -time_t VEpg::prevHour(time_t* t) -{ - struct tm* tms; - tms = localtime(t); - tms->tm_sec = 0; - tms->tm_min = 0; - return mktime(tms); -} - -void VEpg::processMessage(Message* m) -{ - if (m->message == Message::MOUSE_MOVE) - { - if (chanListbox.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) - { - drawData(); - boxstack->update(this); - } - } - else if (m->message == Message::MOUSE_LBDOWN) - { - if (chanListbox.mouseLBDOWN((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) - { - boxstack->handleCommand(Remote::OK); //simulate OK press - } - else - { - //check if press is outside this view! then simulate cancel - int x=(m->parameter>>16)-getScreenX(); - int y=(m->parameter&0xFFFF)-getScreenY(); - int keyx = chanListbox.getRootBoxOffsetX(); - int keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2; - - if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight()) - { - boxstack->handleCommand(Remote::BACK); //simulate cancel press - } - else if (x>=(keyx+72) && y>=(keyy+4) &&x<=(keyx+72+104) &&y<=(keyy+4+Surface::getFontHeight() + 2)) - { - boxstack->handleCommand(Remote::RED); - } - else if (x>=(keyx+72) && y>=(keyy+ Surface::getFontHeight() + 8) &&x<=(keyx+72+104) &&y<=(keyy+8+2*Surface::getFontHeight() + 2)) - { - boxstack->handleCommand(Remote::GREEN); - } - else if (x>=(keyx+180) && y>=(keyy+4) &&x<=(keyx+180+104) &&y<=(keyy+4+Surface::getFontHeight() + 2)) - { - boxstack->handleCommand(Remote::YELLOW); - } - else if (x>=(keyx+180) && y>=(keyy+ Surface::getFontHeight() + 8) &&x<=(keyx+180+104) &&y<=(keyy+8+2*Surface::getFontHeight() + 2)) - { - boxstack->handleCommand(Remote::BLUE); - } - else if (x>=(keyx+290) && y>=(keyy+4) &&x<=(keyx+180+290) &&y<=(keyy+4+Surface::getFontHeight() + 2)) - { - boxstack->handleCommand(Remote::BACK); - } - else if (x>=(keyx+290) && y>=(keyy+ Surface::getFontHeight() + 8) &&x<=(keyx+290+180) &&y<=(keyy+8+2*Surface::getFontHeight() + 2)) - { - boxstack->handleCommand(Remote::RECORD); - } - else if (x>=(keyx+474) && y>=(keyy+4) &&x<=(keyx+128+474) &&y<=(keyy+4+Surface::getFontHeight() + 2)) - { - boxstack->handleCommand(Remote::PLAY); - } - else if (x>=(keyx+474) && y>=(keyy+ Surface::getFontHeight() + 8) &&x<=(keyx+238+474) &&y<=(keyy+8+2*Surface::getFontHeight() + 2)) - { - boxstack->handleCommand(Remote::GO); - } - else if ( x>=(chanListbox.getRootBoxOffsetX()) - && y>=(chanListbox.getRootBoxOffsetY() + 5) - // &&x<=(chanListbox.getOffsetX()+155 + WINDOW_WIDTH * MINUTE_SCALE) - &&y<=(chanListbox.getRootBoxOffsetY() - Surface::getFontHeight() - - 3+(int)chanListbox.getHeight() + Surface::getFontHeight() + 3) - ) - { - int cy=y-(chanListbox.getRootBoxOffsetY() + 5); - int row=cy/(Surface::getFontHeight()+2); - int clistTop = chanListbox.getTopOption(); - chanListbox.hintSetCurrent(clistTop+row); - int cx=x-155; - time_t ttime = cx*60/MINUTE_SCALE+ltime; - //x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60); - - selTime = ttime; - drawData(); - boxstack->update(this); - } - } - } -} - +/* + Copyright 2005 Brian Walton + + 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. +*/ +/* + vepg presents a 2 dimensional electronic programme guide with channels down + the y axis and time along the x axis. + Programmes are layed on the x axis as alterate coloured blocks with as much + of the programme title as will fit inside the block shown as text. + Up and down commands step through the channels whilst left and right commands + move through the programmes of the currently selected channel. + When a programme is selected, it highlights in the grid and full programe details + (start time, title and description) are displayed in an area at te top left of the screen. + Any currently programmed timers will display in the grid and in the orogramme detail window as red + It is possible to select a programme to be recorded by pressing the record button. + The video stream currently being viewed is shown as quarter screen in the top right. +*/ + +#include "vepg.h" + +#include "remote.h" +#include "vchannellist.h" +#include "command.h" +#include "video.h" +#include "vepgsettimer.h" +#include "timers.h" +#include "wsymbol.h" +#include "message.h" +#include "colour.h" +#include "boxstack.h" +#include "channel.h" +#include "i18n.h" +#include "log.h" + +VEpg* VEpg::instance = NULL; + +VEpg::VEpg(void* tparent, UINT tcurrentChannelIndex, ULONG streamType) +{ + instance = this; + currentChannelIndex = tcurrentChannelIndex; + + // PAL / NTSC sizes ----------------------- + + int xsize, ysize; + int xpos, ypos; + int summaryLines, summaryLowerPadding; + int chanNameYpos; + //UINT gridRows; // is a class member + + if (Video::getInstance()->getFormat() == Video::PAL) + { + xsize = 632; + ysize = 541; + xpos = 60; + ypos = 16; + summaryLines = 8; + summaryLowerPadding = 18; + chanNameYpos = 244; + gridRows = 7; + } + else + { + xsize = 632; + ysize = 452; + xpos = 50; + ypos = 10; + summaryLines = 6; + summaryLowerPadding = 28; + chanNameYpos = 206; + gridRows = 5; + } + + // initialise variables and pointers + boxstack = BoxStack::getInstance(); + parent = tparent; + eventList = NULL; + chanList = VDR::getInstance()->getChannelsList(streamType); //TODO want to be able to display video and radio together + e = 0; + + for(UINT listIndex = 0; listIndex < gridRows; listIndex++) + { + // initialise array of pointers to eventlist structures + eventLista[listIndex] = NULL; + } + + // Create pallet on which to paint our epg view and position it in centre of screen. + // Need to reduce size to deal with overscanning TVs. + + setSize(xsize, ysize); + createBuffer(); + setPosition(xpos, ypos); + + // beautify +// Colour transparent = Colour(0, 0, 0, 0); +// setBackgroundColour(transparent); + +// progTitle.setSurface(surface); + progTitle.setPosition(0,0); + progTitle.setSize(300,(getFontHeight() + 4) * 2 + 16); //paragraph line seperation is 4 pixels + progTitle.setBackgroundColour(Colour::TITLEBARBACKGROUND); + progTitle.setTextPos(5, 16); + progTitle.setGap(4); + add(&progTitle); + +// progInfo.setSurface(surface); + progInfo.setBackgroundColour(Colour::VIEWBACKGROUND); + progInfo.setPosition(0, progTitle.getY2()); + progInfo.setSize(300,((getFontHeight() + 4) * summaryLines) + summaryLowerPadding); + progInfo.setGap(4); + add(&progInfo); + +// chanName.setSurface(surface); + chanName.setSize(510, (getFontHeight() + 4)); + chanName.setPosition(305, chanNameYpos); + Colour t1(0, 0, 0, 90); + chanName.setBackgroundColour(t1); + add(&chanName); + + // create area to display list of channels +// chanListbox.setSurface(surface); // add channel list + chanListbox.setPosition(0, progInfo.getY2() + getFontHeight() + 8); // position channel list + chanListbox.setSize(150, ((getFontHeight() + 2) * gridRows) + 5); //listbox line seperation is 2 pixels + chanListbox.setGap(2); + add(&chanListbox); + + // populate channel list + if (chanList) + { + Channel* chan; + int first = 1; + for (UINT i = 0; i < chanList->size(); i++) + { + chan = (*chanList)[i]; + if (i == currentChannelIndex) + first = 1; + chan->index = chanListbox.addOption(chan->name, 0, first); + first = 0; + } + chanName.setText((*chanList)[chanListbox.getCurrentOption()]->name); + } + + listTop = chanListbox.getTopOption(); + chanListbox.draw(); // doing this to allow chanListbox.getBottomOption() in updateEventList() to work + time(<ime); // set ltime to now + ltime = prevHour(<ime); // set ltime to previous hour TODO make this half hour? + time(&selTime); // set selTime to now + updateEventList(); // get list of programmes +} + +void VEpg::preDelete() +{ + Timers::getInstance()->cancelTimer(this, 1); +} + +VEpg::~VEpg() +{ + + instance = NULL; + + for(UINT listIndex = 0; listIndex < gridRows; listIndex++) + { + if (eventLista[listIndex]) + { + (eventLista)[listIndex]->clear(); + delete eventLista[listIndex]; + } + } + // delete [] eventLista; // FIXME + + // destroy dynamically allocated memory +} + +VEpg* VEpg::getInstance() +{ + return instance; +} + +void VEpg::setInfo(Event* event) +{ + time_t t; + struct tm* btime; // to hold programme start and end time + char timeString[9]; // to hold programme start and end time + int length = strlen(event->title); // calculate length of programme title string + char* title = new char[length + 15]; // create string to hold start time, end time and programme title + btime = localtime((time_t*)&event->time); //get programme start time +#ifndef _MSC_VER + strftime(timeString, 9, "%0H:%0M - ", btime); // and format it as hh:mm - +#else + strftime(timeString, 9, "%H:%M - ", btime); // and format it as hh:mm - +#endif + strcpy(title, timeString); // put it in our buffer + t = event->time + event->duration; //get programme end time + btime = localtime(&t); +#ifndef _MSC_VER + strftime(timeString, 7, "%0H:%0M ", btime); // and format it as hh:mm - +#else + strftime(timeString, 7, "%H:%M ", btime); // and format it as hh:mm - +#endif + strcat(title, timeString); // put it in our buffer + strcat(title, event->title); // then add the programme title + progTitle.setText(title); // sput this sring in our text box + length = strlen(event->description); + char* info = new char[length + 1]; // create programme detail string + strcpy(info, event->description); + progInfo.setText(info); // show programme detail string +// destroy dynamically allocated memory + delete[] info; + delete[] title; +} + +void VEpg::draw() +{ +// View::draw(); // draw pallet + // beautify + Colour transparent = Colour(0, 0, 0, 0); + fillColour(transparent); + + + // Moved all the dynamic data drawing to a seperate function + + // Display the status and key stuff at the bottom + + UINT keyx = chanListbox.getRootBoxOffsetX(); + UINT keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2; + Colour ref1 = Colour(100, 100, 100, 255); + rectangle(keyx, keyy, 605, getFontHeight() * 2 + 14, ref1); + + WSymbol w; + TEMPADD(&w); + + w.nextSymbol = WSymbol::LEFTARROW; + w.setPosition(keyx + 1, keyy + 20); + w.draw(); + + w.nextSymbol = WSymbol::UP; + w.setPosition(keyx + 26, keyy + 3); + w.draw(); + + w.nextSymbol = WSymbol::DOWN; + w.setPosition(keyx + 26, keyy + 36); + w.draw(); + + w.nextSymbol = WSymbol::RIGHTARROW; + w.setPosition(keyx + 50, keyy + 20); + w.draw(); + + drawText(tr("OK"), keyx + 18, keyy + 20, Colour::LIGHTTEXT); + + Colour ref2 = Colour(200, 0, 0, 255); + rectangle(keyx + 72, keyy + 4, 104, getFontHeight() + 2, ref2); + drawText(tr("Page up"), keyx + 74, keyy + 5, Colour::LIGHTTEXT); + + Colour ref3 = Colour(0, 200, 0, 255); + rectangle(keyx + 72, keyy + getFontHeight() + 8, 104, getFontHeight() + 2, ref3); + drawText(tr("Page down"), keyx + 74, keyy + getFontHeight() + 9, Colour::LIGHTTEXT); + + Colour ref4 = Colour(200, 200, 0, 255); + rectangle(keyx + 180, keyy + 4, 104, getFontHeight() + 2, ref4); + drawText(tr("-24 hours"), keyx + 182, keyy + 5, Colour::LIGHTTEXT); + + Colour ref5 = Colour(0, 0, 200, 255); + rectangle(keyx + 180, keyy + getFontHeight() + 8, 104, getFontHeight() + 2, ref5); + drawText(tr("+24 hours"), keyx + 182, keyy + getFontHeight() + 9, Colour::LIGHTTEXT); + + Colour ref6 = Colour(180, 180, 180, 255); + rectangle(keyx + 290, keyy + 4, 180, getFontHeight() + 2, ref6); + drawText(tr("Guide / Back: Close"), keyx + 292 , keyy + 5, Colour::LIGHTTEXT); + + Colour ref7 = Colour(180, 180, 180, 255); + rectangle(keyx + 290, keyy + getFontHeight() + 8, 180, getFontHeight() + 2, ref7); + Colour red = Colour(130, 0, 0); + drawText(tr("Rec: Set timer"), keyx + 292, keyy + getFontHeight() + 9, red); + + Colour ref8 = Colour(180, 180, 180, 255); + rectangle(keyx + 474, keyy + 4, 128, getFontHeight() + 2, ref8); + w.nextSymbol = WSymbol::PLAY; + w.setPosition(keyx + 476, keyy + 5); + w.draw(); + drawText(tr("Sel channel"), keyx + 496, keyy + 5, Colour::LIGHTTEXT); + + Colour ref9 = Colour(180, 180, 180, 255); + rectangle(keyx + 474, keyy + getFontHeight() + 8, 128, getFontHeight() + 2, ref9); + drawText(tr("Go: Preview"), keyx + 476, keyy + getFontHeight() + 9, Colour::LIGHTTEXT); + + + // Draw all the dynamic data + drawData(); +} + +void VEpg::drawData() +{ + // Not doing View::draw() every time causes + // things not to be cleared off the surface properly + // So, blank out the data area first + + rectangle( + chanListbox.getRootBoxOffsetX(), + chanListbox.getRootBoxOffsetY() - getFontHeight() - 3, + 155 + WINDOW_WIDTH * MINUTE_SCALE, + chanListbox.getHeight() + getFontHeight() + 4, + Colour::BLACK); + + chanListbox.draw(); + drawgrid(); + chanName.draw(); // TODO this should be dealt with by vvideolive + + progTitle.draw(); + progInfo.draw(); + + // Set timer to redraw to move the current time bar + time_t t, dt; + time(&t); + dt = 60 - (t % 60); + if (dt == 0) dt = 60; + dt += t; + Timers::getInstance()->setTimerT(this, 1, dt); +} + +void VEpg::timercall(int clientReference) +{ + drawData(); + boxstack->update(this); +} + +int VEpg::handleCommand(int command) +{ + switch(command) + { + case Remote::DF_UP: + case Remote::UP: + { // cursor up the channel list + chanListbox.up(); + drawData(); + boxstack->update(this); + return 2; + } + case Remote::DF_DOWN: + case Remote::DOWN: + { // cursor down the channel list + Log::getInstance()->log("VEPG", Log::DEBUG, "Down start"); + + chanListbox.down(); + drawData(); + boxstack->update(this); + Log::getInstance()->log("VEPG", Log::DEBUG, "Down end"); + + return 2; + } + case Remote::DF_LEFT: + case Remote::LEFT: + { // cursor left through time + selTime = thisEvent.time - 1; + drawData(); + boxstack->update(this); + return 2; + } + case Remote::DF_RIGHT: + case Remote::RIGHT: + { + // cursor right through time + selTime = thisEvent.time + thisEvent.duration; + drawData(); + boxstack->update(this); + return 2; + } + case Remote::RED: + { + // cursor up one page + chanListbox.pageUp(); + drawData(); + boxstack->update(this); + return 2; + } + case Remote::GREEN: + { + // cursor down one page + chanListbox.pageDown(); + drawData(); + boxstack->update(this); + return 2; + } + case Remote::BLUE: + { + // step forward 24 hours + selTime += 24 * 60 * 60; + drawData(); + boxstack->update(this); + return 2; + } + case Remote::YELLOW: + { + // step forward 24 hours + selTime -= 24 * 60 * 60; + drawData(); + boxstack->update(this); + return 2; + } + case Remote::RECORD: + { + if (!chanList) return 2; + Log::getInstance()->log("VEPG", Log::DEBUG, "ID %lu TIME %lu DURATION %lu TITLE %s", thisEvent.id, thisEvent.time, thisEvent.duration, thisEvent.title); + VEpgSetTimer* vs = new VEpgSetTimer(&thisEvent, (*chanList)[chanListbox.getCurrentOption()]); + vs->draw(); + boxstack->add(vs); + boxstack->update(vs); + return 2; + } + case Remote::PLAY: + case Remote::GO: + case Remote::OK: + { + if (!chanList) return 2; + + // select programme and display menu TODO currently just changes to selected channel + + currentChannelIndex = chanListbox.getCurrentOption(); + + if (parent) + { + Message* m = new Message(); // Must be done after this view deleted + m->from = this; + m->to = parent; + m->message = Message::CHANNEL_CHANGE; + m->parameter = (*chanList)[currentChannelIndex]->number; + Command::getInstance()->postMessageNoLock(m); + } + + setCurrentChannel(); + + if(command == Remote::GO) + return 2; + // GO just changes channel in preview, PLAY changes channel and returns to normal TV + } + case Remote::BACK: + case Remote::GUIDE: + { + // return to normal TV mode + if (parent) // ptr check done in case being tested from videorec + { + Message* m = new Message(); // Must be done after this view deleted + m->from = this; + m->to = parent; + m->message = Message::EPG_CLOSE; + Command::getInstance()->postMessageNoLock(m); + } + return 4; + } + case Remote::CHANNELUP: + { + if (currentChannelIndex == (chanList->size() - 1)) // at the end + currentChannelIndex = 0; + else + ++currentChannelIndex; + + if (parent) + { + Message* m = new Message(); // Must be done after this view deleted + m->from = this; + m->to = parent; + m->message = Message::CHANNEL_CHANGE; + m->parameter = (*chanList)[currentChannelIndex]->number; + Command::getInstance()->postMessageNoLock(m); + } + + setCurrentChannel(); + + return 2; + } + case Remote::CHANNELDOWN: + { + if (currentChannelIndex == 0) // at the start + currentChannelIndex = chanList->size() - 1; // so go to end + else + --currentChannelIndex; + + if (parent) + { + Message* m = new Message(); // Must be done after this view deleted + m->from = this; + m->to = parent; + m->message = Message::CHANNEL_CHANGE; + m->parameter = (*chanList)[currentChannelIndex]->number; + Command::getInstance()->postMessageNoLock(m); + } + + setCurrentChannel(); + + return 2; + } + } + // stop command getting to any more views + return 1; +} + +void VEpg::drawgrid() // redraws grid and select programme +{ + // draw the grid of programmes + char timeString[20]; + time_t t; + time(&t); // set t = now + if(selTime < t) + selTime = t; // don't allow cursor in the past + if(listTop != chanListbox.getTopOption()) + { + // chanListbox has scrolled TODO speed up by changing only rows that have changed + listTop = chanListbox.getTopOption(); + updateEventList(); + } + if ((selTime >= ltime + WINDOW_WIDTH * 60) || (selTime <= ltime)) + { + // we have cursored back before left time of window + //TODO check that this and above don't happen together + ltime = prevHour(&selTime); + updateEventList(); + } + // draw time scale + Colour white = Colour(255, 255, 255, 255); + + t = ltime; + struct tm* tms; + tms = localtime(&t); + strftime(timeString, 19, "%a %d %b", tms); + int timey = chanListbox.getRootBoxOffsetY() - getFontHeight() - 3; + int timex = 135; + drawTextRJ(timeString, timex - 10, timey, Colour::LIGHTTEXT); // print date + strftime(timeString, 19, "%H:%M", tms); + drawText(timeString, timex, timey, Colour::LIGHTTEXT); // print left time + + rectangle(155, timey + getFontHeight(), 2, 7, white); + t = t + 3600; + tms = localtime(&t); + strftime(timeString, 19, "%H:%M", tms); + drawText(timeString, timex + 180, timey, Colour::LIGHTTEXT); // print middle time + rectangle(335, timey + getFontHeight(), 2, 7, white); + t = t + 3600; + tms = localtime(&t); + strftime(timeString, 19, "%H:%M", tms); + drawText(timeString, timex + 360, timey, Colour::LIGHTTEXT); // print right time + rectangle(515, timey + getFontHeight(), 2, 7, white); + // pointer to selTime + //rectangle(155 + (selTime - ltime) / 20, timey + getFontHeight(), 2, 7, Colour(255, 50, 50, 255)); + + // current time line + time(&t); + if ((t >= ltime) && (t < (ltime + 9000))) + { + rectangle(155 + (t - ltime) / 20, timey + getFontHeight(), 2, ((getFontHeight() + 2) * 7) + 7 + 2, Colour::RED); + } + + // TODO should the above two comditional statements be combined to avoid calling updateEventList() twice? + Event* event; + Event noevent; // an event to use if there are gaps in the epg + thisEvent.setdescription(tr("There are no programme details available for this period")); + thisEvent.duration = WINDOW_WIDTH * 60; + thisEvent.time = ltime; + thisEvent.settitle(tr("No programme details")); + thisEvent.id = 0; + bool swapColour = false; // alternate cell colour + bool currentRow = false; + int y = chanListbox.getRootBoxOffsetY() + 5; // vertical position of cell + Colour bg, fg; // background colour of cells in grid + // for each displayed channel, find programmes that fall in 2.5 hour time window + for(UINT listIndex = 0; listIndex < gridRows; listIndex++) + { + if (listTop + (int)listIndex >= chanListbox.getBottomOption()) + continue; // ensure nothing populates grid below last channel + currentRow = (listTop + (int)listIndex == chanListbox.getCurrentOption()); + noevent.time = ltime; + noevent.duration = WINDOW_WIDTH * 60; + noevent.settitle(""); + paintCell(&noevent, y, Colour::NOPROGRAMME, Colour::LIGHTTEXT); // fill row with no programme colour to be painted ove with valid programmes + if (currentRow) + { + thisEvent.setdescription(tr("There are no programme details available for this period")); + thisEvent.duration = WINDOW_WIDTH * 60; + thisEvent.time = ltime; + thisEvent.settitle(tr("No programme details")); + thisEvent.id = 0; + } + if (eventLista[listIndex]) + { + sort(eventLista[listIndex]->begin(), eventLista[listIndex]->end(), EventSorter()); + for(e = 0; e < (eventLista[listIndex])->size(); e++) // step through events for this channel + { + fg = Colour::LIGHTTEXT; + event = (*eventLista[listIndex])[e]; + if (event) + { + UINT end = event->time + event->duration; // programme end time + if(event->time >= UINT(ltime) + (WINDOW_WIDTH * 60)) // programme starts after RHS of window + continue; // that's enough of this channel's events + if(end <= UINT(ltime)) // programme ends before LHS of window + continue; // this event is before the window - let's try the next event + // this event is one we are interested in + bg = (swapColour)?Colour::PROGRAMMEA:Colour::PROGRAMMEB; // alternate cell colour + swapColour = !swapColour; // it wil be the other colour next time + if(event->time <= UINT(selTime) && end > UINT(selTime) && currentRow) + { + // this is the selected programme + thisEvent.setdescription(event->description); + thisEvent.duration = event->duration; + thisEvent.time = event->time; + thisEvent.settitle(event->title); + thisEvent.id = event->id; + if(thisEvent.id == 0) + thisEvent.id = 1; + bg = Colour::SELECTHIGHLIGHT; // highlight cell + fg = Colour::DARKTEXT; + } + else + { + if (currentRow && thisEvent.id == 0) + { + if (end <= UINT(selTime) && end > UINT(thisEvent.time)) + thisEvent.time = end; + if (event->time > UINT(selTime) && event->time < thisEvent.time + thisEvent.duration) + thisEvent.duration = event->time - thisEvent.time; + } + } + paintCell(event, y, bg, fg); + } + } + } + else + { + // no event list for this channel. Already painted noevent colour so just highlight if selected + if (currentRow) + { + bg = Colour::SELECTHIGHLIGHT; // highlight cell + fg = Colour::DARKTEXT; + paintCell(&thisEvent, y, bg, fg); + } + else + { + bg = Colour::NOPROGRAMME; + fg = Colour::LIGHTTEXT; + noevent.settitle(tr("No programme details")); + paintCell(&noevent, y, bg, fg); + } + } + y += getFontHeight() + 2; + } + setInfo(&thisEvent); +} + +void VEpg::updateEventList() +{ + if (!chanList) return; + Channel* chan; + for(UINT listIndex = 0; listIndex < gridRows; listIndex++) + { + if(listTop + listIndex >= UINT(chanListbox.getBottomOption())) + continue; + chan = (*chanList)[listTop + listIndex]; + + eventLista[listIndex] = VDR::getInstance()->getChannelSchedule(chan->number, ltime - 1, WINDOW_WIDTH * 60 + 2); // ltime - 1 to get prog before window (allows cursor left past ltime). + 2 to get prog after window + } +} + +void VEpg::setCurrentChannel() +{ + chanName.setText((*chanList)[currentChannelIndex]->name); + chanName.draw(); + Region r; + chanName.getRootBoxRegion(&r); + boxstack->update(this, &r); +} + +void VEpg::paintCell(Event* event, int yOffset, const Colour& bg, const Colour& fg) +{ + int w, x, y, h; + w = x = 0; // keep compiler happy + + y =yOffset; + h = getFontHeight(); // TODO if want border around text, need to increae this and wselectlist line height + UINT end = event->time + event->duration; // programme end time + if(event->time <= UINT(ltime) && end > UINT(ltime)) // spans start of displayed window + { + x = 155; // LHS of window + if (end > (UINT(ltime) + (WINDOW_WIDTH * 60))) + w = WINDOW_WIDTH * MINUTE_SCALE; // spans full 2 hour window + else + w = MINUTE_SCALE * (event->time + event->duration - ltime ) / 60; // get width of remaining programme + } + if((event->time >= UINT(ltime)) && (event->time <= UINT(ltime) + (WINDOW_WIDTH * 60))) // starts within window + { + x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60); + w = MINUTE_SCALE * event->duration / 60; + //if (w > 155 + MINUTE_SCALE * WINDOW_WIDTH -x) + // w = w + x - 155 - MINUTE_SCALE * WINDOW_WIDTH; // ends outside window + } + if (w > 155 + WINDOW_WIDTH * MINUTE_SCALE - x) + w = 155 + WINDOW_WIDTH * MINUTE_SCALE -x; // limit cells to RHS of window + rectangle(x, y, w, h, bg); + char* tt = new char[strlen(event->title) + 1]; + strcpy (tt, event->title); + int textWidth = 0; + for (UINT textPos = 0; textPos < strlen(tt); textPos++) + { + int thisCharWidth = charWidth(tt[textPos]); + if (textWidth + thisCharWidth > w) // text will not fit in cell + { + textWidth = textPos; + break; + } + textWidth += thisCharWidth; + } + char* tT = new char[textWidth]; + if(textWidth > 1) + { + strncpy(tT, tt, textWidth - 1); + tT[textWidth - 1] = '\0'; + surface->drawText(tT, x+2, y, fg.rgba()); + } + delete tT; + +} + +time_t VEpg::prevHour(time_t* t) +{ + struct tm* tms; + tms = localtime(t); + tms->tm_sec = 0; + tms->tm_min = 0; + return mktime(tms); +} + +void VEpg::processMessage(Message* m) +{ + if (m->message == Message::MOUSE_MOVE) + { + if (chanListbox.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) + { + drawData(); + boxstack->update(this); + } + } + else if (m->message == Message::MOUSE_LBDOWN) + { + if (chanListbox.mouseLBDOWN((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) + { + boxstack->handleCommand(Remote::OK); //simulate OK press + } + else + { + //check if press is outside this view! then simulate cancel + int x=(m->parameter>>16)-getScreenX(); + int y=(m->parameter&0xFFFF)-getScreenY(); + int keyx = chanListbox.getRootBoxOffsetX(); + int keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2; + + if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight()) + { + boxstack->handleCommand(Remote::BACK); //simulate cancel press + } + else if (x>=(keyx+72) && y>=(keyy+4) &&x<=(keyx+72+104) &&y<=(keyy+4+getFontHeight() + 2)) + { + boxstack->handleCommand(Remote::RED); + } + else if (x>=(keyx+72) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+72+104) &&y<=(keyy+8+2*getFontHeight() + 2)) + { + boxstack->handleCommand(Remote::GREEN); + } + else if (x>=(keyx+180) && y>=(keyy+4) &&x<=(keyx+180+104) &&y<=(keyy+4+getFontHeight() + 2)) + { + boxstack->handleCommand(Remote::YELLOW); + } + else if (x>=(keyx+180) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+180+104) &&y<=(keyy+8+2*getFontHeight() + 2)) + { + boxstack->handleCommand(Remote::BLUE); + } + else if (x>=(keyx+290) && y>=(keyy+4) &&x<=(keyx+180+290) &&y<=(keyy+4+getFontHeight() + 2)) + { + boxstack->handleCommand(Remote::BACK); + } + else if (x>=(keyx+290) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+290+180) &&y<=(keyy+8+2*getFontHeight() + 2)) + { + boxstack->handleCommand(Remote::RECORD); + } + else if (x>=(keyx+474) && y>=(keyy+4) &&x<=(keyx+128+474) &&y<=(keyy+4+getFontHeight() + 2)) + { + boxstack->handleCommand(Remote::PLAY); + } + else if (x>=(keyx+474) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+238+474) &&y<=(keyy+8+2*getFontHeight() + 2)) + { + boxstack->handleCommand(Remote::GO); + } + else if ( x>=(chanListbox.getRootBoxOffsetX()) + && y>=(chanListbox.getRootBoxOffsetY() + 5) + // &&x<=(chanListbox.getOffsetX()+155 + WINDOW_WIDTH * MINUTE_SCALE) + &&y<=(chanListbox.getRootBoxOffsetY() - getFontHeight() + - 3+(int)chanListbox.getHeight() + getFontHeight() + 3) + ) + { + int cy=y-(chanListbox.getRootBoxOffsetY() + 5); + int row=cy/(getFontHeight()+2); + int clistTop = chanListbox.getTopOption(); + chanListbox.hintSetCurrent(clistTop+row); + int cx=x-155; + time_t ttime = cx*60/MINUTE_SCALE+ltime; + //x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60); + + selTime = ttime; + drawData(); + boxstack->update(this); + } + } + } +} + diff --git a/vepgsettimer.cc b/vepgsettimer.cc index 3c5dad4..4e00ab6 100644 --- a/vepgsettimer.cc +++ b/vepgsettimer.cc @@ -60,8 +60,8 @@ VEpgSetTimer::VEpgSetTimer(Event* tevent, Channel* tchannel) add(&buttonYes); add(&buttonNo); - buttonYes.setPosition(80, 40 + (7 * surface->getFontHeight())); - buttonNo.setPosition(220, 40 + (7 * surface->getFontHeight())); + buttonYes.setPosition(80, 40 + (7 * getFontHeight())); + buttonNo.setPosition(220, 40 + (7 * getFontHeight())); buttonYes.setText(tr("Yes")); buttonNo.setText(tr("No")); @@ -174,7 +174,7 @@ void VEpgSetTimer::draw() { TBBoxx::draw(); drawPara(event->title, 10, 40, Colour::LIGHTTEXT); - drawText(channel->name, 10, 40 + (2 * surface->getFontHeight()), Colour::LIGHTTEXT); + drawText(channel->name, 10, 40 + (2 * getFontHeight()), Colour::LIGHTTEXT); char fullString[20]; time_t t; @@ -197,8 +197,8 @@ void VEpgSetTimer::draw() strcat(fullString, timeString); // put it in our buffer - drawText(fullString, 10, 40 + (3 * surface->getFontHeight()), Colour::LIGHTTEXT); - drawText(tr("Create this timer?"), 10, 40 + (5 * surface->getFontHeight()), Colour::LIGHTTEXT); + drawText(fullString, 10, 40 + (3 * getFontHeight()), Colour::LIGHTTEXT); + drawText(tr("Create this timer?"), 10, 40 + (5 * getFontHeight()), Colour::LIGHTTEXT); buttonYes.draw(); buttonNo.draw(); diff --git a/vfeed.cc b/vfeed.cc index f15d713..6640a13 100644 --- a/vfeed.cc +++ b/vfeed.cc @@ -1,82 +1,84 @@ -/* - 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. -*/ - -#include "vfeed.h" - -#include "log.h" -#include "demuxer.h" -#include "callback.h" - -VFeed::VFeed(Callback* tcb) -: cb(*tcb) -{ -} - -int VFeed::init() -{ - return 1; -} - -int VFeed::shutdown() -{ - // FIXME - return 1; -} - -int VFeed::start() -{ - return threadStart(); -} - -void VFeed::stop() -{ - threadCancel(); -} - -void VFeed::release() -{ - threadSignal(); -} - -void VFeed::threadMethod() -{ - bool vlen; - - Log::getInstance()->log("VFeed", Log::DEBUG, "Started"); - threadWaitForSignal(); // Don't feed video until audio has started - Log::getInstance()->log("VFeed", Log::DEBUG, "Released"); - - while(1) - { - threadCheckExit(); - vlen = Demuxer::getInstance()->writeVideo(); - - if (vlen) - { - cb.call(this); - } - else - { - //Log::getInstance()->log("VFeed", Log::DEBUG, "No data delay"); - MILLISLEEP(5); - } - } -} - +/* + 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. +*/ + +#include "vfeed.h" + +#include "log.h" +#include "demuxer.h" +#include "callback.h" + +VFeed::VFeed(Callback* tcb) +: cb(*tcb) +{ +} + +int VFeed::init() +{ + return 1; +} + +int VFeed::shutdown() +{ + // FIXME + return 1; +} + +int VFeed::start() +{ + return threadStart(); +} + +void VFeed::stop() +{ + Log::getInstance()->log("VFeed", Log::DEBUG, "Stop1"); + threadCancel(); + Log::getInstance()->log("VFeed", Log::DEBUG, "Stop2"); +} + +void VFeed::release() +{ + threadSignal(); +} + +void VFeed::threadMethod() +{ + bool vlen; + + Log::getInstance()->log("VFeed", Log::DEBUG, "Started"); + threadWaitForSignal(); // Don't feed video until audio has started + Log::getInstance()->log("VFeed", Log::DEBUG, "Released"); + + while(1) + { + threadCheckExit(); + vlen = Demuxer::getInstance()->writeVideo(); + + if (vlen) + { + cb.call(this); + } + else + { + //Log::getInstance()->log("VFeed", Log::DEBUG, "No data delay"); + MILLISLEEP(5); + } + } +} + diff --git a/vfeed.h b/vfeed.h index 0233e66..0fa2721 100644 --- a/vfeed.h +++ b/vfeed.h @@ -24,11 +24,15 @@ #include #include -#ifdef WIN32 -#include "threadwin.h" +#ifndef WIN32 +#ifdef __ANDROID__ +#include "threadpandroid.h" #else #include "threadp.h" #endif +#else +#include "threadwin.h" +#endif class Callback; diff --git a/video.cc b/video.cc index 1d6254d..864c1a0 100644 --- a/video.cc +++ b/video.cc @@ -1,93 +1,98 @@ -/* - 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. -*/ -//portions from vdr by Klaus Schmiding HSMF code - -#include "video.h" - -Video* Video::instance = NULL; - -Video::Video() -{ - if (instance) return; - instance = this; - initted = 0; - - fdVideo = 0; - - format = 0; - connection = 0; - aspectRatio = 0; - mode = 0; - tvsize = 0; - - screenWidth = 0; - screenHeight = 0; -} - -Video::~Video() -{ - instance = NULL; -} - -Video* Video::getInstance() -{ - return instance; -} - -/* -hmsf Video::framesToHMSF(ULONG frames, double fps) -{ - hmsf ret; - / * from vdr * - double Seconds; - ret.frames= int(modf((frames + 0.5) / fps, &Seconds) * fps + 1); - int s = int(Seconds); - ret.seconds=s; - ret.minutes = s / 60 % 60; - ret.hours = s / 3600; - -/ * if (format == NTSC) - { - ret.hours = frames / 108000; - frames %= 108000; - ret.minutes = frames / 1800; - frames %= 1800; - ret.seconds = frames / 30; - ret.frames = frames % 30; - } - else - { - ret.hours = frames / 90000; - frames %= 90000; - ret.minutes = frames / 1500; - frames %= 1500; - ret.seconds = frames / 25; - ret.frames = frames % 25; - }* / - return ret; -}*/ - -/* -UINT Video::getFPS() -{ - if (format == NTSC) return 30; - else return 25; -} -*/ +/* + 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. +*/ +//portions from vdr by Klaus Schmiding HSMF code + +#include "video.h" + +Video* Video::instance = NULL; + +Video::Video() +{ + if (instance) return; + instance = this; + initted = 0; + + fdVideo = 0; + + format = 0; + connection = 0; + aspectRatio = 0; + mode = 0; + tvsize = 0; + + screenWidth = 0; + screenHeight = 0; +} + +Video::~Video() +{ + instance = NULL; +} + +Video* Video::getInstance() +{ + return instance; +} +/* +void Video::setInstance(Video* inst) +{ + instance=inst; +}*/ + +/* +hmsf Video::framesToHMSF(ULONG frames, double fps) +{ + hmsf ret; + / * from vdr * + double Seconds; + ret.frames= int(modf((frames + 0.5) / fps, &Seconds) * fps + 1); + int s = int(Seconds); + ret.seconds=s; + ret.minutes = s / 60 % 60; + ret.hours = s / 3600; + +/ * if (format == NTSC) + { + ret.hours = frames / 108000; + frames %= 108000; + ret.minutes = frames / 1800; + frames %= 1800; + ret.seconds = frames / 30; + ret.frames = frames % 30; + } + else + { + ret.hours = frames / 90000; + frames %= 90000; + ret.minutes = frames / 1500; + frames %= 1500; + ret.seconds = frames / 25; + ret.frames = frames % 25; + }* / + return ret; +}*/ + +/* +UINT Video::getFPS() +{ + if (format == NTSC) return 30; + else return 25; +} +*/ diff --git a/video.h b/video.h index 502a1ba..e09d017 100644 --- a/video.h +++ b/video.h @@ -1,150 +1,167 @@ -/* - 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 VIDEO_H -#define VIDEO_H - -#include -#include "defines.h" -#include "draintarget.h" -#include "abstractoption.h" - -typedef struct _hmsf -{ - UINT hours; - UINT minutes; - UINT seconds; - UINT frames; -} hmsf; - -class Video: public DrainTarget, public AbstractOption -{ - public: - Video(); - virtual ~Video(); - static Video* getInstance(); - - virtual int init(UCHAR format)=0; - virtual int shutdown()=0; - virtual int setFormat(UCHAR format)=0; - virtual int setConnection(UCHAR connection)=0; - virtual int setAspectRatio(UCHAR aspectRatio)=0; // This one does the pin 8 scart widescreen switching - virtual int setMode(UCHAR mode)=0; - virtual int setTVsize(UCHAR size)=0; // Is the TV a widescreen? - virtual int setDefaultAspect()=0; - virtual int setSource()=0; - virtual int setPosition(int x, int y)=0; - virtual int sync()=0; - virtual int play()=0; - virtual int stop()=0; - virtual int pause()=0; - virtual int unPause()=0; - virtual int fastForward()=0; - virtual int unFastForward()=0; - virtual int reset()=0; - virtual int blank()=0; - virtual int signalOn()=0; - virtual int signalOff()=0; - virtual int attachFrameBuffer()=0; // What does this do? -// virtual ULONG timecodeToFrameNumber(ULLONG timecode)=0; //Obsolete and not HD compatible - virtual ULLONG getCurrentTimestamp()=0; - virtual void displayIFrame(const UCHAR* buffer, UINT length)=0; - - virtual bool supportsh264(){return false;}; - virtual void seth264mode(bool ish264) {h264=ish264;}; - - virtual void turnVideoOn(){}; - virtual void turnVideoOff(){}; -// virtual ULLONG frameNumberToTimecode(ULONG timecode) { return 0; };//Obsolete and not HD compatible - -#ifdef DEV - virtual int test() { return 0; } - virtual int test2() { return 0; } -#endif - - int getMode() { return mode; } - UCHAR getFormat() { return format; } - UINT getScreenWidth() { return screenWidth; } - UINT getScreenHeight() { return screenHeight; } - UCHAR getTVsize() { return tvsize; } - - //hmsf framesToHMSF(ULONG frames,double fps); - // UINT getFPS(); //removed - - // Video formats - AV_SET_VID_DISP_FMT - const static UCHAR NTSC = 0; - const static UCHAR PAL = 1; - - // Video connections - AV_SET_VID_OUTPUT - const static UCHAR COMPOSITERGB = 1; - const static UCHAR SVIDEO = 2; - - // Video aspect ratios - AV_SET_VID_RATIO - const static UCHAR ASPECT4X3 = 0; - const static UCHAR ASPECT16X9 = 1; - - // Video modes - AV_SET_VID_MODE - const static UCHAR NORMAL = 0; - const static UCHAR LETTERBOX = 1; -/* - Actual Source Aspect Aspect IOCTL Mode IOCTL MODE A MODE B - - 4:3 4:3 NORMAL fullframe43 fullframe43 - 4:3 16:9 NORMAL fullframe43 fullframe43 -- invalid? - 4:3 4:3 LETTERBOX fullframe43 fullframe43 - 4:3 16:9 LETTERBOX fullframe43 fullframe43 -- invalid? - 16:9 4:3 NORMAL chop sides fullframe169 - 16:9 16:9 NORMAL chop sides fullframe169 - 16:9 4:3 LETTERBOX letterbox letterbox - 16:9 16:9 LETTERBOX chop sides fullframe169 - - Conclusions - - 1. There are two chip modes - accessible by reopening the fd - 2. The video chip knows the aspect ratio purely from the incoming MPEG - 3. MODE A is for 4:3 TVs, MODE B is for 16:9 TVs - - To switch to MODE A, set the aspect ioctl to 4:3 and reopen the FD. - To switch to MODE B, set the aspect ioctl to 16:9 and reopen the FD. -*/ - - const static UCHAR UNKNOWN2 = 2; - const static UCHAR QUARTER = 3; - const static UCHAR EIGHTH = 4; - const static UCHAR ZOOM = 5; - const static UCHAR UNKNOWN6 = 6; - - protected: - static Video* instance; - int initted; - int fdVideo; - - UCHAR tvsize; - UCHAR format; - UCHAR connection; - UCHAR aspectRatio; - UCHAR mode; - bool h264; - - UINT screenWidth; - UINT screenHeight; -}; - -#endif +/* + 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 VIDEO_H +#define VIDEO_H + +#include +#include "defines.h" +#include "draintarget.h" +#include "abstractoption.h" + +typedef struct _hmsf +{ + UINT hours; + UINT minutes; + UINT seconds; + UINT frames; +} hmsf; + +class Video: public DrainTarget, public AbstractOption +{ + public: + Video(); + virtual ~Video(); + static Video* getInstance(); + //static void setInstance(Video*); + + virtual int init(UCHAR format)=0; + virtual int shutdown()=0; + virtual int setFormat(UCHAR format)=0; + virtual int setConnection(UCHAR connection)=0; + virtual int setAspectRatio(UCHAR aspectRatio)=0; // This one does the pin 8 scart widescreen switching + virtual int setMode(UCHAR mode)=0; + virtual int setTVsize(UCHAR size)=0; // Is the TV a widescreen? + virtual int setDefaultAspect()=0; + virtual int setSource()=0; + virtual int setPosition(int x, int y)=0; + virtual int sync()=0; + virtual int play()=0; + virtual int stop()=0; + virtual int pause()=0; + virtual int unPause()=0; + virtual int fastForward()=0; + virtual int unFastForward()=0; + virtual int reset()=0; + virtual int blank()=0; + virtual int signalOn()=0; + virtual int signalOff()=0; + virtual int attachFrameBuffer()=0; // What does this do? +// virtual ULONG timecodeToFrameNumber(ULLONG timecode)=0; //Obsolete and not HD compatible + virtual ULLONG getCurrentTimestamp()=0; + virtual bool displayIFrame(const UCHAR* buffer, UINT length)=0; + virtual int EnterIframePlayback() {return 1;}; // Must not be implemented + + virtual bool supportsh264(){return false;}; + virtual void seth264mode(bool ish264) {h264=ish264;}; + virtual int getTeletextBufferFaktor(){return 1;}; + + virtual bool independentAVStartUp() {return true;}; + + + //Tells, if the device allows an independent startup of audio and video + // this needs internal buffers in device and is possible for windows and mvp + + virtual bool PTSIFramePlayback() {return false;}; + // Tells, if the iframe playback is realized using a manipulation of the pts of the packets + //android does it like this... + + virtual bool blockingDrainTarget() { return false;}; + // if the draintargets blocks, the feed has to be stop when pausing + + + virtual void turnVideoOn(){}; + virtual void turnVideoOff(){}; +// virtual ULLONG frameNumberToTimecode(ULONG timecode) { return 0; };//Obsolete and not HD compatible + +#ifdef DEV + virtual int test() { return 0; } + virtual int test2() { return 0; } +#endif + + int getMode() { return mode; } + UCHAR getFormat() { return format; } + UINT getScreenWidth() { return screenWidth; } + UINT getScreenHeight() { return screenHeight; } + UCHAR getTVsize() { return tvsize; } + + //hmsf framesToHMSF(ULONG frames,double fps); + // UINT getFPS(); //removed + + // Video formats - AV_SET_VID_DISP_FMT + const static UCHAR NTSC = 0; + const static UCHAR PAL = 1; + + // Video connections - AV_SET_VID_OUTPUT + const static UCHAR COMPOSITERGB = 1; + const static UCHAR SVIDEO = 2; + + // Video aspect ratios - AV_SET_VID_RATIO + const static UCHAR ASPECT4X3 = 0; + const static UCHAR ASPECT16X9 = 1; + + // Video modes - AV_SET_VID_MODE + const static UCHAR NORMAL = 0; + const static UCHAR LETTERBOX = 1; +/* + Actual Source Aspect Aspect IOCTL Mode IOCTL MODE A MODE B + + 4:3 4:3 NORMAL fullframe43 fullframe43 + 4:3 16:9 NORMAL fullframe43 fullframe43 -- invalid? + 4:3 4:3 LETTERBOX fullframe43 fullframe43 + 4:3 16:9 LETTERBOX fullframe43 fullframe43 -- invalid? + 16:9 4:3 NORMAL chop sides fullframe169 + 16:9 16:9 NORMAL chop sides fullframe169 + 16:9 4:3 LETTERBOX letterbox letterbox + 16:9 16:9 LETTERBOX chop sides fullframe169 + + Conclusions + + 1. There are two chip modes - accessible by reopening the fd + 2. The video chip knows the aspect ratio purely from the incoming MPEG + 3. MODE A is for 4:3 TVs, MODE B is for 16:9 TVs + + To switch to MODE A, set the aspect ioctl to 4:3 and reopen the FD. + To switch to MODE B, set the aspect ioctl to 16:9 and reopen the FD. +*/ + + const static UCHAR UNKNOWN2 = 2; + const static UCHAR QUARTER = 3; + const static UCHAR EIGHTH = 4; + const static UCHAR ZOOM = 5; + const static UCHAR UNKNOWN6 = 6; + + protected: + static Video* instance; + int initted; + int fdVideo; + + UCHAR tvsize; + UCHAR format; + UCHAR connection; + UCHAR aspectRatio; + UCHAR mode; + bool h264; + + UINT screenWidth; + UINT screenHeight; +}; + +#endif diff --git a/videomvp.cc b/videomvp.cc index 6e627af..1c6313b 100644 --- a/videomvp.cc +++ b/videomvp.cc @@ -1,405 +1,406 @@ -/* - 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. -*/ - -#include "videomvp.h" - -// temp -#include "log.h" - -VideoMVP::VideoMVP() -{ - if (instance) return; -} - -VideoMVP::~VideoMVP() -{ - instance = NULL; -} - -int VideoMVP::init(UCHAR tformat) -{ - if (initted) return 0; - initted = 1; - - if ((fdVideo = open("/dev/vdec_dev", O_WRONLY)) < 0) return 0; - - if (!setFormat(tformat)) { shutdown(); return 0; } - if (!setConnection(COMPOSITERGB)) { shutdown(); return 0; } - if (!setAspectRatio(ASPECT4X3)) { shutdown(); return 0; } - if (!setMode(NORMAL)) { shutdown(); return 0; } - if (!setSource()) { shutdown(); return 0; } - if (!attachFrameBuffer()) { shutdown(); return 0; } - - setTVsize(ASPECT4X3); - - if (format == PAL) setLetterboxBorder("38"); - else setLetterboxBorder("31"); - - stop(); - - - return 1; -} - -void VideoMVP::setLetterboxBorder(char* border) -{ - FILE* fdlbox = fopen("/proc/lbox_border", "w"); - if (!fdlbox) return; - fputs(border, fdlbox); - fclose(fdlbox); -} - -int VideoMVP::setTVsize(UCHAR ttvsize) -{ - tvsize = ttvsize; - - // Override the aspect ratio usage, temporarily use to set the video chip mode - if (!setAspectRatio(tvsize)) { shutdown(); return 0; } - close(fdVideo); - if ((fdVideo = open("/dev/vdec_dev", O_WRONLY)) < 0) return 0; - if (!setSource()) { shutdown(); return 0; } - if (!attachFrameBuffer()) { shutdown(); return 0; } - - // Reopening the fd causes the scart aspect line to go back to 4:3 - // Set this again to the same as the tv screen size - if (!setAspectRatio(tvsize)) { shutdown(); return 0; } - - // mode == LETTERBOX is invalid if the TV is widescreen - if (tvsize == ASPECT16X9) setMode(NORMAL); - - return 1; -} - -int VideoMVP::setDefaultAspect() -{ - return setAspectRatio(tvsize); -} - -int VideoMVP::shutdown() -{ - if (!initted) return 0; - initted = 0; - close(fdVideo); - return 1; -} - -int VideoMVP::checkSCART() -{ - // Returns 3 for SCART Composite out - // Returns 3 for SCART S-Video out - // Returns 2 for SCART RGB out - // Returns 3 for SCART not plugged in - - // So, as you can have RGB and composite out simultaneously, - // and it can't detect S-Video, what is the point of this? - - int scart; - if (ioctl(fdVideo, AV_CHK_SCART, &scart) != 0) return -10; - - return scart; -} - -int VideoMVP::setFormat(UCHAR tformat) -{ - if (!initted) return 0; - if ((tformat != PAL) && (tformat != NTSC)) return 0; - format = tformat; - - if (ioctl(fdVideo, AV_SET_VID_DISP_FMT, format) != 0) return 0; - - if (format == NTSC) - { - screenWidth = 720; - screenHeight = 480; - } - if (format == PAL) - { - screenWidth = 720; - screenHeight = 576; - } - - return 1; -} - -int VideoMVP::setConnection(UCHAR tconnection) -{ - if (!initted) return 0; - if ((tconnection != COMPOSITERGB) && (tconnection != SVIDEO)) return 0; - connection = tconnection; - - if (ioctl(fdVideo, AV_SET_VID_OUTPUT, connection) != 0) return 0; - return 1; -} - -int VideoMVP::setAspectRatio(UCHAR taspectRatio) -{ - if (!initted) return 0; - if ((taspectRatio != ASPECT4X3) && (taspectRatio != ASPECT16X9)) return 0; - aspectRatio = taspectRatio; - - Log::getInstance()->log("Video", Log::DEBUG, "Setting aspect to %i", aspectRatio); - - if (ioctl(fdVideo, AV_SET_VID_RATIO, aspectRatio) != 0) return 0; - return 1; -} - -int VideoMVP::setMode(UCHAR tmode) -{ - if (!initted) return 0; - - if ((tmode == LETTERBOX) && (tvsize == ASPECT16X9)) return 0; // invalid mode - - if ((tmode != NORMAL) && (tmode != LETTERBOX) && (tmode != UNKNOWN2) && (tmode != QUARTER) && (tmode != EIGHTH) - && (tmode != ZOOM) && (tmode != UNKNOWN6)) return 0; - mode = tmode; - - if (ioctl(fdVideo, AV_SET_VID_MODE, mode) != 0) return 0; - return 1; -} - -int VideoMVP::signalOff() -{ - if (ioctl(fdVideo, AV_SET_VID_DENC, 0) != 0) return 0; - return 1; -} - -int VideoMVP::signalOn() -{ - if (ioctl(fdVideo, AV_SET_VID_DENC, 1) != 0) return 0; - return 1; -} - -int VideoMVP::setSource() -{ - if (!initted) return 0; - - // What does this do... - if (ioctl(fdVideo, AV_SET_VID_SRC, 1) != 0) return 0; - return 1; -} - -int VideoMVP::setPosition(int x, int y) -{ - if (!initted) return 0; - -// vid_pos_regs_t pos_d; -// pos_d.x = x; -// pos_d.y = y; - - vid_pos_regs_t pos_d; - - memset(&pos_d, 0, sizeof(pos_d)); - - pos_d.dest.y = y; - pos_d.dest.x = x; -/* -typedef struct { - int w; - int h; - int scale; - int x1; - int y; - int x; - int y2; - int x3; - int y3; - int x4; - int y4; -} vid_pos_regs_t; -*/ - -/* - pos_d.w = 100; - pos_d.h = 30; - pos_d.scale = 2; - pos_d.x1 = 0; - pos_d.y = 100; // Top left X - pos_d.x = 50; // Top left Y - pos_d.y2 = 30; - pos_d.x3 = 60; - pos_d.y3 = 90; - pos_d.x4 = 120; - pos_d.y4 = 150; -*/ - - if (ioctl(fdVideo, AV_SET_VID_POSITION, &pos_d) != 0) return 0; - return 1; -} - -int VideoMVP::sync() -{ - if (!initted) return 0; - - if (ioctl(fdVideo, AV_SET_VID_SYNC, 2) != 0) return 0; - return 1; -} - -int VideoMVP::play() -{ - if (!initted) return 0; - - if (ioctl(fdVideo, AV_SET_VID_PLAY, 0) != 0) return 0; - return 1; -} - -int VideoMVP::stop() -{ - if (!initted) return 0; - - if (ioctl(fdVideo, AV_SET_VID_STOP, 0) != 0) return 0; - return 1; -} - -int VideoMVP::reset() -{ - if (!initted) return 0; - - if (ioctl(fdVideo, AV_SET_VID_RESET, 0x11) != 0) return 0; - return 1; -} - -int VideoMVP::pause() -{ - if (!initted) return 0; - - if (ioctl(fdVideo, AV_SET_VID_PAUSE, 0) != 0) return 0; - return 1; -} - -int VideoMVP::unPause() // FIXME get rid - same as play!! -{ - if (!initted) return 0; - if (ioctl(fdVideo, AV_SET_VID_PLAY, 0) != 0) return 0; - return 1; -} - -int VideoMVP::fastForward() -{ - if (!initted) return 0; - - if (ioctl(fdVideo, AV_SET_VID_FFWD, 1) != 0) return 0; - return 1; -} - -int VideoMVP::unFastForward() -{ - if (!initted) return 0; - -// if (ioctl(fdVideo, AV_SET_VID_RESET, 0x11) != 0) return 0; // don't need this. - - if (ioctl(fdVideo, AV_SET_VID_PLAY, 0) != 0) return 0; - return 1; -} - -int VideoMVP::attachFrameBuffer() -{ - if (!initted) return 0; - - if (ioctl(fdVideo, AV_SET_VID_FB, 0) != 0) return 0; - return 1; -} - -int VideoMVP::blank(void) -{ - if (ioctl(fdVideo, AV_SET_VID_FB, 1) != 0) return 0; - if (ioctl(fdVideo, AV_SET_VID_FB, 0) != 0) return 0; - return 1; -} - -ULLONG VideoMVP::getCurrentTimestamp() -{ - sync_data_t timestamps; - if (ioctl(fdVideo, AV_GET_VID_TIMESTAMPS, ×tamps) == 0) - { - // FIXME are these the right way around? - - timestamps.stc = (timestamps.stc >> 31 ) | (timestamps.stc & 1); - timestamps.pts = (timestamps.pts >> 31 ) | (timestamps.pts & 1); - - return timestamps.stc; - } - else - { - return 0; - } -} - -ULONG VideoMVP::timecodeToFrameNumber(ULLONG timecode) -{ - if (format == PAL) return (ULONG)(((double)timecode / (double)90000) * (double)25); - else return (ULONG)(((double)timecode / (double)90000) * (double)30); -} - -#ifdef DEV -int VideoMVP::test() -{ - return 0; - -// ULLONG stc = 0; -// return ioctl(fdVideo, AV_SET_VID_STC, &stc); -/* - // reset(); - return 1; -*/ -} - -int VideoMVP::test2() -{ - return 0; -} -#endif - -void VideoMVP::PrepareMediaSample(const MediaPacketList& mplist,UINT samplepos) -{ - MediaPacketList::const_iterator iter = mplist.begin(); - deliver_start = iter->pos_buffer + samplepos; - mediapacket_len[0] = deliver_length = iter->length; - deliver_count = 1; - while (++iter != mplist.end() && - iter->pos_buffer == deliver_start + deliver_length) - { - deliver_length += iter->length; - mediapacket_len[deliver_count] = iter->length; - ++deliver_count; - if (deliver_length >= WRITE_LENGTH || - deliver_count == WRITE_PACKETS) break; - } -} - -UINT VideoMVP::DeliverMediaSample(UCHAR* buffer, UINT* samplepos) -{ - int written = ::write(fdVideo, buffer + deliver_start, deliver_length); - if (written == (int)deliver_length) { *samplepos = 0; return deliver_count;} - if (written <= 0) return 0; - // Handle a partial write. Is this possible? Should we bother? - UINT i = 0; - while ((written -= mediapacket_len[i]) >= 0) i++; - *samplepos = mediapacket_len[i] + written; - return i; -} - -void VideoMVP::ResetTimeOffsets() -{ -} - -void VideoMVP::displayIFrame(const UCHAR* buffer, UINT length) -{ - write(fdVideo, buffer, length); -} +/* + 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. +*/ + +#include "videomvp.h" + +// temp +#include "log.h" + +VideoMVP::VideoMVP() +{ + if (instance) return; +} + +VideoMVP::~VideoMVP() +{ + instance = NULL; +} + +int VideoMVP::init(UCHAR tformat) +{ + if (initted) return 0; + initted = 1; + + if ((fdVideo = open("/dev/vdec_dev", O_WRONLY)) < 0) return 0; + + if (!setFormat(tformat)) { shutdown(); return 0; } + if (!setConnection(COMPOSITERGB)) { shutdown(); return 0; } + if (!setAspectRatio(ASPECT4X3)) { shutdown(); return 0; } + if (!setMode(NORMAL)) { shutdown(); return 0; } + if (!setSource()) { shutdown(); return 0; } + if (!attachFrameBuffer()) { shutdown(); return 0; } + + setTVsize(ASPECT4X3); + + if (format == PAL) setLetterboxBorder("38"); + else setLetterboxBorder("31"); + + stop(); + + + return 1; +} + +void VideoMVP::setLetterboxBorder(char* border) +{ + FILE* fdlbox = fopen("/proc/lbox_border", "w"); + if (!fdlbox) return; + fputs(border, fdlbox); + fclose(fdlbox); +} + +int VideoMVP::setTVsize(UCHAR ttvsize) +{ + tvsize = ttvsize; + + // Override the aspect ratio usage, temporarily use to set the video chip mode + if (!setAspectRatio(tvsize)) { shutdown(); return 0; } + close(fdVideo); + if ((fdVideo = open("/dev/vdec_dev", O_WRONLY)) < 0) return 0; + if (!setSource()) { shutdown(); return 0; } + if (!attachFrameBuffer()) { shutdown(); return 0; } + + // Reopening the fd causes the scart aspect line to go back to 4:3 + // Set this again to the same as the tv screen size + if (!setAspectRatio(tvsize)) { shutdown(); return 0; } + + // mode == LETTERBOX is invalid if the TV is widescreen + if (tvsize == ASPECT16X9) setMode(NORMAL); + + return 1; +} + +int VideoMVP::setDefaultAspect() +{ + return setAspectRatio(tvsize); +} + +int VideoMVP::shutdown() +{ + if (!initted) return 0; + initted = 0; + close(fdVideo); + return 1; +} + +int VideoMVP::checkSCART() +{ + // Returns 3 for SCART Composite out + // Returns 3 for SCART S-Video out + // Returns 2 for SCART RGB out + // Returns 3 for SCART not plugged in + + // So, as you can have RGB and composite out simultaneously, + // and it can't detect S-Video, what is the point of this? + + int scart; + if (ioctl(fdVideo, AV_CHK_SCART, &scart) != 0) return -10; + + return scart; +} + +int VideoMVP::setFormat(UCHAR tformat) +{ + if (!initted) return 0; + if ((tformat != PAL) && (tformat != NTSC)) return 0; + format = tformat; + + if (ioctl(fdVideo, AV_SET_VID_DISP_FMT, format) != 0) return 0; + + if (format == NTSC) + { + screenWidth = 720; + screenHeight = 480; + } + if (format == PAL) + { + screenWidth = 720; + screenHeight = 576; + } + + return 1; +} + +int VideoMVP::setConnection(UCHAR tconnection) +{ + if (!initted) return 0; + if ((tconnection != COMPOSITERGB) && (tconnection != SVIDEO)) return 0; + connection = tconnection; + + if (ioctl(fdVideo, AV_SET_VID_OUTPUT, connection) != 0) return 0; + return 1; +} + +int VideoMVP::setAspectRatio(UCHAR taspectRatio) +{ + if (!initted) return 0; + if ((taspectRatio != ASPECT4X3) && (taspectRatio != ASPECT16X9)) return 0; + aspectRatio = taspectRatio; + + Log::getInstance()->log("Video", Log::DEBUG, "Setting aspect to %i", aspectRatio); + + if (ioctl(fdVideo, AV_SET_VID_RATIO, aspectRatio) != 0) return 0; + return 1; +} + +int VideoMVP::setMode(UCHAR tmode) +{ + if (!initted) return 0; + + if ((tmode == LETTERBOX) && (tvsize == ASPECT16X9)) return 0; // invalid mode + + if ((tmode != NORMAL) && (tmode != LETTERBOX) && (tmode != UNKNOWN2) && (tmode != QUARTER) && (tmode != EIGHTH) + && (tmode != ZOOM) && (tmode != UNKNOWN6)) return 0; + mode = tmode; + + if (ioctl(fdVideo, AV_SET_VID_MODE, mode) != 0) return 0; + return 1; +} + +int VideoMVP::signalOff() +{ + if (ioctl(fdVideo, AV_SET_VID_DENC, 0) != 0) return 0; + return 1; +} + +int VideoMVP::signalOn() +{ + if (ioctl(fdVideo, AV_SET_VID_DENC, 1) != 0) return 0; + return 1; +} + +int VideoMVP::setSource() +{ + if (!initted) return 0; + + // What does this do... + if (ioctl(fdVideo, AV_SET_VID_SRC, 1) != 0) return 0; + return 1; +} + +int VideoMVP::setPosition(int x, int y) +{ + if (!initted) return 0; + +// vid_pos_regs_t pos_d; +// pos_d.x = x; +// pos_d.y = y; + + vid_pos_regs_t pos_d; + + memset(&pos_d, 0, sizeof(pos_d)); + + pos_d.dest.y = y; + pos_d.dest.x = x; +/* +typedef struct { + int w; + int h; + int scale; + int x1; + int y; + int x; + int y2; + int x3; + int y3; + int x4; + int y4; +} vid_pos_regs_t; +*/ + +/* + pos_d.w = 100; + pos_d.h = 30; + pos_d.scale = 2; + pos_d.x1 = 0; + pos_d.y = 100; // Top left X + pos_d.x = 50; // Top left Y + pos_d.y2 = 30; + pos_d.x3 = 60; + pos_d.y3 = 90; + pos_d.x4 = 120; + pos_d.y4 = 150; +*/ + + if (ioctl(fdVideo, AV_SET_VID_POSITION, &pos_d) != 0) return 0; + return 1; +} + +int VideoMVP::sync() +{ + if (!initted) return 0; + + if (ioctl(fdVideo, AV_SET_VID_SYNC, 2) != 0) return 0; + return 1; +} + +int VideoMVP::play() +{ + if (!initted) return 0; + + if (ioctl(fdVideo, AV_SET_VID_PLAY, 0) != 0) return 0; + return 1; +} + +int VideoMVP::stop() +{ + if (!initted) return 0; + + if (ioctl(fdVideo, AV_SET_VID_STOP, 0) != 0) return 0; + return 1; +} + +int VideoMVP::reset() +{ + if (!initted) return 0; + + if (ioctl(fdVideo, AV_SET_VID_RESET, 0x11) != 0) return 0; + return 1; +} + +int VideoMVP::pause() +{ + if (!initted) return 0; + + if (ioctl(fdVideo, AV_SET_VID_PAUSE, 0) != 0) return 0; + return 1; +} + +int VideoMVP::unPause() // FIXME get rid - same as play!! +{ + if (!initted) return 0; + if (ioctl(fdVideo, AV_SET_VID_PLAY, 0) != 0) return 0; + return 1; +} + +int VideoMVP::fastForward() +{ + if (!initted) return 0; + + if (ioctl(fdVideo, AV_SET_VID_FFWD, 1) != 0) return 0; + return 1; +} + +int VideoMVP::unFastForward() +{ + if (!initted) return 0; + +// if (ioctl(fdVideo, AV_SET_VID_RESET, 0x11) != 0) return 0; // don't need this. + + if (ioctl(fdVideo, AV_SET_VID_PLAY, 0) != 0) return 0; + return 1; +} + +int VideoMVP::attachFrameBuffer() +{ + if (!initted) return 0; + + if (ioctl(fdVideo, AV_SET_VID_FB, 0) != 0) return 0; + return 1; +} + +int VideoMVP::blank(void) +{ + if (ioctl(fdVideo, AV_SET_VID_FB, 1) != 0) return 0; + if (ioctl(fdVideo, AV_SET_VID_FB, 0) != 0) return 0; + return 1; +} + +ULLONG VideoMVP::getCurrentTimestamp() +{ + sync_data_t timestamps; + if (ioctl(fdVideo, AV_GET_VID_TIMESTAMPS, ×tamps) == 0) + { + // FIXME are these the right way around? + + timestamps.stc = (timestamps.stc >> 31 ) | (timestamps.stc & 1); + timestamps.pts = (timestamps.pts >> 31 ) | (timestamps.pts & 1); + + return timestamps.stc; + } + else + { + return 0; + } +} + +ULONG VideoMVP::timecodeToFrameNumber(ULLONG timecode) +{ + if (format == PAL) return (ULONG)(((double)timecode / (double)90000) * (double)25); + else return (ULONG)(((double)timecode / (double)90000) * (double)30); +} + +#ifdef DEV +int VideoMVP::test() +{ + return 0; + +// ULLONG stc = 0; +// return ioctl(fdVideo, AV_SET_VID_STC, &stc); +/* + // reset(); + return 1; +*/ +} + +int VideoMVP::test2() +{ + return 0; +} +#endif + +void VideoMVP::PrepareMediaSample(const MediaPacketList& mplist,UINT samplepos) +{ + MediaPacketList::const_iterator iter = mplist.begin(); + deliver_start = iter->pos_buffer + samplepos; + mediapacket_len[0] = deliver_length = iter->length; + deliver_count = 1; + while (++iter != mplist.end() && + iter->pos_buffer == deliver_start + deliver_length) + { + deliver_length += iter->length; + mediapacket_len[deliver_count] = iter->length; + ++deliver_count; + if (deliver_length >= WRITE_LENGTH || + deliver_count == WRITE_PACKETS) break; + } +} + +UINT VideoMVP::DeliverMediaSample(UCHAR* buffer, UINT* samplepos) +{ + int written = ::write(fdVideo, buffer + deliver_start, deliver_length); + if (written == (int)deliver_length) { *samplepos = 0; return deliver_count;} + if (written <= 0) return 0; + // Handle a partial write. Is this possible? Should we bother? + UINT i = 0; + while ((written -= mediapacket_len[i]) >= 0) i++; + *samplepos = mediapacket_len[i] + written; + return i; +} + +void VideoMVP::ResetTimeOffsets() +{ +} + +bool VideoMVP::displayIFrame(const UCHAR* buffer, UINT length) +{ + write(fdVideo, buffer, length); + return true; +} diff --git a/videomvp.h b/videomvp.h index fc086e6..9184bc3 100644 --- a/videomvp.h +++ b/videomvp.h @@ -1,155 +1,155 @@ -/* - 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. -*/ - -// Thanks to Jon Gettler and BtB of the MVPMC project for all the hardware information - - -#ifndef VIDEOMVP_H -#define VIDEOMVP_H - -// FIXME - check why so many things include unistd - -#include -#include -#include -#include -#include - -#include - -#include "defines.h" -#include "video.h" - -typedef struct -{ - int nleft; - int state; -} vid_state_regs_t; - -typedef struct -{ - uint64_t stc; - uint64_t pts; -} sync_data_t; - -typedef struct -{ - int y; - int x; - int w; - int h; -} vid_rect_t; - -typedef struct -{ - vid_rect_t src; - vid_rect_t dest; -} vid_pos_regs_t; - -struct vid_regs -{ - UCHAR dummy[44]; -}; - -#define AV_SET_VID_STOP _IOW('v', 21, int) -#define AV_SET_VID_PLAY _IOW('v', 22, int) -#define AV_SET_VID_FREEZE _IOW('v', 23, int) -#define AV_SET_VID_RESUME _IOW('v', 24, int) -#define AV_SET_VID_SRC _IOW('v', 25, int) -#define AV_SET_VID_FB _IOW('v', 26, int) -#define AV_GET_VID_STATE _IOR('v', 27, vid_state_regs_t*) -#define AV_SET_VID_PAUSE _IOW('v', 28, int) -#define AV_SET_VID_FFWD _IOW('v', 29, int) -#define AV_SET_VID_SLOMO _IOW('v', 30, int) -#define AV_SET_VID_BLANK _IOW('v', 32, int) -#define AV_SET_VID_POSITION _IOW('v', 36, vid_pos_regs_t*) -#define AV_SET_VID_SCALE_ON _IOW('v', 37, int) -#define AV_SET_VID_SCALE_OFF _IOW('v', 38, int) -#define AV_GET_VID_TIMESTAMPS _IOR('v', 39, sync_data_t*) -#define AV_SET_VID_STC _IOW('v', 40, uint64_t*) -#define AV_SET_VID_RATIO _IOW('v', 41, int) -#define AV_SET_VID_SYNC _IOW('v', 42, int) -#define AV_SET_VID_DISABLE_SYNC _IOW('v', 43, int) -#define AV_SET_VID_DISP_FMT _IOW('v', 45, int) -#define AV_SET_VID_RESET _IOW('v', 51, int) -#define AV_SET_VID_OUTPUT _IOW('v', 57, int) -#define AV_SET_VID_MODE _IOW('v', 58, int) -#define AV_GET_VID_REGS _IOR('v', 61, struct vid_regs*) -#define AV_SET_VID_COLORBAR _IOW('v', 62, int) -#define AV_SET_VID_DENC _IOW('v', 63, int) -#define AV_CHK_SCART _IOW('v', 64, int) - -#define WRITE_LENGTH (32*1024) // Consume up to 32K at a time from Stream -#define WRITE_PACKETS 16 // But no more than 16 packets - -class VideoMVP : public Video -{ - public: - VideoMVP(); - ~VideoMVP(); - - int init(UCHAR format); - int shutdown(); - - int setFormat(UCHAR format); - int setConnection(UCHAR connection); - int setAspectRatio(UCHAR aspectRatio); // This one does the pin 8 scart widescreen switching - int setMode(UCHAR mode); - int setTVsize(UCHAR size); // Is the TV a widescreen? - int setDefaultAspect(); - int setSource(); - int setPosition(int x, int y); - int sync(); - int play(); - int stop(); - int pause(); - int unPause(); - int fastForward(); - int unFastForward(); - int reset(); - int blank(); - int signalOn(); - int signalOff(); - int attachFrameBuffer(); // What does this do? - ULONG timecodeToFrameNumber(ULLONG timecode); - ULLONG getCurrentTimestamp(); - void displayIFrame(const UCHAR* buffer, UINT length); - - // Writing Data to Videodevice - virtual void PrepareMediaSample(const MediaPacketList&, UINT samplepos); - virtual UINT DeliverMediaSample( UCHAR* buffer, UINT* samplepos); - virtual long long SetStartOffset(long long curreftime, bool *rsync) - { return 0; }; - virtual void ResetTimeOffsets(); - -#ifdef DEV - int test(); - int test2(); -#endif - - private: - int checkSCART(); - void setLetterboxBorder(char* border); - - UINT deliver_start, deliver_length, deliver_count; - UINT mediapacket_len[WRITE_PACKETS]; -}; - -#endif +/* + 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. +*/ + +// Thanks to Jon Gettler and BtB of the MVPMC project for all the hardware information + + +#ifndef VIDEOMVP_H +#define VIDEOMVP_H + +// FIXME - check why so many things include unistd + +#include +#include +#include +#include +#include + +#include + +#include "defines.h" +#include "video.h" + +typedef struct +{ + int nleft; + int state; +} vid_state_regs_t; + +typedef struct +{ + uint64_t stc; + uint64_t pts; +} sync_data_t; + +typedef struct +{ + int y; + int x; + int w; + int h; +} vid_rect_t; + +typedef struct +{ + vid_rect_t src; + vid_rect_t dest; +} vid_pos_regs_t; + +struct vid_regs +{ + UCHAR dummy[44]; +}; + +#define AV_SET_VID_STOP _IOW('v', 21, int) +#define AV_SET_VID_PLAY _IOW('v', 22, int) +#define AV_SET_VID_FREEZE _IOW('v', 23, int) +#define AV_SET_VID_RESUME _IOW('v', 24, int) +#define AV_SET_VID_SRC _IOW('v', 25, int) +#define AV_SET_VID_FB _IOW('v', 26, int) +#define AV_GET_VID_STATE _IOR('v', 27, vid_state_regs_t*) +#define AV_SET_VID_PAUSE _IOW('v', 28, int) +#define AV_SET_VID_FFWD _IOW('v', 29, int) +#define AV_SET_VID_SLOMO _IOW('v', 30, int) +#define AV_SET_VID_BLANK _IOW('v', 32, int) +#define AV_SET_VID_POSITION _IOW('v', 36, vid_pos_regs_t*) +#define AV_SET_VID_SCALE_ON _IOW('v', 37, int) +#define AV_SET_VID_SCALE_OFF _IOW('v', 38, int) +#define AV_GET_VID_TIMESTAMPS _IOR('v', 39, sync_data_t*) +#define AV_SET_VID_STC _IOW('v', 40, uint64_t*) +#define AV_SET_VID_RATIO _IOW('v', 41, int) +#define AV_SET_VID_SYNC _IOW('v', 42, int) +#define AV_SET_VID_DISABLE_SYNC _IOW('v', 43, int) +#define AV_SET_VID_DISP_FMT _IOW('v', 45, int) +#define AV_SET_VID_RESET _IOW('v', 51, int) +#define AV_SET_VID_OUTPUT _IOW('v', 57, int) +#define AV_SET_VID_MODE _IOW('v', 58, int) +#define AV_GET_VID_REGS _IOR('v', 61, struct vid_regs*) +#define AV_SET_VID_COLORBAR _IOW('v', 62, int) +#define AV_SET_VID_DENC _IOW('v', 63, int) +#define AV_CHK_SCART _IOW('v', 64, int) + +#define WRITE_LENGTH (32*1024) // Consume up to 32K at a time from Stream +#define WRITE_PACKETS 16 // But no more than 16 packets + +class VideoMVP : public Video +{ + public: + VideoMVP(); + ~VideoMVP(); + + int init(UCHAR format); + int shutdown(); + + int setFormat(UCHAR format); + int setConnection(UCHAR connection); + int setAspectRatio(UCHAR aspectRatio); // This one does the pin 8 scart widescreen switching + int setMode(UCHAR mode); + int setTVsize(UCHAR size); // Is the TV a widescreen? + int setDefaultAspect(); + int setSource(); + int setPosition(int x, int y); + int sync(); + int play(); + int stop(); + int pause(); + int unPause(); + int fastForward(); + int unFastForward(); + int reset(); + int blank(); + int signalOn(); + int signalOff(); + int attachFrameBuffer(); // What does this do? + ULONG timecodeToFrameNumber(ULLONG timecode); + ULLONG getCurrentTimestamp(); + bool displayIFrame(const UCHAR* buffer, UINT length); + + // Writing Data to Videodevice + virtual void PrepareMediaSample(const MediaPacketList&, UINT samplepos); + virtual UINT DeliverMediaSample( UCHAR* buffer, UINT* samplepos); + virtual long long SetStartOffset(long long curreftime, bool *rsync) + { return 0; }; + virtual void ResetTimeOffsets(); + +#ifdef DEV + int test(); + int test2(); +#endif + + private: + int checkSCART(); + void setLetterboxBorder(char* border); + + UINT deliver_start, deliver_length, deliver_count; + UINT mediapacket_len[WRITE_PACKETS]; +}; + +#endif diff --git a/videowin.cc b/videowin.cc index b62861c..8979151 100644 --- a/videowin.cc +++ b/videowin.cc @@ -1,2063 +1,2066 @@ -/* - 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. -*/ - - - -#include "videowin.h" -#include "log.h" -#include "dssourcefilter.h" -#include "dsallocator.h" -#include "vdr.h" -#include "osdwin.h" -#include "audiowin.h" -#include "wwinvideofilter.h" -#include "wwinvideoh264filter.h" -#include "wtabbar.h" -#include "woptionpane.h" -#include "i18n.h" -#include "demuxer.h" - -#include -#include - -void AdjustWindow(); - - - -VideoWin::VideoWin() -{ - dsinited=false; - dsgraphbuilder=NULL; - dsmediacontrol=NULL; - dsrenderer=NULL; - dsrefclock=NULL; - dsmediafilter=NULL; - dsbasicaudio=NULL; - sourcefilter=NULL; - allocatorvmr=NULL; - cr_time=0; - lastaudiomode=MPTYPE_MPEG_AUDIO; - //lastaudiomode=MPTYPE_AC3; - dsvmrsurfnotify=NULL; - filtermutex=CreateMutex(NULL,FALSE,NULL); - offsetnotset=true; - offsetvideonotset=true; - offsetaudionotset=true; - startoffset=0; - lastrefaudiotime=0; - lastrefvideotime=0; - lastreftimeRT=0; - lastreftimePTS=0; - firstsynched=false; - cur_audio_media_sample=NULL; - cur_video_media_sample=NULL; - videoon=true; - audioon=true; - audiovolume=0; - pseudotvsize=0; - videoposx=0; - videoposy=0; - aud_type=Audio::MPEG2_PES; - iframemode=false;//We are not in Iframe mode at begining - vmrdeinterlacing=2;//Best - videofilterselected=-1; - videoH264filterselected=-1; - OSVERSIONINFO verinfo; - verinfo.dwOSVersionInfoSize=sizeof(verinfo); - GetVersionEx(&verinfo); - - if (verinfo.dwMajorVersion>=6) { - currentpresenter=EVR; - } else { - currentpresenter=VMR9; - } - videoH264dtsfix=false; - videompeg2dtsfix=false; - - - - -} - -VideoWin::~VideoWin() -{ - CleanupDS(); - CloseHandle(filtermutex); - unsigned int i; - for (i=0;ilog("VideoWin", Log::ERR , "Unable to create FilterMapper!"); - return; - } - /* Wishlist, what Mediatypes do we want */ - GUID mtypesin[]={MEDIATYPE_Video,MEDIASUBTYPE_MPEG2_VIDEO}; - IEnumMoniker *myenum; - result = filtmap->EnumMatchingFilters(&myenum,0,TRUE,MERIT_DO_NOT_USE+1, - TRUE,1,mtypesin,NULL,NULL,FALSE,TRUE,0,NULL,NULL,NULL); - if (result != S_OK) - { - filtmap->Release(); - Log::getInstance()->log("VideoWin", Log::ERR , "Unable to enum Filters!"); - return; - } - ULONG gethowmany; - IMoniker * moni; - while(myenum->Next(1,&moni,&gethowmany)==S_OK) - { - VideoFilterDesc desc; - ZeroMemory(&desc,sizeof(desc)); - - LPOLESTR string; - moni->GetDisplayName(0,0,&string); - desc.displayname=new char[wcslen(string)+1]; - wcstombs(desc.displayname,string,wcslen(string)+1); - CoTaskMemFree(string); - IPropertyBag *bag; - if (moni->BindToStorage(0,0,IID_IPropertyBag,(void**)&bag) == S_OK) - { - VARIANT vari; - VariantInit(&vari); - result = bag->Read(L"FriendlyName",&vari,NULL); - if (result == S_OK) - { - desc.friendlyname=new char[wcslen(vari.bstrVal)+1]; - wcstombs(desc.friendlyname,vari.bstrVal,wcslen(vari.bstrVal)+1); - } - VariantClear(&vari); - bag->Release(); - - } - - - videofilterlist.push_back(desc); - - - - moni->Release(); - // bctx->Release(); - } - int i; - videofilterselected=-1; - - - - myenum->Release(); - - - - filtmap->Release(); -} - -void VideoWin::initH264FilterDatabase() -{ - /* This method should determine all availiable DirectShow Filters */ - IFilterMapper2* filtmap=NULL; - HRESULT result; - result = CoCreateInstance(CLSID_FilterMapper2,NULL,CLSCTX_INPROC, - IID_IFilterMapper2,(void**)&filtmap); - if (result != S_OK) - { - Log::getInstance()->log("VideoWin", Log::ERR , "Unable to create FilterMapper!"); - return; - } - /* Wishlist, what Mediatypes do we want */ - GUID mtypesin[]={MEDIATYPE_Video,MEDIASUBTYPE_H264}; - IEnumMoniker *myenum; - result = filtmap->EnumMatchingFilters(&myenum,0,TRUE,MERIT_DO_NOT_USE+1, - TRUE,1,mtypesin,NULL,NULL,FALSE,TRUE,0,NULL,NULL,NULL); - if (result != S_OK) - { - filtmap->Release(); - Log::getInstance()->log("VideoWin", Log::ERR , "Unable to enum Filters!"); - return; - } - ULONG gethowmany; - IMoniker * moni; - while(myenum->Next(1,&moni,&gethowmany)==S_OK) - { - VideoFilterDesc desc; - ZeroMemory(&desc,sizeof(desc)); - - LPOLESTR string; - moni->GetDisplayName(0,0,&string); - desc.displayname=new char[wcslen(string)+1]; - wcstombs(desc.displayname,string,wcslen(string)+1); - CoTaskMemFree(string); - IPropertyBag *bag; - if (moni->BindToStorage(0,0,IID_IPropertyBag,(void**)&bag) == S_OK) - { - VARIANT vari; - VariantInit(&vari); - result = bag->Read(L"FriendlyName",&vari,NULL); - if (result == S_OK) - { - desc.friendlyname=new char[wcslen(vari.bstrVal)+1]; - wcstombs(desc.friendlyname,vari.bstrVal,wcslen(vari.bstrVal)+1); - } - VariantClear(&vari); - bag->Release(); - - } - - - videoH264filterlist.push_back(desc); - - - - moni->Release(); - // bctx->Release(); - } - int i; - videoH264filterselected=-1; - - - - myenum->Release(); - - - - filtmap->Release(); -} - -bool VideoWin::loadOptionsfromServer(VDR* vdr) -{ - char *name=vdr->configLoad("DirectShow","VideoFilter"); - - if (name != NULL) - { - for (int i = 0;i configLoad("DirectShow","VideoH264Filter"); - - if (name != NULL) - { - for (int i = 0;i configLoad("DirectShow","VMR9DeinterlacingMode"); - if (name != NULL) - { - if (STRCASECMP(name,"NoMix")==0) { - vmrdeinterlacing=0; - } else if (STRCASECMP(name,"None")==0) { - vmrdeinterlacing=1; - } else if (STRCASECMP(name,"Best")==0) { - vmrdeinterlacing=2; - } else if (STRCASECMP(name,"Bob")==0) { - vmrdeinterlacing=3; - } else if (STRCASECMP(name,"Weave")==0) { - vmrdeinterlacing=4; - } - } - - name=vdr->configLoad("DirectShow", "VideoPresenter"); - if (name!=NULL) { - if (STRCASECMP(name,"VMR9")==0) { - currentpresenter=VMR9; - } else if (STRCASECMP(name,"EVR")==0) { - currentpresenter=EVR; - } - } - if (!((OsdWin*)Osd::getInstance())->IsEvrSupported()) { - currentpresenter=VMR9; - } - - name=vdr->configLoad("DirectShow","videoH264dtsfix"); - if (name!=NULL) { - if (STRCASECMP(name,"YES")==0) { - videoH264dtsfix=true; - } else { - videoH264dtsfix=false; - } - } - name=vdr->configLoad("DirectShow","videompeg2dtsfix"); - if (name!=NULL) { - if (STRCASECMP(name,"YES")==0) { - videompeg2dtsfix=true; - } else { - videompeg2dtsfix=false; - } - } - - name=vdr->configLoad("DirectGraphics", "StretchFiltering"); - if (name!=NULL) { - if (STRCASECMP(name,"None")==0) { - ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_NONE); - } else if (STRCASECMP(name,"Point")==0) { - ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_POINT); - } else if (STRCASECMP(name,"Linear")==0) { - ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_LINEAR); - } - } - - - - - return true; - -} - -bool VideoWin::handleOptionChanges(Option* option) -{ - if( Video::handleOptionChanges(option)) return true; - switch(option->id) { - case 1: { - if (STRCASECMP(option->options[option->userSetChoice],"None")==0) { - ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_NONE); - } else if (STRCASECMP(option->options[option->userSetChoice],"Point")==0) { - ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_POINT); - } else if (STRCASECMP(option->options[option->userSetChoice],"Linear")==0) { - ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_LINEAR); - } - return true; - } break; - case 2: { - if (STRCASECMP(option->options[option->userSetChoice],"NoMix")==0) { - vmrdeinterlacing=0; - } else if (STRCASECMP(option->options[option->userSetChoice],"None")==0) { - vmrdeinterlacing=1; - } else if (STRCASECMP(option->options[option->userSetChoice],"Best")==0) { - vmrdeinterlacing=2; - } else if (STRCASECMP(option->options[option->userSetChoice],"Bob")==0) { - vmrdeinterlacing=3; - } else if (STRCASECMP(option->options[option->userSetChoice],"Weave")==0) { - vmrdeinterlacing=4; - } - }break; - case 3: { - if (STRCASECMP(option->options[option->userSetChoice],"VMR9")==0) { - currentpresenter=VMR9; - } else if (STRCASECMP(option->options[option->userSetChoice],"EVR")==0) { - currentpresenter=EVR; - } - }break; - case 4: { - if (STRCASECMP(option->options[option->userSetChoice],"Yes")==0) { - videoH264dtsfix=true; - } else { - videoH264dtsfix=false; - } - }break; - case 5: { - if (STRCASECMP(option->options[option->userSetChoice],"Yes")==0) { - videompeg2dtsfix=true; - } else { - videompeg2dtsfix=false; - } - }break; - }; - return false; - -} - -bool VideoWin::saveOptionstoServer() -{ - if (videofilterselected!=-1) { - VDR::getInstance()->configSave("DirectShow", - "VideoFilter",videofilterlist[videofilterselected].displayname); - VDR::getInstance()->configSave("DirectShow", - "VideoH264Filter",videoH264filterlist[videoH264filterselected].displayname); - } - return true; -} - -/*Option(UINT id, const char* displayText, const char* configSection, const char* configKey, UINT optionType, - UINT numChoices, UINT defaultChoice, UINT startInt, - const char * const * options, const char * const * optionkeys = NULL, AbstractOption* handler=NULL);*/ - -bool VideoWin::addOptionsToPanes(int panenumber,Options *options,WOptionPane* pane) -{ - if (!Video::addOptionsToPanes(panenumber,options,pane)) return false; - - - Option* option; - if (panenumber == 2) - { - DWORD scalingcaps=((OsdWin*)Osd::getInstance())->getFilterCaps(); - char **scalingopts=new char *[3]; - int i=0; - scalingopts[i]=new char[strlen("None")+1]; - strcpy(scalingopts[i],"None"); - i++; - if ((scalingcaps & D3DPTFILTERCAPS_MINFPOINT)!=0 - && (scalingcaps & D3DPTFILTERCAPS_MAGFPOINT)!=0) { - scalingopts[i]=new char[strlen("Point")+1]; - strcpy(scalingopts[i],"Point"); - i++; - } - if ((scalingcaps & D3DPTFILTERCAPS_MINFLINEAR)!=0 - && (scalingcaps & D3DPTFILTERCAPS_MAGFLINEAR)!=0) { - scalingopts[i]=new char[strlen("Linear")+1]; - strcpy(scalingopts[i],"Linear"); - i++; - } - option = new Option(1 ,tr("Video Stretching Filter"), "DirectGraphics", "StretchFiltering", Option::TYPE_TEXT, i, (i-1), 0, scalingopts,NULL,true, this); - options->push_back(option); - pane->addOptionLine(option); - static const char* deintopts[]={"NoMix","None","Best","Bob","Weave"}; - option = new Option(2,tr("VMR9 Deinterlacing Mode"), "DirectShow","VMR9DeinterlacingMode",Option::TYPE_TEXT,5,2,0,deintopts,NULL,false,this); - options->push_back(option); - pane->addOptionLine(option); - - if (((OsdWin*)Osd::getInstance())->IsEvrSupported()) - { - static const char* presenteropts[]={"EVR","VMR9"}; - option = new Option(3,tr("Video Presenter Filter"),"DirectShow", "VideoPresenter",Option::TYPE_TEXT,2, - (currentpresenter==EVR)?0:1,0,presenteropts,NULL,false,this); - } else { - static const char* presenteropts[]={"VMR9"}; - option = new Option(3,tr("Video Presenter Filter"),"DirectShow", "VideoPresenter",Option::TYPE_TEXT,1,0,0,presenteropts,NULL,false,this); - } - options->push_back(option); - pane->addOptionLine(option); - - static const char* yesnoopts[]={"Yes","No"}; - option = new Option(4,tr("Video H264 fix dts time"), "DirectShow","videoH264dtsfix",Option::TYPE_TEXT,2,1,0,yesnoopts,NULL,false,this); - options->push_back(option); - pane->addOptionLine(option); - - option = new Option(5,tr("Video Mpeg2 fix dts time"), "DirectShow","videompeg2dtsfix",Option::TYPE_TEXT,2,1,0,yesnoopts,NULL,false,this); - options->push_back(option); - pane->addOptionLine(option); - - - - } - - return true; -} - -IBaseFilter *VideoWin::getVideoFilter() -{ - IBaseFilter *curfilter= NULL; - if (videofilterselected == -1) - { - int i; - for (i = 0;i BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) - { - IAMDecoderCaps* desccaps=NULL; - if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK) - { - DWORD caps; - desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps); - if (caps == DECODER_CAP_SUPPORTED) - { - videofilterlist[i].vmr9tested = true; - videofilterlist[i].vmr9 = true; - videofilterselected = i; - } - else - { - videofilterlist[i].vmr9tested = true; - videofilterlist[i].vmr9 = false; - - curfilter->Release(); - curfilter=NULL; - } - } - desccaps->Release(); - } - moni->Release(); - } - delete [] name; - bindctx->Release(); - } - if (videofilterlist[i].vmr9) break; - - } - if (curfilter != NULL) - { - VDR *vdr=VDR::getInstance(); - if (vdr != NULL) - { - vdr->configSave("DirectShow","VideoFilter", - videofilterlist[videofilterselected].displayname); - } - return curfilter; - } - } - else - { - IBindCtx *bindctx=NULL; - if (CreateBindCtx(0,&bindctx)!=S_OK) return NULL; - IMoniker * moni=NULL; - LPCOLESTR name=new WCHAR[strlen(videofilterlist[videofilterselected].displayname)+1]; - mbstowcs((wchar_t*)name,videofilterlist[videofilterselected].displayname, - strlen(videofilterlist[videofilterselected].displayname)+1); - ULONG eater; - if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK) - { - if (moni->BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) - { - IAMDecoderCaps* desccaps=NULL; - if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK) - { - DWORD caps; - desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps); - if (caps == DECODER_CAP_SUPPORTED) - { - videofilterlist[videofilterselected].vmr9tested = true; - videofilterlist[videofilterselected].vmr9 = true; - } - else - { - videofilterlist[videofilterselected].vmr9tested = true; - videofilterlist[videofilterselected].vmr9 = false; - Log::getInstance()->log("VideoWin", Log::WARN ,"Filter does not support VMR9, but is selected, manual selection!"); - } - } - moni->Release(); - delete [] name; - bindctx->Release(); - return curfilter; - } - moni->Release(); - } - bindctx->Release(); - delete [] name; - return NULL; - } - return NULL; - -} - -IBaseFilter *VideoWin::getVideoH264Filter() -{ - IBaseFilter *curfilter= NULL; - if (videoH264filterselected == -1) - { - int i; - for (i = 0;i log("VideoWin", Log::DEBUG ,"Creating filter: %s",videoH264filterlist[i].friendlyname); - - - - if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK) - { - if (moni->BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) - { - IAMDecoderCaps* desccaps=NULL; - if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK) - { - DWORD caps; - desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps); - if (caps == DECODER_CAP_SUPPORTED) - { - videoH264filterlist[i].vmr9tested = true; - videoH264filterlist[i].vmr9 = true; - videoH264filterselected = i; - } - else - { - videoH264filterlist[i].vmr9tested = true; - videoH264filterlist[i].vmr9 = false; - - curfilter->Release(); - curfilter=NULL; - } - } - desccaps->Release(); - } - moni->Release(); - } - delete [] name; - bindctx->Release(); - } - if (videoH264filterlist[i].vmr9) break; - - } - if (curfilter != NULL) - { - VDR *vdr=VDR::getInstance(); - if (vdr != NULL) - { - vdr->configSave("DirectShow","VideoH264Filter", - videoH264filterlist[videoH264filterselected].displayname); - } - return curfilter; - } - } - else - { - IBindCtx *bindctx=NULL; - if (CreateBindCtx(0,&bindctx)!=S_OK) return NULL; - IMoniker * moni=NULL; - LPCOLESTR name=new WCHAR[strlen(videoH264filterlist[videoH264filterselected].displayname)+1]; - mbstowcs((wchar_t*)name,videoH264filterlist[videoH264filterselected].displayname, - strlen(videoH264filterlist[videoH264filterselected].displayname)+1); - ULONG eater; - Log::getInstance()->log("VideoWin", Log::DEBUG ,"Creating filter: %s",videoH264filterlist[videoH264filterselected].friendlyname); - if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK) - { - if (moni->BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) - { - IAMDecoderCaps* desccaps=NULL; - if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK) - { - DWORD caps; - desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps); - if (caps == DECODER_CAP_SUPPORTED) - { - videoH264filterlist[videoH264filterselected].vmr9tested = true; - videoH264filterlist[videoH264filterselected].vmr9 = true; - } - else - { - videoH264filterlist[videoH264filterselected].vmr9tested = true; - videoH264filterlist[videoH264filterselected].vmr9 = false; - Log::getInstance()->log("VideoWin", Log::WARN ,"Filter does not support VMR9, but is selected, manual selection!"); - } - } - moni->Release(); - delete [] name; - bindctx->Release(); - return curfilter; - } - moni->Release(); - } - bindctx->Release(); - delete [] name; - return NULL; - } - return NULL; - -} - - -#ifdef DS_DEBUG // This stuff would not included in vomp due to lincemse restrcitions -#include "dshelper.h" -#endif - -#define DO_VIDEO - -int VideoWin::play() -{ - if (!initted) return 0; - return 1; -} - -bool VideoWin::addOptionPagesToWTB(WTabBar *wtb) -{ - Boxx *box=new WWinVideoFilter(); - wtb->addTab(tr("Video Filter"), box); - box=new WWinVideoH264Filter(); - wtb->addTab(tr("H264 Filter"), box); - return true; -} - -const VideoFilterDescList *VideoWin::getVideoFilterList(int &selected) -{ - selected=videofilterselected; - return &videofilterlist; -} - -const VideoFilterDescList *VideoWin::getVideoH264FilterList(int &selected) -{ - selected=videoH264filterselected; - return &videoH264filterlist; -} - -bool VideoWin::selectVideoFilter(int filter) -{ - IBindCtx *bindctx=NULL; - if (CreateBindCtx(0,&bindctx)!=S_OK) return NULL; - IMoniker * moni=NULL; - LPCOLESTR name=new WCHAR[strlen(videofilterlist[filter].displayname)+1]; - mbstowcs((wchar_t*)name,videofilterlist[filter].displayname, - strlen(videofilterlist[filter].displayname)+1); - ULONG eater; - bool success=false; - if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK) - { - IBaseFilter* curfilter=NULL; - if (moni->BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) - { - IAMDecoderCaps* desccaps=NULL; - if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK) - { - DWORD caps; - HRESULT hres=desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps); - if (caps == DECODER_CAP_SUPPORTED) - { - videofilterlist[filter].vmr9tested = true; - videofilterlist[filter].vmr9 = true; - success=true; - } - else - { - videofilterlist[filter].vmr9tested = true; - videofilterlist[filter].vmr9 = false; - success=false; - } - desccaps->Release(); - } else { - videofilterlist[filter].vmr9tested = true; - videofilterlist[filter].vmr9 = false; - success=false; - } - - curfilter->Release(); - - } - moni->Release(); - } - bindctx->Release(); - delete [] name; - if (success || true) - { - videofilterselected=filter; - return true; - } - else - { - return false; - } -} - -bool VideoWin::selectVideoH264Filter(int filter) -{ - IBindCtx *bindctx=NULL; - if (CreateBindCtx(0,&bindctx)!=S_OK) return NULL; - IMoniker * moni=NULL; - LPCOLESTR name=new WCHAR[strlen(videoH264filterlist[filter].displayname)+1]; - mbstowcs((wchar_t*)name,videoH264filterlist[filter].displayname, - strlen(videoH264filterlist[filter].displayname)+1); - ULONG eater; - bool success=false; - if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK) - { - IBaseFilter* curfilter=NULL; - if (moni->BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) - { - IAMDecoderCaps* desccaps=NULL; - if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK) - { - DWORD caps; - HRESULT hres=desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps); - if (caps == DECODER_CAP_SUPPORTED) - { - videoH264filterlist[filter].vmr9tested = true; - videoH264filterlist[filter].vmr9 = true; - success=true; - } - else - { - videoH264filterlist[filter].vmr9tested = true; - videoH264filterlist[filter].vmr9 = false; - success=false; - } - desccaps->Release(); - } else { - videoH264filterlist[filter].vmr9tested = true; - videoH264filterlist[filter].vmr9 = false; - success=false; - } - - curfilter->Release(); - - } - moni->Release(); - } - bindctx->Release(); - delete [] name; - if (success || true) - { - videoH264filterselected=filter; - return true; - } - else - { - return false; - } -} - -int VideoWin::dsInitVideoFilter() -{ - #ifdef DO_VIDEO - HRESULT hres; - if (videoon) { - //We alloc the vmr9 as next step - if (currentpresenter==VMR9) - { - Log::getInstance()->log("VideoWin", Log::INFO ,"VMR9 Videopresenter selected!"); - if (hres=CoCreateInstance(CLSID_VideoMixingRenderer9,0, - CLSCTX_INPROC_SERVER,IID_IBaseFilter,(void**) &dsrenderer)!=S_OK) - { - Log::getInstance()->log("VideoWin", Log::WARN ,"Failed creating VMR9 renderer!"); - ReleaseMutex(filtermutex); - CleanupDS(); - } - /*VMR 9 stuff**/ - if (hres=dsgraphbuilder->AddFilter(dsrenderer,L"VMR9")!=S_OK) - { - ReleaseMutex(filtermutex); - CleanupDS(); - Log::getInstance()->log("VideoWin", Log::WARN ,"Failed adding VMR9 renderer!"); - return 0; - } - IVMRFilterConfig9* vmrfilconfig; - if (dsrenderer->QueryInterface(IID_IVMRFilterConfig9,(void**)&vmrfilconfig)!=S_OK) - { - ReleaseMutex(filtermutex); - CleanupDS(); - Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting VMR9 Filterconfig interface!"); - return 0; - } - if (vmrdeinterlacing!=0) vmrfilconfig->SetNumberOfStreams(1);//Enter Mixing Mode - vmrfilconfig->SetRenderingMode(VMR9Mode_Renderless); - vmrfilconfig->Release(); - if (dsrenderer->QueryInterface(IID_IVMRSurfaceAllocatorNotify9, - (void**)& dsvmrsurfnotify) != S_OK) - { - ReleaseMutex(filtermutex); - CleanupDS(); - Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting VMR9 Surface Allocator interface!"); - return 0; - } - allocatorvmr=new DsAllocator(); - dsvmrsurfnotify->AdviseSurfaceAllocator(NULL,allocatorvmr); - allocatorvmr->AdviseNotify(dsvmrsurfnotify); - - IVMRDeinterlaceControl9* deintctrl; - if (dsrenderer->QueryInterface(IID_IVMRDeinterlaceControl9,(void**)&deintctrl)!=S_OK) - { - ReleaseMutex(filtermutex); - CleanupDS(); - Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting VMR9 Deinterlace control!"); - return 0; - } - /*turnoff*/ - switch (vmrdeinterlacing) - { - case 1: //No Deinterlasing - deintctrl->SetDeinterlaceMode(0xFFFFFFFF,(LPGUID)&GUID_NULL);//Turn Off - break; - case 2: //Best - deintctrl->SetDeinterlacePrefs(DeinterlacePref_NextBest);//Choose Next Best - break; - case 3: //Bob - deintctrl->SetDeinterlacePrefs(DeinterlacePref_BOB);//Choose NBob - break; - case 4: //Weave - deintctrl->SetDeinterlacePrefs(DeinterlacePref_Weave);//Choose Weave - break; - }; - deintctrl->Release(); - /*VMR 9 stuff end */ - } - else if (currentpresenter==EVR) - { - Log::getInstance()->log("VideoWin", Log::INFO ,"EVR Videopresenter selected!"); - if (hres=CoCreateInstance(CLSID_EnhancedVideoRenderer,0, - CLSCTX_INPROC_SERVER,IID_IBaseFilter,(void**) &dsrenderer)!=S_OK) - { - Log::getInstance()->log("VideoWin", Log::WARN ,"Failed creating EVR renderer!"); - ReleaseMutex(filtermutex); - CleanupDS(); - } - /*EVR stuff**/ - if (hres=dsgraphbuilder->AddFilter(dsrenderer,L"EVR")!=S_OK) - { - ReleaseMutex(filtermutex); - CleanupDS(); - Log::getInstance()->log("VideoWin", Log::WARN ,"Failed adding EVR renderer!"); - return 0; - } - - - IMFGetService *evr_services; - if (dsrenderer->QueryInterface(IID_IMFGetService,(void**)&evr_services)!=S_OK) - { - ReleaseMutex(filtermutex); - CleanupDS(); - Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting EVR IMFGetServices interface!"); - return 0; - } - - IMFVideoDisplayControl* mfvideodisplaycontrol; - if (evr_services->GetService(MR_VIDEO_RENDER_SERVICE,IID_IMFVideoDisplayControl,(void**)&mfvideodisplaycontrol)!=S_OK) - { - ReleaseMutex(filtermutex); - CleanupDS(); - Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting EVR IMFVideoDisplayControl interface!"); - return 0; - } - - evr_services->Release(); - mfvideodisplaycontrol->SetVideoWindow(((OsdWin*) Osd::getInstance())->getWindow()); - //RECT client; - //GetClientRect(((OsdWin*) Osd::getInstance())->getWindow(), &client); - //mfvideodisplaycontrol->SetVideoPosition(NULL,&client); - - mfvideodisplaycontrol->Release(); - - - /// if (vmrdeinterlacing!=0) vmrfilconfig->SetNumberOfStreams(1);//Enter Mixing Mode //always the case for evr! - - IMFVideoRenderer *mfvideorenderer; - if (dsrenderer->QueryInterface(IID_IMFVideoRenderer,(void**)&mfvideorenderer)!=S_OK) - { - ReleaseMutex(filtermutex); - CleanupDS(); - Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting EVR IMFVideoRenderer interface!"); - return 0; - } - - allocatorvmr=new DsAllocator(); - HRESULT hres=mfvideorenderer->InitializeRenderer(NULL,allocatorvmr); - - mfvideorenderer->Release(); - //How should I do this in EVR? - /* IVMRDeinterlaceControl9* deintctrl; - if (dsrenderer->QueryInterface(IID_IVMRDeinterlaceControl9,(void**)&deintctrl)!=S_OK) - { - ReleaseMutex(filtermutex); - CleanupDS(); - Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting VMR9 Deinterlace control!"); - return 0; - } - /*turnoff* - switch (vmrdeinterlacing) - { - case 1: //No Deinterlasing - deintctrl->SetDeinterlaceMode(0xFFFFFFFF,(LPGUID)&GUID_NULL);//Turn Off - break; - case 2: //Best - deintctrl->SetDeinterlacePrefs(DeinterlacePref_NextBest);//Choose Next Best - break; - case 3: //Bob - deintctrl->SetDeinterlacePrefs(DeinterlacePref_BOB);//Choose NBob - break; - case 4: //Weave - deintctrl->SetDeinterlacePrefs(DeinterlacePref_Weave);//Choose Weave - break; - }; - deintctrl->Release();*/ - /*EVR stuff end */ - } else { - Log::getInstance()->log("VideoWin", Log::ERR ,"No videopresenter selected! Please post on the forum!"); - return -1; - } - IFilterGraph2*fg2=NULL; - if (dsgraphbuilder->QueryInterface(IID_IFilterGraph2,(void**)&fg2)!= S_OK) - { - Log::getInstance()->log("VideoWin", Log::WARN , "Failed querying for FilterGraph2 Interface!"); - ReleaseMutex(filtermutex); - CleanupDS(); - return 0; - } -/*#ifndef NEW_DS_MECHANISMENS - - if (hres=fg2->RenderEx((IPin*)sourcefilter->GetVideoPin()/*video*, - AM_RENDEREX_RENDERTOEXISTINGRENDERERS,NULL) != S_OK) - { - Log::getInstance()->log("VideoWin", Log::WARN , "Failed rendering Video!"); - fg2->Release(); - ReleaseMutex(filtermutex); - CleanupDS(); - return 0; - } - -#else*/ - IBaseFilter*videofilter; - if (h264) - { - Log::getInstance()->log("VideoWin", Log::DEBUG ,"Entering h264 playback..."); - videofilter=getVideoH264Filter(); - } - else - { - Log::getInstance()->log("VideoWin", Log::DEBUG ,"Entering MPEG2 playback..."); - videofilter=getVideoFilter(); - } - if (hres=dsgraphbuilder->AddFilter(videofilter,NULL) != S_OK) - { - Log::getInstance()->log("VideoWin", Log::WARN , "Failed adding Video Filter!"); - ReleaseMutex(filtermutex); - CleanupDS(); - return 0; - } - IEnumPins *pinenum=NULL; - bool error=false; - - mptype_video_detail vid_details; - Demuxer* demux=Demuxer::getInstance(); - vid_details.width=demux->getHorizontalSize(); - vid_details.height=demux->getVerticalSize(); - - if (h264) - { - if (vid_details.width!=0 && vid_details.height!=0) - { - sourcefilter->GetVideoPin()->SetPinMode(MPTYPE_VIDEO_H264,&vid_details); - } - else - { - sourcefilter->GetVideoPin()->SetPinMode(MPTYPE_VIDEO_H264,NULL); - } - - } - else - { - if (vid_details.width!=0 && vid_details.height!=0) - { - sourcefilter->GetVideoPin()->SetPinMode(MPTYPE_VIDEO_MPEG2,&vid_details); - } - else - { - sourcefilter->GetVideoPin()->SetPinMode(MPTYPE_VIDEO_MPEG2,NULL); - } - } - if (videofilter->EnumPins(&pinenum) == S_OK) - { - IPin *current=NULL; - ULONG fetch=0; - bool firststep=false; - - while (pinenum->Next(1,¤t,&fetch)==S_OK) - { - PIN_DIRECTION dir; - if (current->QueryDirection(&dir)==S_OK) - { - if (dir == PINDIR_INPUT) - { - if (sourcefilter->GetVideoPin()->Connect(current,NULL)==S_OK) - { - current->Release(); - firststep=true; - break; - } - } - } - current->Release(); - } - if (firststep==false) - { - Log::getInstance()->log("VideoWin", Log::WARN , "Video Filter has no suitable input!"); - videofilter->Release(); - ReleaseMutex(filtermutex); - CleanupDS(); - return 0; - } - bool secondstep=false; - pinenum->Reset(); - while (pinenum->Next(1,¤t,&fetch)==S_OK) - { - PIN_DIRECTION dir; - if (current->QueryDirection(&dir)==S_OK) - { - if (dir == PINDIR_OUTPUT) - { - - if (fg2->RenderEx((IPin*)current/*video*/, - AM_RENDEREX_RENDERTOEXISTINGRENDERERS,NULL) ==S_OK) - { - current->Release(); - secondstep=true; - break; - } - } - } - current->Release(); - } - if (secondstep==false) - { - Log::getInstance()->log("VideoWin", Log::WARN , "Video Filter has no suitable output!"); - videofilter->Release(); - ReleaseMutex(filtermutex); - CleanupDS(); - return 0; - } - - videofilter->Release(); - pinenum->Release(); - - } - - - - fg2->Release(); - return 1; - } -#endif - return 1; -} - -int VideoWin::setAudioStreamType(UCHAR type) -{ - aud_type=type; - if (!initted) return 0; - return 1; -} - -int VideoWin::dsplay() -{ - if (!initted) return 0; - CleanupDS(); - - //Build filter graph - HRESULT hres; - //So this is the real code, this prevents the feeder from calling noexisting objects! - WaitForSingleObject(filtermutex,INFINITE); - if (hres=CoCreateInstance(CLSID_FilterGraph,NULL,CLSCTX_INPROC_SERVER, - IID_IGraphBuilder,(void**)&dsgraphbuilder) != S_OK) - { - ReleaseMutex(filtermutex); - return 0; - } - #ifdef DS_DEBUG - AddToRot(dsgraphbuilder,&graphidentifier); - #endif - - firstsynched=false; - if (aud_type==Audio::MP3) { - lastaudiomode=MPTYPE_MPEG_AUDIO_LAYER3; - } else { - lastaudiomode=MPTYPE_MPEG_AUDIO; - } - //lastaudiomode=MPTYPE_AC3; - sourcefilter=new DsSourceFilter(); //Creating our Source filter for pushing Data - // to DirectShow - if (hres=dsgraphbuilder->AddFilter(sourcefilter,L"Vomp Win Source Filter") != S_OK) - { - Log::getInstance()->log("VideoWin", Log::WARN , "Failed adding Vomp Source Filter!"); - ReleaseMutex(filtermutex); - CleanupDS(); - return 0; - } - sourcefilter->GetAudioPin()->SetPinMode(lastaudiomode); - /*if (hres=dsgraphbuilder->Render((IPin*)sourcefilter->GetAudioPin()/*audio*)!=S_OK) - { - Log::getInstance()->log("VideoWin", Log::WARN , "Failed rendering audio!"); - ReleaseMutex(filtermutex); - CleanupDS(); - return 0; - }*/ - if (((AudioWin*)Audio::getInstance())->dsInitAudioFilter(dsgraphbuilder)==0) - { - Log::getInstance()->log("VideoWin", Log::WARN , "Failed rendering audio!"); - ReleaseMutex(filtermutex); - CleanupDS(); - return 0; - } - - - if (dsInitVideoFilter()==0) - { - return 0; - } - - if (hres=CoCreateInstance(CLSID_SystemClock,NULL,CLSCTX_INPROC_SERVER, - IID_IReferenceClock,(void**)&dsrefclock)!=S_OK) - { - return 0; - } - dsgraphbuilder->QueryInterface(IID_IMediaFilter,(void **) &dsmediafilter); - HRESULT hresdeb = dsmediafilter->SetSyncSource(dsrefclock); - - dsgraphbuilder->QueryInterface(IID_IMediaControl,(void **) &dsmediacontrol); - dsgraphbuilder->QueryInterface(IID_IBasicAudio,(void **) &dsbasicaudio); - if (dsbasicaudio) - dsbasicaudio->put_Volume(audiovolume); - dsinited=true; - //MILLISLEEP(100); - - hresdeb=dsmediacontrol->Run(); - iframemode=false;//exit iframe mode - ReleaseMutex(filtermutex); - return 1; -} - -int VideoWin::EnterIframePlayback() -{ - if (!initted) return 0; - CleanupDS(); - //So this is the real code, this prevents the feeder from calling noexisting objects! - WaitForSingleObject(filtermutex,INFINITE); - iframemode=true;//enter iframe mode - //Build filter graph - HRESULT hres; - if (hres=CoCreateInstance(CLSID_FilterGraph,NULL,CLSCTX_INPROC_SERVER, - IID_IGraphBuilder,(void**)&dsgraphbuilder)!=S_OK) { - ReleaseMutex(filtermutex); - return 0; - } -#ifdef DS_DEBUG - AddToRot(dsgraphbuilder,&graphidentifier); -#endif - - //firstsynched=false; - sourcefilter=new DsSourceFilter(); //Creating our Source filter for pushing Data - // to DirectShow - if (hres=dsgraphbuilder->AddFilter(sourcefilter,L"Vomp Win Source Filter")!=S_OK) { - Log::getInstance()->log("VideoWin", Log::WARN , "Failed adding Vomp Source Filter!"); - ReleaseMutex(filtermutex); - CleanupDS(); - return 0; - } -#ifdef DO_VIDEO - if (videoon) { - dsInitVideoFilter(); - } -#endif -/* if (hres=CoCreateInstance(CLSID_SystemClock,NULL,CLSCTX_INPROC_SERVER, - IID_IReferenceClock,(void**)&dsrefclock)!=S_OK) { - return 0; - }*/ - - dsgraphbuilder->QueryInterface(IID_IMediaFilter,(void **) &dsmediafilter); - dsmediafilter->SetSyncSource(/*dsrefclock*/NULL); //Run as fast as you can! - - dsgraphbuilder->QueryInterface(IID_IMediaControl,(void **) &dsmediacontrol); - dsgraphbuilder->QueryInterface(IID_IBasicAudio,(void **) &dsbasicaudio); - dsinited=true; - - - dsmediacontrol->Run(); - ReleaseMutex(filtermutex); - return 1; - -} - -int VideoWin::dsstop() -{ - if (!initted) return 0; - - CleanupDS(); - - - return 1; -} - -int VideoWin::stop() -{ - if (!initted) return 0; - - - return 1; -} - -int VideoWin::reset() -{ - if (!initted) return 0; - - - return 1; -} - -int VideoWin::dsreset() -{ - if (!initted) return 0; - videoposx=0; - videoposy=0; - iframemode=false;//exit iframe mode - CleanupDS(); - - return 1; -} - -int VideoWin::dspause() -{ - if (!initted) return 0; - WaitForSingleObject(filtermutex,INFINITE); - if (dsmediacontrol) dsmediacontrol->Pause(); - ReleaseMutex(filtermutex); - return 1; -} - -int VideoWin::pause() -{ - if (!initted) return 0; - - return 1; -} - -int VideoWin::unPause() // FIXME get rid - same as play!! -{//No on windows this is not the same, I don't get rid of! - if (!initted) return 0; - return 1; -} - -int VideoWin::dsunPause() // FIXME get rid - same as play!! -{//No on windows this is not the same, I don't get rid of! - if (!initted) return 0; - WaitForSingleObject(filtermutex,INFINITE); - if (dsmediacontrol) dsmediacontrol->Run(); - ReleaseMutex(filtermutex); - - return 1; -} - -int VideoWin::fastForward() -{ - if (!initted) return 0; - - return 1; -} - -int VideoWin::unFastForward() -{ - if (!initted) return 0; - - return 1; -} - -int VideoWin::attachFrameBuffer() -{ - if (!initted) return 0; - return 1; -} - -int VideoWin::blank(void) -{ - ((OsdWin*)Osd::getInstance())->Blank(); - return 1; -} - -ULLONG VideoWin::getCurrentTimestamp() -{ - REFERENCE_TIME startoffset; - REFERENCE_TIME ncr_time; - if (iframemode) return 0; //Not in iframe mode! - if (!dsrefclock || !sourcefilter) return 0; - FILTER_STATE state; - sourcefilter->GetState(10,&state); - - if (state==State_Running) dsrefclock->GetTime(&cr_time); - ncr_time=cr_time; - startoffset=sourcefilter->getStartOffset(); - if (startoffset==0) return 0; - ncr_time-=startoffset; - ncr_time-=lastreftimeRT; - /* ULLONG result=frameNumberToTimecode( - VDR::getInstance()->frameNumberFromPosition(lastreftimeBYTE));*/ - long long result=lastreftimePTS; - result+=(ULLONG)(ncr_time/10000LL*90LL); - if (result<0) result=(1LL << 33)-result; - return result; - -} -/* //to beremoved -ULONG VideoWin::timecodeToFrameNumber(ULLONG timecode) -{ - if (format == PAL) return (ULONG)(((double)timecode / (double)90000) * (double)25); - else return (ULONG)(((double)timecode / (double)90000) * (double)30); -} - -ULLONG VideoWin::frameNumberToTimecode(ULONG framenumber) -{ - if (format == PAL) return (ULLONG)(((double)framenumber * (double)90000) / (double)25); - else return (ULLONG)(((double)framenumber * (double)90000) / (double)30); -} -*/ -void VideoWin::CleanupDS() -{ - WaitForSingleObject(filtermutex,INFINITE); - dsinited=false; - if (dsmediacontrol)dsmediacontrol->Stop(); - if (cur_audio_media_sample) { - cur_audio_media_sample->Release(); - cur_audio_media_sample=NULL; - } - if (cur_video_media_sample) { - cur_video_media_sample->Release(); - cur_video_media_sample=NULL; - } - if (dsbasicaudio) { - dsbasicaudio->Release(); - dsbasicaudio=NULL; - } - if (dsvmrsurfnotify) { - dsvmrsurfnotify->Release(); - dsvmrsurfnotify=NULL; - } - if (dsrenderer) { - dsrenderer->Release(); - dsrenderer=NULL; - } - - if (allocatorvmr) { - allocatorvmr->Release(); - allocatorvmr=NULL; - } - - if (dsrefclock) { - dsrefclock->Release(); - dsrefclock=NULL; - } - if (dsmediafilter) { - dsmediafilter->Release(); - dsmediafilter=NULL; - } - - - - if (dsmediacontrol) { - dsmediacontrol->Stop(); - dsmediacontrol->Release(); - dsmediacontrol=NULL; - } - if (dsgraphbuilder){ -#ifdef DS_DEBUG - RemoveFromRot(graphidentifier); -#endif - dsgraphbuilder->Release(); - dsgraphbuilder=NULL; - - sourcefilter=NULL; //The Graph Builder destroys our SourceFilter - } - ReleaseMutex(filtermutex); - -} - -void VideoWin::PrepareMediaSample(const MediaPacketList& mplist, - UINT samplepos) -{ - mediapacket = mplist.front(); -} - -UINT VideoWin::DeliverMediaSample(UCHAR* buffer, UINT *samplepos) -{ - DeliverMediaPacket(mediapacket, buffer, samplepos); - if (*samplepos == mediapacket.length) { - *samplepos = 0; - return 1; - } - else return 0; -} - -UINT VideoWin::DeliverMediaPacket(MediaPacket packet, - const UCHAR* buffer, - UINT *samplepos) -{ - - /*First Check, if we have an audio sample*/ - if (!isdsinited()) return 0; - if (packet.type == MPTYPE_VIDEO_H264) - { - h264=true; - } - else - { - h264=false; - } - -#ifdef DO_VIDEO - if (!videoon) { - *samplepos+=packet.length; - MILLISLEEP(0); //yet not implemented//bad idea - return packet.length; - } - /*First Check, if we have an audio sample*/ - if (iframemode) { - //samplepos=0; - MILLISLEEP(10); - return 0; //Not in iframe mode! - } - IMediaSample* ms=NULL; - REFERENCE_TIME reftime1=0; - REFERENCE_TIME reftime2=0; - - UINT headerstrip=0; - if (packet.disconti) { - firstsynched=false; - DeliverVideoMediaSample(); - } - - - - /*Inspect PES-Header */ - - if (*samplepos==0) {//stripheader - headerstrip=buffer[packet.pos_buffer+8]+9/*is this right*/; - *samplepos+=headerstrip; - if ( packet.synched ) { - DeliverVideoMediaSample();//write out old data - /* if (packet.presentation_time<0) { //Preroll? - *samplepos=packet.length;//if we have not processed at least one - return packet.length;//synched packet ignore it! - }*/ - - reftime1=packet.presentation_time; - reftime2=reftime1+1; - firstsynched=true; - } else { - if (!firstsynched) {// - *samplepos=packet.length;//if we have not processed at least one - return packet.length;//synched packet ignore it! - } - } - } - BYTE *ms_buf; - UINT ms_length; - UINT ms_pos; - UINT haveToCopy; - - if (!getCurrentVideoMediaSample(&ms) || ms==NULL) {// get the current sample - //samplepos=0; - //MessageBox(0,"da isser","hei",0); - //MILLISLEEP(1); - return 0; - } - ms_pos=ms->GetActualDataLength(); - ms_length=ms->GetSize(); - haveToCopy=min(ms_length-ms_pos,packet.length-*samplepos); - if ((ms_length-ms_pos)<1 ) { - DeliverVideoMediaSample(); //we are full! - if (!getCurrentVideoMediaSample(&ms) || ms==NULL) {// get the current sample - //samplepos=0; - //MessageBox(0,"da isser","hei",0); - //MILLISLEEP(10); - return 0; - } - ms_pos=ms->GetActualDataLength(); - ms_length=ms->GetSize(); - haveToCopy=min(ms_length-ms_pos,packet.length-*samplepos); - } - ms->GetPointer(&ms_buf); - - - if (ms_pos==0) {//will only be changed on first packet - if (packet.disconti) { - ms->SetDiscontinuity(TRUE); - } else { - ms->SetDiscontinuity(FALSE); - } - if (packet.synched) { - ms->SetSyncPoint(TRUE); - ms->SetTime(&reftime1,&reftime2); - //Log::getInstance()->log("VideoWin", Log::DEBUG , "Setted videotime to %lld %lld",reftime1,reftime2); - //Log::getInstance()->log("VideoWin", Log::DEBUG , "Packet pts %lld dts %lld",packet.pts,packet.dts); - //ms->SetTime(NULL,NULL); - ms->SetMediaTime(NULL, NULL); - if (reftime1<0) ms->SetPreroll(TRUE); - else ms->SetPreroll(FALSE); - /*Timecode handling*/ - lastreftimeRT=reftime1; - lastreftimePTS=packet.pts; - - } - else - { - ms->SetSyncPoint(FALSE); - ms->SetTime(NULL,NULL); - ms->SetMediaTime(NULL, NULL); - ms->SetPreroll(FALSE); - - // ms->SetSyncPoint(TRUE); - } - } - - - - memcpy(ms_buf+ms_pos,buffer+packet.pos_buffer+*samplepos,haveToCopy); - ms->SetActualDataLength(haveToCopy+ms_pos); - - *samplepos+=haveToCopy; - - return haveToCopy+headerstrip; - -#else - - *samplepos+=packet.length; - MILLISLEEP(0); //yet not implemented//bad idea - return packet.length; -#endif -} - -int VideoWin::getCurrentAudioMediaSample(IMediaSample** ms) -{ - //WaitForSingleObject(filtermutex,INFINITE); - if (!sourcefilter){ - // ReleaseMutex(filtermutex); - return 0; - } - if (cur_audio_media_sample) { - *ms=cur_audio_media_sample;//already open - return 1; - } - if (!sourcefilter->getCurrentAudioMediaSample(ms)) { - // ReleaseMutex(filtermutex); - } - if (*ms) (*ms)->SetActualDataLength(0); - cur_audio_media_sample=*ms; - //Don't release the mutex before deliver - return 1; -} - -int VideoWin::getCurrentVideoMediaSample(IMediaSample** ms) -{ - //WaitForSingleObject(filtermutex,INFINITE); - if (!sourcefilter){ - // ReleaseMutex(filtermutex); - return 0; - } - if (cur_video_media_sample) { - *ms=cur_video_media_sample;//already open - return 1; - } - if (!sourcefilter->getCurrentVideoMediaSample(ms)) { - // ReleaseMutex(filtermutex); - } - if (*ms) (*ms)->SetActualDataLength(0); - - cur_video_media_sample=*ms; - //Don't release the mutex before deliver - return 1; -} - -int VideoWin::DeliverAudioMediaSample(){ - if (cur_audio_media_sample) { - sourcefilter->DeliverAudioMediaSample(cur_audio_media_sample); - cur_audio_media_sample=NULL; - } - //ReleaseMutex(filtermutex); - return 1; -} - -int VideoWin::DeliverVideoMediaSample(){ - if (cur_video_media_sample) { - sourcefilter->DeliverVideoMediaSample(cur_video_media_sample); - cur_video_media_sample=NULL; - } - //ReleaseMutex(filtermutex); - return 1; -} - -long long VideoWin::SetStartOffset(long long curreftime, bool *rsync) -{ - *rsync=false; - if (offsetnotset) { - startoffset=curreftime;//offset is set for audio - offsetnotset=false; - offsetvideonotset=false; - - - } else { - if (offsetvideonotset) { - offsetvideonotset=false; - *rsync=true; - } else { - if ( (curreftime-lastrefvideotime)>10000000LL - || (curreftime-lastrefvideotime)<-10000000LL) {//if pts jumps to big resync - startoffset+=curreftime-lastrefvideotime; - lastrefaudiotime+=curreftime-lastrefvideotime; - //*rsync=true; - offsetaudionotset=true; - - } - } - - } - - lastrefvideotime=curreftime; - - return startoffset; - -} - -long long VideoWin::SetStartAudioOffset(long long curreftime, bool *rsync) -{ - *rsync=false; - if (offsetnotset) { - startoffset=curreftime; - offsetnotset=false; - offsetaudionotset=false; - }else { - if (offsetaudionotset) { - offsetaudionotset=false; - *rsync=true; - } else { - if ( (curreftime-lastrefaudiotime)>10000000LL - || (curreftime-lastrefaudiotime)<-10000000LL) {//if pts jumps to big resync - startoffset+=curreftime-lastrefaudiotime; - lastrefvideotime+=curreftime-lastrefaudiotime; - //*rsync=true; - offsetvideonotset=true; - - } - } - - } - lastrefaudiotime=curreftime; - return startoffset; - -} -void VideoWin::ResetTimeOffsets() { - offsetnotset=true; //called from demuxer - offsetvideonotset=true; - offsetaudionotset=true; - startoffset=0; - lastrefaudiotime=0; - lastrefvideotime=0; - lastreftimeRT=0; - lastreftimePTS=0; - - -} - -void VideoWin::SetAudioVolume(long volume) -{ - audiovolume=volume; - if (dsbasicaudio) dsbasicaudio->put_Volume(volume); -} - -void VideoWin::displayIFrame(const UCHAR* buffer, UINT length) -{ - if (!iframemode) EnterIframePlayback(); - if (!isdsinited()) return ; - -#ifdef DO_VIDEO - IMediaSample* ms=NULL; - REFERENCE_TIME reftime1=0; - REFERENCE_TIME reftime2=0; - if (!videoon) return; - if (!getCurrentVideoMediaSample(&ms) || ms==NULL) {// get the current sample - MILLISLEEP(10); - return ; - } - BYTE *ms_buf; - DWORD ms_length; - ms->GetPointer(&ms_buf); - ms_length=ms->GetSize(); - - /*First Check, if we have an video sample*/ - DWORD read_pos = 0, write_pos = 0; - DWORD pattern, packet_length; - DWORD headerstrip=0; - bool first=true; - if (length < 4) return ; - //Now we strip the pes header - pattern = (buffer[0] << 16) | (buffer[1] << 8) | (buffer[2]); - while (read_pos + 7 <= length) - { - pattern = ((pattern << 8) & 0xFFFFFFFF) | buffer[read_pos+3]; - if (pattern < 0x000001E0 || pattern > 0x000001EF) { - read_pos++; - continue; - } - else - { - headerstrip=buffer[read_pos+8]+9/*is this right*/; - packet_length = ((buffer[read_pos+4] << 8) | (buffer[read_pos+5])) + 6; - if (read_pos + packet_length > length) - read_pos = length; - else - { - if ( (headerstrip < packet_length) && - (write_pos+packet_length-headerstrip)>ms_length) { - if (first) { - ms->SetSyncPoint(TRUE); - ms->SetDiscontinuity(TRUE); - first=false; - } else ms->SetSyncPoint(FALSE); - ms->SetTime(NULL,NULL); - ms->SetMediaTime(NULL, NULL); - ms->SetActualDataLength(write_pos); - DeliverVideoMediaSample(); - - if (!getCurrentVideoMediaSample(&ms) || ms==NULL) {// get the current sample - MILLISLEEP(10); - return ; - } - write_pos=0; - ms_length=ms->GetSize(); - ms->GetPointer(&ms_buf); - } - if (packet_length>headerstrip) { - memcpy(ms_buf+write_pos, buffer+read_pos+headerstrip, packet_length-headerstrip); - write_pos += packet_length-headerstrip; - } - read_pos += packet_length; - - pattern = (buffer[read_pos] << 16) | (buffer[read_pos+1] << 8) - | (buffer[read_pos+2]); - } - } - } - - if (first) {ms->SetSyncPoint(TRUE); - ms->SetDiscontinuity(TRUE); - first=false;} - else ms->SetSyncPoint(FALSE); - ms->SetTime(NULL,NULL); - ms->SetMediaTime(NULL, NULL); - ms->SetActualDataLength(write_pos); - DeliverVideoMediaSample(); - -#else - - // *samplepos+=packet.length; - MILLISLEEP(0); //yet not implemented//bad idea - return ; -#endif -} - -bool VideoWin::supportsAc3(){ - if (sourcefilter != NULL) { - return sourcefilter->supportsAc3(); - } else { - return false; - } -} - -bool VideoWin::supportsh264() -{ - if (videoH264filterlist.size()>0) return true; - else return false; -} - - -bool VideoWin::changeAType(int type,IMediaSample* ms){ - if (sourcefilter!= NULL) { - lastaudiomode=type; - return sourcefilter->changeAType(type,ms); - } - else - { - return false; - } -} - -#ifdef DEV -int VideoWin::test() -{ - return 0; -} - -int VideoWin::test2() -{ - return 0; -} -#endif - - - - +/* + 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. +*/ + + + +#include "videowin.h" +#include "log.h" +#include "dssourcefilter.h" +#include "dsallocator.h" +#include "vdr.h" +#include "osdwin.h" +#include "audiowin.h" +#include "wwinvideofilter.h" +#include "wwinvideoh264filter.h" +#include "wtabbar.h" +#include "woptionpane.h" +#include "i18n.h" +#include "demuxer.h" + +#include +#include + +void AdjustWindow(); + + + +VideoWin::VideoWin() +{ + dsinited=false; + dsgraphbuilder=NULL; + dsmediacontrol=NULL; + dsrenderer=NULL; + dsrefclock=NULL; + dsmediafilter=NULL; + dsbasicaudio=NULL; + sourcefilter=NULL; + allocatorvmr=NULL; + cr_time=0; + lastaudiomode=MPTYPE_MPEG_AUDIO; + //lastaudiomode=MPTYPE_AC3; + dsvmrsurfnotify=NULL; + filtermutex=CreateMutex(NULL,FALSE,NULL); + offsetnotset=true; + offsetvideonotset=true; + offsetaudionotset=true; + startoffset=0; + lastrefaudiotime=0; + lastrefvideotime=0; + lastreftimeRT=0; + lastreftimePTS=0; + firstsynched=false; + cur_audio_media_sample=NULL; + cur_video_media_sample=NULL; + videoon=true; + audioon=true; + audiovolume=0; + pseudotvsize=0; + videoposx=0; + videoposy=0; + aud_type=Audio::MPEG2_PES; + iframemode=false;//We are not in Iframe mode at begining + vmrdeinterlacing=2;//Best + videofilterselected=-1; + videoH264filterselected=-1; + OSVERSIONINFO verinfo; + verinfo.dwOSVersionInfoSize=sizeof(verinfo); + GetVersionEx(&verinfo); + + if (verinfo.dwMajorVersion>=6) { + currentpresenter=EVR; + } else { + currentpresenter=VMR9; + } + videoH264dtsfix=false; + videompeg2dtsfix=false; + + + + +} + +VideoWin::~VideoWin() +{ + CleanupDS(); + CloseHandle(filtermutex); + unsigned int i; + for (i=0;ilog("VideoWin", Log::ERR , "Unable to create FilterMapper!"); + return; + } + /* Wishlist, what Mediatypes do we want */ + GUID mtypesin[]={MEDIATYPE_Video,MEDIASUBTYPE_MPEG2_VIDEO}; + IEnumMoniker *myenum; + result = filtmap->EnumMatchingFilters(&myenum,0,TRUE,MERIT_DO_NOT_USE+1, + TRUE,1,mtypesin,NULL,NULL,FALSE,TRUE,0,NULL,NULL,NULL); + if (result != S_OK) + { + filtmap->Release(); + Log::getInstance()->log("VideoWin", Log::ERR , "Unable to enum Filters!"); + return; + } + ULONG gethowmany; + IMoniker * moni; + while(myenum->Next(1,&moni,&gethowmany)==S_OK) + { + VideoFilterDesc desc; + ZeroMemory(&desc,sizeof(desc)); + + LPOLESTR string; + moni->GetDisplayName(0,0,&string); + desc.displayname=new char[wcslen(string)+1]; + wcstombs(desc.displayname,string,wcslen(string)+1); + CoTaskMemFree(string); + IPropertyBag *bag; + if (moni->BindToStorage(0,0,IID_IPropertyBag,(void**)&bag) == S_OK) + { + VARIANT vari; + VariantInit(&vari); + result = bag->Read(L"FriendlyName",&vari,NULL); + if (result == S_OK) + { + desc.friendlyname=new char[wcslen(vari.bstrVal)+1]; + wcstombs(desc.friendlyname,vari.bstrVal,wcslen(vari.bstrVal)+1); + } + VariantClear(&vari); + bag->Release(); + + } + + + videofilterlist.push_back(desc); + + + + moni->Release(); + // bctx->Release(); + } + int i; + videofilterselected=-1; + + + + myenum->Release(); + + + + filtmap->Release(); +} + +void VideoWin::initH264FilterDatabase() +{ + /* This method should determine all availiable DirectShow Filters */ + IFilterMapper2* filtmap=NULL; + HRESULT result; + result = CoCreateInstance(CLSID_FilterMapper2,NULL,CLSCTX_INPROC, + IID_IFilterMapper2,(void**)&filtmap); + if (result != S_OK) + { + Log::getInstance()->log("VideoWin", Log::ERR , "Unable to create FilterMapper!"); + return; + } + /* Wishlist, what Mediatypes do we want */ + GUID mtypesin[]={MEDIATYPE_Video,MEDIASUBTYPE_H264}; + IEnumMoniker *myenum; + result = filtmap->EnumMatchingFilters(&myenum,0,TRUE,MERIT_DO_NOT_USE+1, + TRUE,1,mtypesin,NULL,NULL,FALSE,TRUE,0,NULL,NULL,NULL); + if (result != S_OK) + { + filtmap->Release(); + Log::getInstance()->log("VideoWin", Log::ERR , "Unable to enum Filters!"); + return; + } + ULONG gethowmany; + IMoniker * moni; + while(myenum->Next(1,&moni,&gethowmany)==S_OK) + { + VideoFilterDesc desc; + ZeroMemory(&desc,sizeof(desc)); + + LPOLESTR string; + moni->GetDisplayName(0,0,&string); + desc.displayname=new char[wcslen(string)+1]; + wcstombs(desc.displayname,string,wcslen(string)+1); + CoTaskMemFree(string); + IPropertyBag *bag; + if (moni->BindToStorage(0,0,IID_IPropertyBag,(void**)&bag) == S_OK) + { + VARIANT vari; + VariantInit(&vari); + result = bag->Read(L"FriendlyName",&vari,NULL); + if (result == S_OK) + { + desc.friendlyname=new char[wcslen(vari.bstrVal)+1]; + wcstombs(desc.friendlyname,vari.bstrVal,wcslen(vari.bstrVal)+1); + } + VariantClear(&vari); + bag->Release(); + + } + + + videoH264filterlist.push_back(desc); + + + + moni->Release(); + // bctx->Release(); + } + int i; + videoH264filterselected=-1; + + + + myenum->Release(); + + + + filtmap->Release(); +} + +bool VideoWin::loadOptionsfromServer(VDR* vdr) +{ + char *name=vdr->configLoad("DirectShow","VideoFilter"); + + if (name != NULL) + { + for (int i = 0;i configLoad("DirectShow","VideoH264Filter"); + + if (name != NULL) + { + for (int i = 0;i configLoad("DirectShow","VMR9DeinterlacingMode"); + if (name != NULL) + { + if (STRCASECMP(name,"NoMix")==0) { + vmrdeinterlacing=0; + } else if (STRCASECMP(name,"None")==0) { + vmrdeinterlacing=1; + } else if (STRCASECMP(name,"Best")==0) { + vmrdeinterlacing=2; + } else if (STRCASECMP(name,"Bob")==0) { + vmrdeinterlacing=3; + } else if (STRCASECMP(name,"Weave")==0) { + vmrdeinterlacing=4; + } + } + + name=vdr->configLoad("DirectShow", "VideoPresenter"); + if (name!=NULL) { + if (STRCASECMP(name,"VMR9")==0) { + currentpresenter=VMR9; + } else if (STRCASECMP(name,"EVR")==0) { + currentpresenter=EVR; + } + } + if (!((OsdWin*)Osd::getInstance())->IsEvrSupported()) { + currentpresenter=VMR9; + } + + name=vdr->configLoad("DirectShow","videoH264dtsfix"); + if (name!=NULL) { + if (STRCASECMP(name,"YES")==0) { + videoH264dtsfix=true; + } else { + videoH264dtsfix=false; + } + } + name=vdr->configLoad("DirectShow","videompeg2dtsfix"); + if (name!=NULL) { + if (STRCASECMP(name,"YES")==0) { + videompeg2dtsfix=true; + } else { + videompeg2dtsfix=false; + } + } + + name=vdr->configLoad("DirectGraphics", "StretchFiltering"); + if (name!=NULL) { + if (STRCASECMP(name,"None")==0) { + ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_NONE); + } else if (STRCASECMP(name,"Point")==0) { + ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_POINT); + } else if (STRCASECMP(name,"Linear")==0) { + ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_LINEAR); + } + } + + + + + return true; + +} + +bool VideoWin::handleOptionChanges(Option* option) +{ + if( Video::handleOptionChanges(option)) return true; + switch(option->id) { + case 1: { + if (STRCASECMP(option->options[option->userSetChoice],"None")==0) { + ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_NONE); + } else if (STRCASECMP(option->options[option->userSetChoice],"Point")==0) { + ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_POINT); + } else if (STRCASECMP(option->options[option->userSetChoice],"Linear")==0) { + ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_LINEAR); + } + return true; + } break; + case 2: { + if (STRCASECMP(option->options[option->userSetChoice],"NoMix")==0) { + vmrdeinterlacing=0; + } else if (STRCASECMP(option->options[option->userSetChoice],"None")==0) { + vmrdeinterlacing=1; + } else if (STRCASECMP(option->options[option->userSetChoice],"Best")==0) { + vmrdeinterlacing=2; + } else if (STRCASECMP(option->options[option->userSetChoice],"Bob")==0) { + vmrdeinterlacing=3; + } else if (STRCASECMP(option->options[option->userSetChoice],"Weave")==0) { + vmrdeinterlacing=4; + } + }break; + case 3: { + if (STRCASECMP(option->options[option->userSetChoice],"VMR9")==0) { + currentpresenter=VMR9; + } else if (STRCASECMP(option->options[option->userSetChoice],"EVR")==0) { + currentpresenter=EVR; + } + }break; + case 4: { + if (STRCASECMP(option->options[option->userSetChoice],"Yes")==0) { + videoH264dtsfix=true; + } else { + videoH264dtsfix=false; + } + }break; + case 5: { + if (STRCASECMP(option->options[option->userSetChoice],"Yes")==0) { + videompeg2dtsfix=true; + } else { + videompeg2dtsfix=false; + } + }break; + }; + return false; + +} + +bool VideoWin::saveOptionstoServer() +{ + if (videofilterselected!=-1) { + VDR::getInstance()->configSave("DirectShow", + "VideoFilter",videofilterlist[videofilterselected].displayname); + VDR::getInstance()->configSave("DirectShow", + "VideoH264Filter",videoH264filterlist[videoH264filterselected].displayname); + } + return true; +} + +/*Option(UINT id, const char* displayText, const char* configSection, const char* configKey, UINT optionType, + UINT numChoices, UINT defaultChoice, UINT startInt, + const char * const * options, const char * const * optionkeys = NULL, AbstractOption* handler=NULL);*/ + +bool VideoWin::addOptionsToPanes(int panenumber,Options *options,WOptionPane* pane) +{ + if (!Video::addOptionsToPanes(panenumber,options,pane)) return false; + + + Option* option; + if (panenumber == 2) + { + DWORD scalingcaps=((OsdWin*)Osd::getInstance())->getFilterCaps(); + char **scalingopts=new char *[3]; + int i=0; + scalingopts[i]=new char[strlen("None")+1]; + strcpy(scalingopts[i],"None"); + i++; + if ((scalingcaps & D3DPTFILTERCAPS_MINFPOINT)!=0 + && (scalingcaps & D3DPTFILTERCAPS_MAGFPOINT)!=0) { + scalingopts[i]=new char[strlen("Point")+1]; + strcpy(scalingopts[i],"Point"); + i++; + } + if ((scalingcaps & D3DPTFILTERCAPS_MINFLINEAR)!=0 + && (scalingcaps & D3DPTFILTERCAPS_MAGFLINEAR)!=0) { + scalingopts[i]=new char[strlen("Linear")+1]; + strcpy(scalingopts[i],"Linear"); + i++; + } + option = new Option(1 ,tr("Video Stretching Filter"), "DirectGraphics", "StretchFiltering", Option::TYPE_TEXT, i, (i-1), 0, scalingopts,NULL,true, this); + options->push_back(option); + pane->addOptionLine(option); + static const char* deintopts[]={"NoMix","None","Best","Bob","Weave"}; + option = new Option(2,tr("VMR9 Deinterlacing Mode"), "DirectShow","VMR9DeinterlacingMode",Option::TYPE_TEXT,5,2,0,deintopts,NULL,false,this); + options->push_back(option); + pane->addOptionLine(option); + + if (((OsdWin*)Osd::getInstance())->IsEvrSupported()) + { + static const char* presenteropts[]={"EVR","VMR9"}; + option = new Option(3,tr("Video Presenter Filter"),"DirectShow", "VideoPresenter",Option::TYPE_TEXT,2, + (currentpresenter==EVR)?0:1,0,presenteropts,NULL,false,this); + } else { + static const char* presenteropts[]={"VMR9"}; + option = new Option(3,tr("Video Presenter Filter"),"DirectShow", "VideoPresenter",Option::TYPE_TEXT,1,0,0,presenteropts,NULL,false,this); + } + options->push_back(option); + pane->addOptionLine(option); + + static const char* yesnoopts[]={"Yes","No"}; + option = new Option(4,tr("Video H264 fix dts time"), "DirectShow","videoH264dtsfix",Option::TYPE_TEXT,2,1,0,yesnoopts,NULL,false,this); + options->push_back(option); + pane->addOptionLine(option); + + option = new Option(5,tr("Video Mpeg2 fix dts time"), "DirectShow","videompeg2dtsfix",Option::TYPE_TEXT,2,1,0,yesnoopts,NULL,false,this); + options->push_back(option); + pane->addOptionLine(option); + + + + } + + return true; +} + +IBaseFilter *VideoWin::getVideoFilter() +{ + IBaseFilter *curfilter= NULL; + if (videofilterselected == -1) + { + int i; + for (i = 0;i BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) + { + IAMDecoderCaps* desccaps=NULL; + if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK) + { + DWORD caps; + desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps); + if (caps == DECODER_CAP_SUPPORTED) + { + videofilterlist[i].vmr9tested = true; + videofilterlist[i].vmr9 = true; + videofilterselected = i; + } + else + { + videofilterlist[i].vmr9tested = true; + videofilterlist[i].vmr9 = false; + + curfilter->Release(); + curfilter=NULL; + } + } + desccaps->Release(); + } + moni->Release(); + } + delete [] name; + bindctx->Release(); + } + if (videofilterlist[i].vmr9) break; + + } + if (curfilter != NULL) + { + VDR *vdr=VDR::getInstance(); + if (vdr != NULL) + { + vdr->configSave("DirectShow","VideoFilter", + videofilterlist[videofilterselected].displayname); + } + return curfilter; + } + } + else + { + IBindCtx *bindctx=NULL; + if (CreateBindCtx(0,&bindctx)!=S_OK) return NULL; + IMoniker * moni=NULL; + LPCOLESTR name=new WCHAR[strlen(videofilterlist[videofilterselected].displayname)+1]; + mbstowcs((wchar_t*)name,videofilterlist[videofilterselected].displayname, + strlen(videofilterlist[videofilterselected].displayname)+1); + ULONG eater; + if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK) + { + if (moni->BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) + { + IAMDecoderCaps* desccaps=NULL; + if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK) + { + DWORD caps; + desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps); + if (caps == DECODER_CAP_SUPPORTED) + { + videofilterlist[videofilterselected].vmr9tested = true; + videofilterlist[videofilterselected].vmr9 = true; + } + else + { + videofilterlist[videofilterselected].vmr9tested = true; + videofilterlist[videofilterselected].vmr9 = false; + Log::getInstance()->log("VideoWin", Log::WARN ,"Filter does not support VMR9, but is selected, manual selection!"); + } + } + moni->Release(); + delete [] name; + bindctx->Release(); + return curfilter; + } + moni->Release(); + } + bindctx->Release(); + delete [] name; + return NULL; + } + return NULL; + +} + +IBaseFilter *VideoWin::getVideoH264Filter() +{ + IBaseFilter *curfilter= NULL; + if (videoH264filterselected == -1) + { + int i; + for (i = 0;i log("VideoWin", Log::DEBUG ,"Creating filter: %s",videoH264filterlist[i].friendlyname); + + + + if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK) + { + if (moni->BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) + { + IAMDecoderCaps* desccaps=NULL; + if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK) + { + DWORD caps; + desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps); + if (caps == DECODER_CAP_SUPPORTED) + { + videoH264filterlist[i].vmr9tested = true; + videoH264filterlist[i].vmr9 = true; + videoH264filterselected = i; + } + else + { + videoH264filterlist[i].vmr9tested = true; + videoH264filterlist[i].vmr9 = false; + + curfilter->Release(); + curfilter=NULL; + } + } + desccaps->Release(); + } + moni->Release(); + } + delete [] name; + bindctx->Release(); + } + if (videoH264filterlist[i].vmr9) break; + + } + if (curfilter != NULL) + { + VDR *vdr=VDR::getInstance(); + if (vdr != NULL) + { + vdr->configSave("DirectShow","VideoH264Filter", + videoH264filterlist[videoH264filterselected].displayname); + } + return curfilter; + } + } + else + { + IBindCtx *bindctx=NULL; + if (CreateBindCtx(0,&bindctx)!=S_OK) return NULL; + IMoniker * moni=NULL; + LPCOLESTR name=new WCHAR[strlen(videoH264filterlist[videoH264filterselected].displayname)+1]; + mbstowcs((wchar_t*)name,videoH264filterlist[videoH264filterselected].displayname, + strlen(videoH264filterlist[videoH264filterselected].displayname)+1); + ULONG eater; + Log::getInstance()->log("VideoWin", Log::DEBUG ,"Creating filter: %s",videoH264filterlist[videoH264filterselected].friendlyname); + if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK) + { + if (moni->BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) + { + IAMDecoderCaps* desccaps=NULL; + if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK) + { + DWORD caps; + desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps); + if (caps == DECODER_CAP_SUPPORTED) + { + videoH264filterlist[videoH264filterselected].vmr9tested = true; + videoH264filterlist[videoH264filterselected].vmr9 = true; + } + else + { + videoH264filterlist[videoH264filterselected].vmr9tested = true; + videoH264filterlist[videoH264filterselected].vmr9 = false; + Log::getInstance()->log("VideoWin", Log::WARN ,"Filter does not support VMR9, but is selected, manual selection!"); + } + } + moni->Release(); + delete [] name; + bindctx->Release(); + return curfilter; + } + moni->Release(); + } + bindctx->Release(); + delete [] name; + return NULL; + } + return NULL; + +} + + +#ifdef DS_DEBUG // This stuff would not included in vomp due to lincemse restrcitions +#include "dshelper.h" +#endif + +#define DO_VIDEO + +int VideoWin::play() +{ + if (!initted) return 0; + return 1; +} + +bool VideoWin::addOptionPagesToWTB(WTabBar *wtb) +{ + Boxx *box=new WWinVideoFilter(); + wtb->addTab(tr("Video Filter"), box); + box=new WWinVideoH264Filter(); + wtb->addTab(tr("H264 Filter"), box); + return true; +} + +const VideoFilterDescList *VideoWin::getVideoFilterList(int &selected) +{ + selected=videofilterselected; + return &videofilterlist; +} + +const VideoFilterDescList *VideoWin::getVideoH264FilterList(int &selected) +{ + selected=videoH264filterselected; + return &videoH264filterlist; +} + +bool VideoWin::selectVideoFilter(int filter) +{ + IBindCtx *bindctx=NULL; + if (CreateBindCtx(0,&bindctx)!=S_OK) return NULL; + IMoniker * moni=NULL; + LPCOLESTR name=new WCHAR[strlen(videofilterlist[filter].displayname)+1]; + mbstowcs((wchar_t*)name,videofilterlist[filter].displayname, + strlen(videofilterlist[filter].displayname)+1); + ULONG eater; + bool success=false; + if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK) + { + IBaseFilter* curfilter=NULL; + if (moni->BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) + { + IAMDecoderCaps* desccaps=NULL; + if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK) + { + DWORD caps; + HRESULT hres=desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps); + if (caps == DECODER_CAP_SUPPORTED) + { + videofilterlist[filter].vmr9tested = true; + videofilterlist[filter].vmr9 = true; + success=true; + } + else + { + videofilterlist[filter].vmr9tested = true; + videofilterlist[filter].vmr9 = false; + success=false; + } + desccaps->Release(); + } else { + videofilterlist[filter].vmr9tested = true; + videofilterlist[filter].vmr9 = false; + success=false; + } + + curfilter->Release(); + + } + moni->Release(); + } + bindctx->Release(); + delete [] name; + if (success || true) + { + videofilterselected=filter; + return true; + } + else + { + return false; + } +} + +bool VideoWin::selectVideoH264Filter(int filter) +{ + IBindCtx *bindctx=NULL; + if (CreateBindCtx(0,&bindctx)!=S_OK) return NULL; + IMoniker * moni=NULL; + LPCOLESTR name=new WCHAR[strlen(videoH264filterlist[filter].displayname)+1]; + mbstowcs((wchar_t*)name,videoH264filterlist[filter].displayname, + strlen(videoH264filterlist[filter].displayname)+1); + ULONG eater; + bool success=false; + if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK) + { + IBaseFilter* curfilter=NULL; + if (moni->BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) + { + IAMDecoderCaps* desccaps=NULL; + if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK) + { + DWORD caps; + HRESULT hres=desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps); + if (caps == DECODER_CAP_SUPPORTED) + { + videoH264filterlist[filter].vmr9tested = true; + videoH264filterlist[filter].vmr9 = true; + success=true; + } + else + { + videoH264filterlist[filter].vmr9tested = true; + videoH264filterlist[filter].vmr9 = false; + success=false; + } + desccaps->Release(); + } else { + videoH264filterlist[filter].vmr9tested = true; + videoH264filterlist[filter].vmr9 = false; + success=false; + } + + curfilter->Release(); + + } + moni->Release(); + } + bindctx->Release(); + delete [] name; + if (success || true) + { + videoH264filterselected=filter; + return true; + } + else + { + return false; + } +} + +int VideoWin::dsInitVideoFilter() +{ + #ifdef DO_VIDEO + HRESULT hres; + if (videoon) { + //We alloc the vmr9 as next step + if (currentpresenter==VMR9) + { + Log::getInstance()->log("VideoWin", Log::INFO ,"VMR9 Videopresenter selected!"); + if (hres=CoCreateInstance(CLSID_VideoMixingRenderer9,0, + CLSCTX_INPROC_SERVER,IID_IBaseFilter,(void**) &dsrenderer)!=S_OK) + { + Log::getInstance()->log("VideoWin", Log::WARN ,"Failed creating VMR9 renderer!"); + ReleaseMutex(filtermutex); + CleanupDS(); + } + /*VMR 9 stuff**/ + if (hres=dsgraphbuilder->AddFilter(dsrenderer,L"VMR9")!=S_OK) + { + ReleaseMutex(filtermutex); + CleanupDS(); + Log::getInstance()->log("VideoWin", Log::WARN ,"Failed adding VMR9 renderer!"); + return 0; + } + IVMRFilterConfig9* vmrfilconfig; + if (dsrenderer->QueryInterface(IID_IVMRFilterConfig9,(void**)&vmrfilconfig)!=S_OK) + { + ReleaseMutex(filtermutex); + CleanupDS(); + Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting VMR9 Filterconfig interface!"); + return 0; + } + if (vmrdeinterlacing!=0) vmrfilconfig->SetNumberOfStreams(1);//Enter Mixing Mode + vmrfilconfig->SetRenderingMode(VMR9Mode_Renderless); + vmrfilconfig->Release(); + if (dsrenderer->QueryInterface(IID_IVMRSurfaceAllocatorNotify9, + (void**)& dsvmrsurfnotify) != S_OK) + { + ReleaseMutex(filtermutex); + CleanupDS(); + Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting VMR9 Surface Allocator interface!"); + return 0; + } + allocatorvmr=new DsAllocator(); + dsvmrsurfnotify->AdviseSurfaceAllocator(NULL,allocatorvmr); + allocatorvmr->AdviseNotify(dsvmrsurfnotify); + + IVMRDeinterlaceControl9* deintctrl; + if (dsrenderer->QueryInterface(IID_IVMRDeinterlaceControl9,(void**)&deintctrl)!=S_OK) + { + ReleaseMutex(filtermutex); + CleanupDS(); + Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting VMR9 Deinterlace control!"); + return 0; + } + /*turnoff*/ + switch (vmrdeinterlacing) + { + case 1: //No Deinterlasing + deintctrl->SetDeinterlaceMode(0xFFFFFFFF,(LPGUID)&GUID_NULL);//Turn Off + break; + case 2: //Best + deintctrl->SetDeinterlacePrefs(DeinterlacePref_NextBest);//Choose Next Best + break; + case 3: //Bob + deintctrl->SetDeinterlacePrefs(DeinterlacePref_BOB);//Choose NBob + break; + case 4: //Weave + deintctrl->SetDeinterlacePrefs(DeinterlacePref_Weave);//Choose Weave + break; + }; + deintctrl->Release(); + /*VMR 9 stuff end */ + } + else if (currentpresenter==EVR) + { + Log::getInstance()->log("VideoWin", Log::INFO ,"EVR Videopresenter selected!"); + if (hres=CoCreateInstance(CLSID_EnhancedVideoRenderer,0, + CLSCTX_INPROC_SERVER,IID_IBaseFilter,(void**) &dsrenderer)!=S_OK) + { + Log::getInstance()->log("VideoWin", Log::WARN ,"Failed creating EVR renderer!"); + ReleaseMutex(filtermutex); + CleanupDS(); + } + /*EVR stuff**/ + if (hres=dsgraphbuilder->AddFilter(dsrenderer,L"EVR")!=S_OK) + { + ReleaseMutex(filtermutex); + CleanupDS(); + Log::getInstance()->log("VideoWin", Log::WARN ,"Failed adding EVR renderer!"); + return 0; + } + + + IMFGetService *evr_services; + if (dsrenderer->QueryInterface(IID_IMFGetService,(void**)&evr_services)!=S_OK) + { + ReleaseMutex(filtermutex); + CleanupDS(); + Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting EVR IMFGetServices interface!"); + return 0; + } + + IMFVideoDisplayControl* mfvideodisplaycontrol; + if (evr_services->GetService(MR_VIDEO_RENDER_SERVICE,IID_IMFVideoDisplayControl,(void**)&mfvideodisplaycontrol)!=S_OK) + { + ReleaseMutex(filtermutex); + CleanupDS(); + Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting EVR IMFVideoDisplayControl interface!"); + return 0; + } + + evr_services->Release(); + mfvideodisplaycontrol->SetVideoWindow(((OsdWin*) Osd::getInstance())->getWindow()); + //RECT client; + //GetClientRect(((OsdWin*) Osd::getInstance())->getWindow(), &client); + //mfvideodisplaycontrol->SetVideoPosition(NULL,&client); + + mfvideodisplaycontrol->Release(); + + + /// if (vmrdeinterlacing!=0) vmrfilconfig->SetNumberOfStreams(1);//Enter Mixing Mode //always the case for evr! + + IMFVideoRenderer *mfvideorenderer; + if (dsrenderer->QueryInterface(IID_IMFVideoRenderer,(void**)&mfvideorenderer)!=S_OK) + { + ReleaseMutex(filtermutex); + CleanupDS(); + Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting EVR IMFVideoRenderer interface!"); + return 0; + } + + allocatorvmr=new DsAllocator(); + HRESULT hres=mfvideorenderer->InitializeRenderer(NULL,allocatorvmr); + + mfvideorenderer->Release(); + //How should I do this in EVR? + /* IVMRDeinterlaceControl9* deintctrl; + if (dsrenderer->QueryInterface(IID_IVMRDeinterlaceControl9,(void**)&deintctrl)!=S_OK) + { + ReleaseMutex(filtermutex); + CleanupDS(); + Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting VMR9 Deinterlace control!"); + return 0; + } + /*turnoff* + switch (vmrdeinterlacing) + { + case 1: //No Deinterlasing + deintctrl->SetDeinterlaceMode(0xFFFFFFFF,(LPGUID)&GUID_NULL);//Turn Off + break; + case 2: //Best + deintctrl->SetDeinterlacePrefs(DeinterlacePref_NextBest);//Choose Next Best + break; + case 3: //Bob + deintctrl->SetDeinterlacePrefs(DeinterlacePref_BOB);//Choose NBob + break; + case 4: //Weave + deintctrl->SetDeinterlacePrefs(DeinterlacePref_Weave);//Choose Weave + break; + }; + deintctrl->Release();*/ + /*EVR stuff end */ + } else { + Log::getInstance()->log("VideoWin", Log::ERR ,"No videopresenter selected! Please post on the forum!"); + return -1; + } + IFilterGraph2*fg2=NULL; + if (dsgraphbuilder->QueryInterface(IID_IFilterGraph2,(void**)&fg2)!= S_OK) + { + Log::getInstance()->log("VideoWin", Log::WARN , "Failed querying for FilterGraph2 Interface!"); + ReleaseMutex(filtermutex); + CleanupDS(); + return 0; + } +/*#ifndef NEW_DS_MECHANISMENS + + if (hres=fg2->RenderEx((IPin*)sourcefilter->GetVideoPin()/*video*, + AM_RENDEREX_RENDERTOEXISTINGRENDERERS,NULL) != S_OK) + { + Log::getInstance()->log("VideoWin", Log::WARN , "Failed rendering Video!"); + fg2->Release(); + ReleaseMutex(filtermutex); + CleanupDS(); + return 0; + } + +#else*/ + IBaseFilter*videofilter; + if (h264) + { + Log::getInstance()->log("VideoWin", Log::DEBUG ,"Entering h264 playback..."); + videofilter=getVideoH264Filter(); + } + else + { + Log::getInstance()->log("VideoWin", Log::DEBUG ,"Entering MPEG2 playback..."); + videofilter=getVideoFilter(); + } + if (hres=dsgraphbuilder->AddFilter(videofilter,NULL) != S_OK) + { + Log::getInstance()->log("VideoWin", Log::WARN , "Failed adding Video Filter!"); + ReleaseMutex(filtermutex); + CleanupDS(); + return 0; + } + IEnumPins *pinenum=NULL; + bool error=false; + + mptype_video_detail vid_details; + Demuxer* demux=Demuxer::getInstance(); + vid_details.width=demux->getHorizontalSize(); + vid_details.height=demux->getVerticalSize(); + + if (h264) + { + if (vid_details.width!=0 && vid_details.height!=0) + { + sourcefilter->GetVideoPin()->SetPinMode(MPTYPE_VIDEO_H264,&vid_details); + } + else + { + sourcefilter->GetVideoPin()->SetPinMode(MPTYPE_VIDEO_H264,NULL); + } + + } + else + { + if (vid_details.width!=0 && vid_details.height!=0) + { + sourcefilter->GetVideoPin()->SetPinMode(MPTYPE_VIDEO_MPEG2,&vid_details); + } + else + { + sourcefilter->GetVideoPin()->SetPinMode(MPTYPE_VIDEO_MPEG2,NULL); + } + } + if (videofilter->EnumPins(&pinenum) == S_OK) + { + IPin *current=NULL; + ULONG fetch=0; + bool firststep=false; + + while (pinenum->Next(1,¤t,&fetch)==S_OK) + { + PIN_DIRECTION dir; + if (current->QueryDirection(&dir)==S_OK) + { + if (dir == PINDIR_INPUT) + { + if (sourcefilter->GetVideoPin()->Connect(current,NULL)==S_OK) + { + current->Release(); + firststep=true; + break; + } + } + } + current->Release(); + } + if (firststep==false) + { + Log::getInstance()->log("VideoWin", Log::WARN , "Video Filter has no suitable input!"); + videofilter->Release(); + ReleaseMutex(filtermutex); + CleanupDS(); + return 0; + } + bool secondstep=false; + pinenum->Reset(); + while (pinenum->Next(1,¤t,&fetch)==S_OK) + { + PIN_DIRECTION dir; + if (current->QueryDirection(&dir)==S_OK) + { + if (dir == PINDIR_OUTPUT) + { + + if (fg2->RenderEx((IPin*)current/*video*/, + AM_RENDEREX_RENDERTOEXISTINGRENDERERS,NULL) ==S_OK) + { + current->Release(); + secondstep=true; + break; + } + } + } + current->Release(); + } + if (secondstep==false) + { + Log::getInstance()->log("VideoWin", Log::WARN , "Video Filter has no suitable output!"); + videofilter->Release(); + ReleaseMutex(filtermutex); + CleanupDS(); + return 0; + } + + videofilter->Release(); + pinenum->Release(); + + } + + + + fg2->Release(); + return 1; + } +#endif + return 1; +} + +int VideoWin::setAudioStreamType(UCHAR type) +{ + aud_type=type; + if (!initted) return 0; + return 1; +} + +int VideoWin::dsplay() +{ + if (!initted) return 0; + CleanupDS(); + + //Build filter graph + HRESULT hres; + //So this is the real code, this prevents the feeder from calling noexisting objects! + WaitForSingleObject(filtermutex,INFINITE); + if (hres=CoCreateInstance(CLSID_FilterGraph,NULL,CLSCTX_INPROC_SERVER, + IID_IGraphBuilder,(void**)&dsgraphbuilder) != S_OK) + { + ReleaseMutex(filtermutex); + return 0; + } + #ifdef DS_DEBUG + AddToRot(dsgraphbuilder,&graphidentifier); + #endif + + firstsynched=false; + if (aud_type==Audio::MP3) { + lastaudiomode=MPTYPE_MPEG_AUDIO_LAYER3; + } else { + lastaudiomode=MPTYPE_MPEG_AUDIO; + } + //lastaudiomode=MPTYPE_AC3; + sourcefilter=new DsSourceFilter(); //Creating our Source filter for pushing Data + // to DirectShow + if (hres=dsgraphbuilder->AddFilter(sourcefilter,L"Vomp Win Source Filter") != S_OK) + { + Log::getInstance()->log("VideoWin", Log::WARN , "Failed adding Vomp Source Filter!"); + ReleaseMutex(filtermutex); + CleanupDS(); + return 0; + } + sourcefilter->GetAudioPin()->SetPinMode(lastaudiomode); + /*if (hres=dsgraphbuilder->Render((IPin*)sourcefilter->GetAudioPin()/*audio*)!=S_OK) + { + Log::getInstance()->log("VideoWin", Log::WARN , "Failed rendering audio!"); + ReleaseMutex(filtermutex); + CleanupDS(); + return 0; + }*/ + if (((AudioWin*)Audio::getInstance())->dsInitAudioFilter(dsgraphbuilder)==0) + { + Log::getInstance()->log("VideoWin", Log::WARN , "Failed rendering audio!"); + ReleaseMutex(filtermutex); + CleanupDS(); + return 0; + } + + + if (dsInitVideoFilter()==0) + { + return 0; + } + + if (hres=CoCreateInstance(CLSID_SystemClock,NULL,CLSCTX_INPROC_SERVER, + IID_IReferenceClock,(void**)&dsrefclock)!=S_OK) + { + return 0; + } + dsgraphbuilder->QueryInterface(IID_IMediaFilter,(void **) &dsmediafilter); + HRESULT hresdeb = dsmediafilter->SetSyncSource(dsrefclock); + + dsgraphbuilder->QueryInterface(IID_IMediaControl,(void **) &dsmediacontrol); + dsgraphbuilder->QueryInterface(IID_IBasicAudio,(void **) &dsbasicaudio); + if (dsbasicaudio) + dsbasicaudio->put_Volume(audiovolume); + dsinited=true; + //MILLISLEEP(100); + + hresdeb=dsmediacontrol->Run(); + iframemode=false;//exit iframe mode + ReleaseMutex(filtermutex); + return 1; +} + +int VideoWin::EnterIframePlayback() +{ + if (!initted) return 0; + CleanupDS(); + //So this is the real code, this prevents the feeder from calling noexisting objects! + WaitForSingleObject(filtermutex,INFINITE); + iframemode=true;//enter iframe mode + //Build filter graph + HRESULT hres; + if (hres=CoCreateInstance(CLSID_FilterGraph,NULL,CLSCTX_INPROC_SERVER, + IID_IGraphBuilder,(void**)&dsgraphbuilder)!=S_OK) { + ReleaseMutex(filtermutex); + return 0; + } +#ifdef DS_DEBUG + AddToRot(dsgraphbuilder,&graphidentifier); +#endif + + //firstsynched=false; + sourcefilter=new DsSourceFilter(); //Creating our Source filter for pushing Data + // to DirectShow + if (hres=dsgraphbuilder->AddFilter(sourcefilter,L"Vomp Win Source Filter")!=S_OK) { + Log::getInstance()->log("VideoWin", Log::WARN , "Failed adding Vomp Source Filter!"); + ReleaseMutex(filtermutex); + CleanupDS(); + return 0; + } +#ifdef DO_VIDEO + if (videoon) { + dsInitVideoFilter(); + } +#endif +/* if (hres=CoCreateInstance(CLSID_SystemClock,NULL,CLSCTX_INPROC_SERVER, + IID_IReferenceClock,(void**)&dsrefclock)!=S_OK) { + return 0; + }*/ + + dsgraphbuilder->QueryInterface(IID_IMediaFilter,(void **) &dsmediafilter); + dsmediafilter->SetSyncSource(/*dsrefclock*/NULL); //Run as fast as you can! + + dsgraphbuilder->QueryInterface(IID_IMediaControl,(void **) &dsmediacontrol); + dsgraphbuilder->QueryInterface(IID_IBasicAudio,(void **) &dsbasicaudio); + dsinited=true; + + + dsmediacontrol->Run(); + ReleaseMutex(filtermutex); + return 1; + +} + +int VideoWin::dsstop() +{ + if (!initted) return 0; + + CleanupDS(); + + + return 1; +} + +int VideoWin::stop() +{ + if (!initted) return 0; + + + return 1; +} + +int VideoWin::reset() +{ + if (!initted) return 0; + + + return 1; +} + +int VideoWin::dsreset() +{ + if (!initted) return 0; + videoposx=0; + videoposy=0; + iframemode=false;//exit iframe mode + CleanupDS(); + + return 1; +} + +int VideoWin::dspause() +{ + if (!initted) return 0; + WaitForSingleObject(filtermutex,INFINITE); + if (dsmediacontrol) dsmediacontrol->Pause(); + ReleaseMutex(filtermutex); + return 1; +} + +int VideoWin::pause() +{ + if (!initted) return 0; + + return 1; +} + +int VideoWin::unPause() // FIXME get rid - same as play!! +{//No on windows this is not the same, I don't get rid of! + if (!initted) return 0; + return 1; +} + +int VideoWin::dsunPause() // FIXME get rid - same as play!! +{//No on windows this is not the same, I don't get rid of! + if (!initted) return 0; + WaitForSingleObject(filtermutex,INFINITE); + if (dsmediacontrol) dsmediacontrol->Run(); + ReleaseMutex(filtermutex); + + return 1; +} + +int VideoWin::fastForward() +{ + if (!initted) return 0; + + return 1; +} + +int VideoWin::unFastForward() +{ + if (!initted) return 0; + + return 1; +} + +int VideoWin::attachFrameBuffer() +{ + if (!initted) return 0; + return 1; +} + +int VideoWin::blank(void) +{ + ((OsdWin*)Osd::getInstance())->Blank(); + return 1; +} + +ULLONG VideoWin::getCurrentTimestamp() +{ + REFERENCE_TIME startoffset; + REFERENCE_TIME ncr_time; + if (iframemode) return 0; //Not in iframe mode! + if (!dsrefclock || !sourcefilter) return 0; + FILTER_STATE state; + sourcefilter->GetState(10,&state); + + if (state==State_Running) dsrefclock->GetTime(&cr_time); + ncr_time=cr_time; + startoffset=sourcefilter->getStartOffset(); + if (startoffset==0) return 0; + ncr_time-=startoffset; + ncr_time-=lastreftimeRT; + /* ULLONG result=frameNumberToTimecode( + VDR::getInstance()->frameNumberFromPosition(lastreftimeBYTE));*/ + long long result=lastreftimePTS; + result+=(ULLONG)(ncr_time/10000LL*90LL); + if (result<0) result=(1LL << 33)-result; + return result; + +} + + +/* //to beremoved +ULONG VideoWin::timecodeToFrameNumber(ULLONG timecode) +{ + if (format == PAL) return (ULONG)(((double)timecode / (double)90000) * (double)25); + else return (ULONG)(((double)timecode / (double)90000) * (double)30); +} + +ULLONG VideoWin::frameNumberToTimecode(ULONG framenumber) +{ + if (format == PAL) return (ULLONG)(((double)framenumber * (double)90000) / (double)25); + else return (ULLONG)(((double)framenumber * (double)90000) / (double)30); +} +*/ +void VideoWin::CleanupDS() +{ + WaitForSingleObject(filtermutex,INFINITE); + dsinited=false; + if (dsmediacontrol)dsmediacontrol->Stop(); + if (cur_audio_media_sample) { + cur_audio_media_sample->Release(); + cur_audio_media_sample=NULL; + } + if (cur_video_media_sample) { + cur_video_media_sample->Release(); + cur_video_media_sample=NULL; + } + if (dsbasicaudio) { + dsbasicaudio->Release(); + dsbasicaudio=NULL; + } + if (dsvmrsurfnotify) { + dsvmrsurfnotify->Release(); + dsvmrsurfnotify=NULL; + } + if (dsrenderer) { + dsrenderer->Release(); + dsrenderer=NULL; + } + + if (allocatorvmr) { + allocatorvmr->Release(); + allocatorvmr=NULL; + } + + if (dsrefclock) { + dsrefclock->Release(); + dsrefclock=NULL; + } + if (dsmediafilter) { + dsmediafilter->Release(); + dsmediafilter=NULL; + } + + + + if (dsmediacontrol) { + dsmediacontrol->Stop(); + dsmediacontrol->Release(); + dsmediacontrol=NULL; + } + if (dsgraphbuilder){ +#ifdef DS_DEBUG + RemoveFromRot(graphidentifier); +#endif + dsgraphbuilder->Release(); + dsgraphbuilder=NULL; + + sourcefilter=NULL; //The Graph Builder destroys our SourceFilter + } + ReleaseMutex(filtermutex); + +} + +void VideoWin::PrepareMediaSample(const MediaPacketList& mplist, + UINT samplepos) +{ + mediapacket = mplist.front(); +} + +UINT VideoWin::DeliverMediaSample(UCHAR* buffer, UINT *samplepos) +{ + DeliverMediaPacket(mediapacket, buffer, samplepos); + if (*samplepos == mediapacket.length) { + *samplepos = 0; + return 1; + } + else return 0; +} + +UINT VideoWin::DeliverMediaPacket(MediaPacket packet, + const UCHAR* buffer, + UINT *samplepos) +{ + + /*First Check, if we have an audio sample*/ + if (!isdsinited()) return 0; + if (packet.type == MPTYPE_VIDEO_H264) + { + h264=true; + } + else + { + h264=false; + } + +#ifdef DO_VIDEO + if (!videoon) { + *samplepos+=packet.length; + MILLISLEEP(0); //yet not implemented//bad idea + return packet.length; + } + /*First Check, if we have an audio sample*/ + if (iframemode) { + //samplepos=0; + MILLISLEEP(10); + return 0; //Not in iframe mode! + } + IMediaSample* ms=NULL; + REFERENCE_TIME reftime1=0; + REFERENCE_TIME reftime2=0; + + UINT headerstrip=0; + if (packet.disconti) { + firstsynched=false; + DeliverVideoMediaSample(); + } + + + + /*Inspect PES-Header */ + + if (*samplepos==0) {//stripheader + headerstrip=buffer[packet.pos_buffer+8]+9/*is this right*/; + *samplepos+=headerstrip; + if ( packet.synched ) { + DeliverVideoMediaSample();//write out old data + /* if (packet.presentation_time<0) { //Preroll? + *samplepos=packet.length;//if we have not processed at least one + return packet.length;//synched packet ignore it! + }*/ + + reftime1=packet.presentation_time; + reftime2=reftime1+1; + firstsynched=true; + } else { + if (!firstsynched) {// + *samplepos=packet.length;//if we have not processed at least one + return packet.length;//synched packet ignore it! + } + } + } + BYTE *ms_buf; + UINT ms_length; + UINT ms_pos; + UINT haveToCopy; + + if (!getCurrentVideoMediaSample(&ms) || ms==NULL) {// get the current sample + //samplepos=0; + //MessageBox(0,"da isser","hei",0); + //MILLISLEEP(1); + return 0; + } + ms_pos=ms->GetActualDataLength(); + ms_length=ms->GetSize(); + haveToCopy=min(ms_length-ms_pos,packet.length-*samplepos); + if ((ms_length-ms_pos)<1 ) { + DeliverVideoMediaSample(); //we are full! + if (!getCurrentVideoMediaSample(&ms) || ms==NULL) {// get the current sample + //samplepos=0; + //MessageBox(0,"da isser","hei",0); + //MILLISLEEP(10); + return 0; + } + ms_pos=ms->GetActualDataLength(); + ms_length=ms->GetSize(); + haveToCopy=min(ms_length-ms_pos,packet.length-*samplepos); + } + ms->GetPointer(&ms_buf); + + + if (ms_pos==0) {//will only be changed on first packet + if (packet.disconti) { + ms->SetDiscontinuity(TRUE); + } else { + ms->SetDiscontinuity(FALSE); + } + if (packet.synched) { + ms->SetSyncPoint(TRUE); + ms->SetTime(&reftime1,&reftime2); + //Log::getInstance()->log("VideoWin", Log::DEBUG , "Setted videotime to %lld %lld",reftime1,reftime2); + //Log::getInstance()->log("VideoWin", Log::DEBUG , "Packet pts %lld dts %lld",packet.pts,packet.dts); + //ms->SetTime(NULL,NULL); + ms->SetMediaTime(NULL, NULL); + if (reftime1<0) ms->SetPreroll(TRUE); + else ms->SetPreroll(FALSE); + /*Timecode handling*/ + lastreftimeRT=reftime1; + lastreftimePTS=packet.pts; + + } + else + { + ms->SetSyncPoint(FALSE); + ms->SetTime(NULL,NULL); + ms->SetMediaTime(NULL, NULL); + ms->SetPreroll(FALSE); + + // ms->SetSyncPoint(TRUE); + } + } + + + + memcpy(ms_buf+ms_pos,buffer+packet.pos_buffer+*samplepos,haveToCopy); + ms->SetActualDataLength(haveToCopy+ms_pos); + + *samplepos+=haveToCopy; + + return haveToCopy+headerstrip; + +#else + + *samplepos+=packet.length; + MILLISLEEP(0); //yet not implemented//bad idea + return packet.length; +#endif +} + +int VideoWin::getCurrentAudioMediaSample(IMediaSample** ms) +{ + //WaitForSingleObject(filtermutex,INFINITE); + if (!sourcefilter){ + // ReleaseMutex(filtermutex); + return 0; + } + if (cur_audio_media_sample) { + *ms=cur_audio_media_sample;//already open + return 1; + } + if (!sourcefilter->getCurrentAudioMediaSample(ms)) { + // ReleaseMutex(filtermutex); + } + if (*ms) (*ms)->SetActualDataLength(0); + cur_audio_media_sample=*ms; + //Don't release the mutex before deliver + return 1; +} + +int VideoWin::getCurrentVideoMediaSample(IMediaSample** ms) +{ + //WaitForSingleObject(filtermutex,INFINITE); + if (!sourcefilter){ + // ReleaseMutex(filtermutex); + return 0; + } + if (cur_video_media_sample) { + *ms=cur_video_media_sample;//already open + return 1; + } + if (!sourcefilter->getCurrentVideoMediaSample(ms)) { + // ReleaseMutex(filtermutex); + } + if (*ms) (*ms)->SetActualDataLength(0); + + cur_video_media_sample=*ms; + //Don't release the mutex before deliver + return 1; +} + +int VideoWin::DeliverAudioMediaSample(){ + if (cur_audio_media_sample) { + sourcefilter->DeliverAudioMediaSample(cur_audio_media_sample); + cur_audio_media_sample=NULL; + } + //ReleaseMutex(filtermutex); + return 1; +} + +int VideoWin::DeliverVideoMediaSample(){ + if (cur_video_media_sample) { + sourcefilter->DeliverVideoMediaSample(cur_video_media_sample); + cur_video_media_sample=NULL; + } + //ReleaseMutex(filtermutex); + return 1; +} + +long long VideoWin::SetStartOffset(long long curreftime, bool *rsync) +{ + *rsync=false; + if (offsetnotset) { + startoffset=curreftime;//offset is set for audio + offsetnotset=false; + offsetvideonotset=false; + + + } else { + if (offsetvideonotset) { + offsetvideonotset=false; + *rsync=true; + } else { + if ( (curreftime-lastrefvideotime)>10000000LL + || (curreftime-lastrefvideotime)<-10000000LL) {//if pts jumps to big resync + startoffset+=curreftime-lastrefvideotime; + lastrefaudiotime+=curreftime-lastrefvideotime; + //*rsync=true; + offsetaudionotset=true; + + } + } + + } + + lastrefvideotime=curreftime; + + return startoffset; + +} + +long long VideoWin::SetStartAudioOffset(long long curreftime, bool *rsync) +{ + *rsync=false; + if (offsetnotset) { + startoffset=curreftime; + offsetnotset=false; + offsetaudionotset=false; + }else { + if (offsetaudionotset) { + offsetaudionotset=false; + *rsync=true; + } else { + if ( (curreftime-lastrefaudiotime)>10000000LL + || (curreftime-lastrefaudiotime)<-10000000LL) {//if pts jumps to big resync + startoffset+=curreftime-lastrefaudiotime; + lastrefvideotime+=curreftime-lastrefaudiotime; + //*rsync=true; + offsetvideonotset=true; + + } + } + + } + lastrefaudiotime=curreftime; + return startoffset; + +} +void VideoWin::ResetTimeOffsets() { + offsetnotset=true; //called from demuxer + offsetvideonotset=true; + offsetaudionotset=true; + startoffset=0; + lastrefaudiotime=0; + lastrefvideotime=0; + lastreftimeRT=0; + lastreftimePTS=0; + + +} + +void VideoWin::SetAudioVolume(long volume) +{ + audiovolume=volume; + if (dsbasicaudio) dsbasicaudio->put_Volume(volume); +} + +bool VideoWin::displayIFrame(const UCHAR* buffer, UINT length) +{ + if (!iframemode) EnterIframePlayback(); + if (!isdsinited()) return false; + +#ifdef DO_VIDEO + IMediaSample* ms=NULL; + REFERENCE_TIME reftime1=0; + REFERENCE_TIME reftime2=0; + if (!videoon) return; + if (!getCurrentVideoMediaSample(&ms) || ms==NULL) {// get the current sample + MILLISLEEP(10); + return ; + } + BYTE *ms_buf; + DWORD ms_length; + ms->GetPointer(&ms_buf); + ms_length=ms->GetSize(); + + /*First Check, if we have an video sample*/ + DWORD read_pos = 0, write_pos = 0; + DWORD pattern, packet_length; + DWORD headerstrip=0; + bool first=true; + if (length < 4) return ; + //Now we strip the pes header + pattern = (buffer[0] << 16) | (buffer[1] << 8) | (buffer[2]); + while (read_pos + 7 <= length) + { + pattern = ((pattern << 8) & 0xFFFFFFFF) | buffer[read_pos+3]; + if (pattern < 0x000001E0 || pattern > 0x000001EF) { + read_pos++; + continue; + } + else + { + headerstrip=buffer[read_pos+8]+9/*is this right*/; + packet_length = ((buffer[read_pos+4] << 8) | (buffer[read_pos+5])) + 6; + if (read_pos + packet_length > length) + read_pos = length; + else + { + if ( (headerstrip < packet_length) && + (write_pos+packet_length-headerstrip)>ms_length) { + if (first) { + ms->SetSyncPoint(TRUE); + ms->SetDiscontinuity(TRUE); + first=false; + } else ms->SetSyncPoint(FALSE); + ms->SetTime(NULL,NULL); + ms->SetMediaTime(NULL, NULL); + ms->SetActualDataLength(write_pos); + DeliverVideoMediaSample(); + + if (!getCurrentVideoMediaSample(&ms) || ms==NULL) {// get the current sample + MILLISLEEP(10); + return ; + } + write_pos=0; + ms_length=ms->GetSize(); + ms->GetPointer(&ms_buf); + } + if (packet_length>headerstrip) { + memcpy(ms_buf+write_pos, buffer+read_pos+headerstrip, packet_length-headerstrip); + write_pos += packet_length-headerstrip; + } + read_pos += packet_length; + + pattern = (buffer[read_pos] << 16) | (buffer[read_pos+1] << 8) + | (buffer[read_pos+2]); + } + } + } + + if (first) {ms->SetSyncPoint(TRUE); + ms->SetDiscontinuity(TRUE); + first=false;} + else ms->SetSyncPoint(FALSE); + ms->SetTime(NULL,NULL); + ms->SetMediaTime(NULL, NULL); + ms->SetActualDataLength(write_pos); + DeliverVideoMediaSample(); + + return true; +#else + + // *samplepos+=packet.length; + MILLISLEEP(0); //yet not implemented//bad idea + return true; +#endif +} + +bool VideoWin::supportsAc3(){ + if (sourcefilter != NULL) { + return sourcefilter->supportsAc3(); + } else { + return false; + } +} + +bool VideoWin::supportsh264() +{ + if (videoH264filterlist.size()>0) return true; + else return false; +} + + +bool VideoWin::changeAType(int type,IMediaSample* ms){ + if (sourcefilter!= NULL) { + lastaudiomode=type; + return sourcefilter->changeAType(type,ms); + } + else + { + return false; + } +} + +#ifdef DEV +int VideoWin::test() +{ + return 0; +} + +int VideoWin::test2() +{ + return 0; +} +#endif + + + + diff --git a/videowin.h b/videowin.h index 7090d6c..f57f4b7 100644 --- a/videowin.h +++ b/videowin.h @@ -1,223 +1,223 @@ -/* - 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 VIDEOWIN_H -#define VIDEOWIN_H - -#include -#include -#include -#include -#include -#include -#include - -#include "defines.h" -#include "video.h" - -//#define DS_DEBUG -#define NEW_DS_MECHANISMENS - -#ifdef NEW_DS_MECHANISMENS -struct VideoFilterDesc { - char * displayname; - char * friendlyname; - bool vmr9; - bool vmr9tested; -}; -using namespace std; -typedef vector VideoFilterDescList; -#endif - -class DsSourceFilter; -class DsAllocator; - -class VideoWin : public Video -{ -public: - VideoWin(); - ~VideoWin(); - - int init(UCHAR format); - int shutdown(); - - int setFormat(UCHAR format); - int setConnection(UCHAR connection); - int setAspectRatio(UCHAR aspectRatio); // This one does the pin 8 scart widescreen switching - UCHAR getAspectRatio(){return aspectRatio;}; - UCHAR getMode(){return mode;}; - UCHAR getPseudoTVsize() {return pseudotvsize;}; - int setMode(UCHAR mode); - int setTVsize(UCHAR size); // Is the TV a widescreen? - int setDefaultAspect(); - int setSource(); - int setPosition(int x, int y); - int sync(); - int play(); - int dsplay(); - bool InIframemode() {return iframemode;}; - int stop(); - int dsstop(); - int pause(); - int dspause(); - int unPause(); - int dsunPause(); - int fastForward(); - int unFastForward(); - int reset(); - int dsreset(); - int blank(); - int signalOn(); - int signalOff(); - int attachFrameBuffer(); // What does this do? -// ULONG timecodeToFrameNumber(ULLONG timecode); -// ULLONG frameNumberToTimecode(ULONG framenumber); - ULLONG getCurrentTimestamp(); - - bool loadOptionsfromServer(VDR* vdr); - bool saveOptionstoServer(); - bool addOptionPagesToWTB(WTabBar *wtb); - bool addOptionsToPanes(int panenumber,Options *options,WOptionPane* pane); - bool handleOptionChanges(Option* option); - - //Writing Data to Videodevice - virtual void PrepareMediaSample(const MediaPacketList&, UINT samplepos); - virtual UINT DeliverMediaSample(UCHAR* buffer, UINT *samplepos); - UINT DeliverMediaPacket(const MediaPacket packet, const UCHAR* buffer, UINT *samplepos); - virtual bool dtsTimefix() {if (h264)return videoH264dtsfix; else return videompeg2dtsfix;} - - virtual bool supportsh264(); - - - virtual bool supportsAc3(); - - enum VideoPresenter { - VMR9, - EVR - } ; - - - -private: - MediaPacket mediapacket; -public: - - int getCurrentAudioMediaSample(IMediaSample** ms); - int DeliverAudioMediaSample(); - - int getCurrentVideoMediaSample(IMediaSample** ms); - int DeliverVideoMediaSample(); - int setAudioStreamType(UCHAR type); - - virtual long long SetStartOffset(long long curreftime, bool *rsync); - long long SetStartAudioOffset(long long curreftime, bool *rsync); - virtual void ResetTimeOffsets(); - - void SetAudioState(bool state){audioon=state;}; - void SetAudioVolume(long volume); - - void turnVideoOn(){videoon=true;}; - void turnVideoOff(){videoon=false;}; - - virtual void displayIFrame(const UCHAR* buffer, UINT length); - - unsigned int getPosx() {return videoposx;}; - unsigned int getPosy() {return videoposy;}; - bool isVideoOn() {return videoon;}; - bool isdsinited() {return dsinited;}; - int lastAType() {return lastaudiomode;}; - bool changeAType(int type,IMediaSample* ms); - - - const VideoFilterDescList *getVideoFilterList(int &selected); - bool selectVideoFilter(int filter); - DsSourceFilter* getSourceFilter() {return sourcefilter;}; - - const VideoFilterDescList *getVideoH264FilterList(int &selected); - bool selectVideoH264Filter(int filter); - - -#ifdef DEV - int test(); - int test2(); -#endif -private: - int EnterIframePlayback(); -#ifdef NEW_DS_MECHANISMENS - void dstest(); - void initFilterDatabase(); - IBaseFilter *getVideoFilter(); - VideoFilterDescList videofilterlist; - int videofilterselected; - - void initH264FilterDatabase(); - IBaseFilter *getVideoH264Filter(); - VideoFilterDescList videoH264filterlist; - int videoH264filterselected; - bool videoH264dtsfix; - bool videompeg2dtsfix; -#endif - int dsInitVideoFilter(); - IMediaControl* dsmediacontrol; - - IGraphBuilder* dsgraphbuilder; - IMediaSample* cur_audio_media_sample; - IMediaSample* cur_video_media_sample; - IBaseFilter* dsrenderer; - IVMRSurfaceAllocatorNotify9 *dsvmrsurfnotify; - IReferenceClock *dsrefclock; - IMediaFilter* dsmediafilter; - IBasicAudio* dsbasicaudio; - REFERENCE_TIME cr_time; - - DsSourceFilter* sourcefilter; - DsAllocator* allocatorvmr; - HANDLE filtermutex; - void CleanupDS(); - bool offsetnotset; - bool offsetvideonotset; - bool offsetaudionotset; - long long startoffset; - long long lastrefvideotime; - long long lastrefaudiotime; - bool dsinited; - bool firstsynched; - bool audioon; - bool videoon; - bool iframemode; - UCHAR pseudotvsize; - REFERENCE_TIME lastreftimeRT; - ULLONG lastreftimePTS; - unsigned int videoposx; - unsigned int videoposy; - int lastaudiomode; - int audiovolume; - UCHAR aud_type; - unsigned int vmrdeinterlacing; - VideoPresenter currentpresenter; -#ifdef DS_DEBUG - DWORD graphidentifier; -#endif -}; - -#endif - - - +/* + 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 VIDEOWIN_H +#define VIDEOWIN_H + +#include +#include +#include +#include +#include +#include +#include + +#include "defines.h" +#include "video.h" + +//#define DS_DEBUG +#define NEW_DS_MECHANISMENS + +#ifdef NEW_DS_MECHANISMENS +struct VideoFilterDesc { + char * displayname; + char * friendlyname; + bool vmr9; + bool vmr9tested; +}; +using namespace std; +typedef vector VideoFilterDescList; +#endif + +class DsSourceFilter; +class DsAllocator; + +class VideoWin : public Video +{ +public: + VideoWin(); + virtual ~VideoWin(); + + int init(UCHAR format); + int shutdown(); + + int setFormat(UCHAR format); + int setConnection(UCHAR connection); + int setAspectRatio(UCHAR aspectRatio); // This one does the pin 8 scart widescreen switching + UCHAR getAspectRatio(){return aspectRatio;}; + UCHAR getMode(){return mode;}; + UCHAR getPseudoTVsize() {return pseudotvsize;}; + int setMode(UCHAR mode); + int setTVsize(UCHAR size); // Is the TV a widescreen? + int setDefaultAspect(); + int setSource(); + int setPosition(int x, int y); + int sync(); + int play(); + int dsplay(); + bool InIframemode() {return iframemode;}; + int stop(); + int dsstop(); + int pause(); + int dspause(); + int unPause(); + int dsunPause(); + int fastForward(); + int unFastForward(); + int reset(); + int dsreset(); + int blank(); + int signalOn(); + int signalOff(); + int attachFrameBuffer(); // What does this do? +// ULONG timecodeToFrameNumber(ULLONG timecode); +// ULLONG frameNumberToTimecode(ULONG framenumber); + ULLONG getCurrentTimestamp(); + + bool loadOptionsfromServer(VDR* vdr); + bool saveOptionstoServer(); + bool addOptionPagesToWTB(WTabBar *wtb); + bool addOptionsToPanes(int panenumber,Options *options,WOptionPane* pane); + bool handleOptionChanges(Option* option); + + //Writing Data to Videodevice + virtual void PrepareMediaSample(const MediaPacketList&, UINT samplepos); + virtual UINT DeliverMediaSample(UCHAR* buffer, UINT *samplepos); + UINT DeliverMediaPacket(const MediaPacket packet, const UCHAR* buffer, UINT *samplepos); + virtual bool dtsTimefix() {if (h264)return videoH264dtsfix; else return videompeg2dtsfix;} + + virtual bool supportsh264(); + + + virtual bool supportsAc3(); + + enum VideoPresenter { + VMR9, + EVR + } ; + + + +private: + MediaPacket mediapacket; +public: + + int getCurrentAudioMediaSample(IMediaSample** ms); + int DeliverAudioMediaSample(); + + int getCurrentVideoMediaSample(IMediaSample** ms); + int DeliverVideoMediaSample(); + int setAudioStreamType(UCHAR type); + + virtual long long SetStartOffset(long long curreftime, bool *rsync); + long long SetStartAudioOffset(long long curreftime, bool *rsync); + virtual void ResetTimeOffsets(); + + void SetAudioState(bool state){audioon=state;}; + void SetAudioVolume(long volume); + + void turnVideoOn(){videoon=true;}; + void turnVideoOff(){videoon=false;}; + + virtual bool displayIFrame(const UCHAR* buffer, UINT length); + + unsigned int getPosx() {return videoposx;}; + unsigned int getPosy() {return videoposy;}; + bool isVideoOn() {return videoon;}; + bool isdsinited() {return dsinited;}; + int lastAType() {return lastaudiomode;}; + bool changeAType(int type,IMediaSample* ms); + + + const VideoFilterDescList *getVideoFilterList(int &selected); + bool selectVideoFilter(int filter); + DsSourceFilter* getSourceFilter() {return sourcefilter;}; + + const VideoFilterDescList *getVideoH264FilterList(int &selected); + bool selectVideoH264Filter(int filter); + + +#ifdef DEV + int test(); + int test2(); +#endif +private: + int EnterIframePlayback(); +#ifdef NEW_DS_MECHANISMENS + void dstest(); + void initFilterDatabase(); + IBaseFilter *getVideoFilter(); + VideoFilterDescList videofilterlist; + int videofilterselected; + + void initH264FilterDatabase(); + IBaseFilter *getVideoH264Filter(); + VideoFilterDescList videoH264filterlist; + int videoH264filterselected; + bool videoH264dtsfix; + bool videompeg2dtsfix; +#endif + int dsInitVideoFilter(); + IMediaControl* dsmediacontrol; + + IGraphBuilder* dsgraphbuilder; + IMediaSample* cur_audio_media_sample; + IMediaSample* cur_video_media_sample; + IBaseFilter* dsrenderer; + IVMRSurfaceAllocatorNotify9 *dsvmrsurfnotify; + IReferenceClock *dsrefclock; + IMediaFilter* dsmediafilter; + IBasicAudio* dsbasicaudio; + REFERENCE_TIME cr_time; + + DsSourceFilter* sourcefilter; + DsAllocator* allocatorvmr; + HANDLE filtermutex; + void CleanupDS(); + bool offsetnotset; + bool offsetvideonotset; + bool offsetaudionotset; + long long startoffset; + long long lastrefvideotime; + long long lastrefaudiotime; + bool dsinited; + bool firstsynched; + bool audioon; + bool videoon; + bool iframemode; + UCHAR pseudotvsize; + REFERENCE_TIME lastreftimeRT; + ULLONG lastreftimePTS; + unsigned int videoposx; + unsigned int videoposy; + int lastaudiomode; + int audiovolume; + UCHAR aud_type; + unsigned int vmrdeinterlacing; + VideoPresenter currentpresenter; +#ifdef DS_DEBUG + DWORD graphidentifier; +#endif +}; + +#endif + + + diff --git a/vmediaview.cc b/vmediaview.cc index af6cd2c..0e9f274 100644 --- a/vmediaview.cc +++ b/vmediaview.cc @@ -1,1457 +1,1457 @@ -/* - Copyright 2004-2005 Chris Tallon, Andreas Vogel - - 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 - -#include "vmediaview.h" - -#include "vpicturebanner.h" -#include "vcolourtuner.h" -#include "audioplayer.h" -#include "timers.h" -#include "boxx.h" -#include "wselectlist.h" -#include "remote.h" -#include "wsymbol.h" -#include "boxstack.h" -#include "vdr.h" -#include "media.h" -#include "video.h" -#include "vinfo.h" -#include "i18n.h" -#include "message.h" -#include "command.h" -#include "mediaoptions.h" -#include "mediaplayer.h" -#include "log.h" - -const int VMediaView::EVENT_SLIDESHOW=100; -const int VMediaView::EVENT_DRAWINGDONE=101; -const int VMediaView::EVENT_DRAWINGERROR=102; -const int VMediaView::EVENT_DIRECTORYDONE=103; - - -/** - * the combined user interface for pictures and audio - * has 2 surfaces to enable drawing in a separate thread - * the info display can either show a picture info or and audio info - * if the audio player comes on top it disables the picture info display - * basically we have 3 modes: - * - HIDDEN viewer is hidden (pictureEnabled=false, audioEnabled=false) - * if justPlaying=true the audioplayer is still running - * - PICTURE picture viewer on top ("slide show") - pictureEnabled=true, audioEnabled=false - * no AudioBanner visible - * if justPlaying=true the audio player runs in bg - * - AUDIO audioPlayer on top ("playlist mode") - pictureEnabled=true, audioEnabled=true - * the picture viewer is currently halted (should be able to continue if no AudioInfo) - * no picture banner (we should have an audio banner to indicate the audio player on top!) - * state transitions (via setPictureMode, setAudioMode) - * - show Picture -> pictureEnabled=true, - * only called from command handler if audioEnabled=false - * call from mediaList only possible if audioEnabled=false - * *** TODO: would be better to have separate function for external call/internal call - * - play [Audio] -> if activate is set -> audioEnabled=true (-> Mode AUDIO) - * used for calls from medialist - * internal calls do not set activate! - * - YELLOW key - toggle - * if audioActive==true -> audioActive=false (new mode depends on pictureEnabled - either HIDDEN or PICTURE) - * if audioActive==false -> audioActive=true (new mode AUDIO) - * *** open handling if no audio file there (ignore key???) - currently empty player - * - BACK if mode AUDIO -> audioEnabled=false, justPlaying=false - * - BACK if mode PICTURE -> pictureEnabled=false; - * - player StreamEnd -> audioEnabled=false (new mode depends on pciture - either HIDDEN or PICTURE) - * info/banner handling: - * - AudioInfo - alternativ to pictureInfo - * off when disabling audio or after timer or with OK in AUDIO mode - * on currently any command or player event (end/new song) when audioEnabled==true and retriggerAudioInfo=true - * on on OK in AUDIO mode - * - Picture Info - * off when disabling Picture or after timer, OK, green - * on only on green when pictureEnabled==true - * - PictureBanner - * 2 modes: loading/normal - * normal: - * off when disabling picture, OK, after timer - * on when enabling picture, with OK - * loading: - * off when disabling picture and when loading done - * on on start loading - * *** open: do not show when audio enabled and slide show running (currently slideshow is paused) - * - AudioBanner - * always shown when audioEnabled=true - * on when enabling Audio - * off when disabling audio - * update in play - * timers: 1 - slide show - * 2 - pictureBanner - * 3 - info showtime - * 4 - audio short update - */ - -#define DRAWING_THREAD - -Colour VMediaView::pictureBack=Colour(140,140,140); -Colour VMediaView::infoBack=Colour(110,110,110); -Colour VMediaView::audioBannerBack=Colour(110,110,110,20); -//the jpeg reader - -//how long do we wait for a single picture chunk -//the unit is 100ms (the timeout on the server side) -#define MAXTRY 100 -class VPreader : public JpegReader { - private: - ImageReader *reader; - VMediaView * parent; - ULLONG size; - bool dobreak;; - public: - VPreader(VMediaView *p,ImageReader *r){ - parent=p; - size=0; - reader=r; - dobreak=false; - }; - virtual ULONG readChunk(ULONG offset,ULONG len,char ** buf) { - Log::getInstance()->log("VMediaView::jpegReader", Log::DEBUG, "read chunk o=%d,len=%d,buf=%p", - offset,len,*buf); - UINT numrec=0; - int rt=0; - for (int trycount=0;trycount < MAXTRY && ! dobreak && numrec == 0 && rt == 0;trycount++) { - if (dobreak) { - *buf=NULL; - rt=-1; - } - else { - rt=reader->getImageChunk((ULLONG)offset,(UINT)len,&numrec,(UCHAR **)buf); - } - } - Log::getInstance()->log("VMediaView::jpegReader", Log::DEBUG, "got n=%d,buf=%p,rt=%d", - numrec,*buf,rt); - return numrec; - } - virtual int initRead(const MediaURI *uri,ULLONG *sz,ULONG factor=100) { - Log::getInstance()->log("VMediaView::jpegReader", Log::DEBUG, "load image %s",uri->getDisplayName()); - Video* video = Video::getInstance(); - dobreak=false; - int rt=MediaPlayer::getInstance()->openMedium(1,uri,sz,video->getScreenWidth()*factor/100, video->getScreenHeight()*factor/100); - if (rt < 0) *sz=0; - size=*sz; - Log::getInstance()->log("VMediaView::jpegReader", Log::DEBUG, "load image %s returned %llu",uri->getDisplayName(),size); - return rt; - } - virtual ULONG getSize() {return size;} - //seems to be thread safe (more or less...) - void setBreak() { - dobreak=true; - } -}; - -/** - * a separate thread for drawing pictures - * will be started with the sfc and the draw control - * will send a player event when done - */ -class DrawingThread : public Thread_TYPE { - private: - VMediaView *_parent; - VPreader * _reader; - WJpeg::JpegControl *_ctl; - Surface *_sfc; - Colour _colour; - bool _interrupted; - public: - DrawingThread(VMediaView *parent) { - _parent=parent; - _reader=NULL; - _ctl=NULL; - _sfc=NULL; - _interrupted=false; - } - virtual ~DrawingThread(){} - bool isRunning() { - return (threadIsActive()!=0); - } - bool start(WJpeg::JpegControl *ctl,Surface *sfc,VPreader *reader,Colour &c) { - if (isRunning()) { - return false; - } - _interrupted=false; - _ctl=ctl; - _sfc=sfc; - _reader=reader; - _colour=c; - return (threadStart() == 1); - } - bool stop() { - Log::getInstance()->log("DrawingThread",Log::DEBUG,"stop initiated"); - if (threadIsActive()) { - _interrupted=true; - if (_reader) _reader->setBreak(); - threadStop(); - } - Log::getInstance()->log("DrawingThread",Log::DEBUG,"stop done"); - _reader=NULL; - return true; - } - protected: - virtual void threadMethod() { - Log::getInstance()->log("DrawingThread",Log::DEBUG,"started"); - bool rt=WJpeg::drawJpeg(_ctl,_sfc,_reader,_colour); - _reader=NULL; - if (! _interrupted) { - Message* m = new Message(); - //we misuse PLAYER_EVENT here - m->message = Message::PLAYER_EVENT; - m->to = _parent; - m->from = _parent; - m->parameter= rt?VMediaView::EVENT_DRAWINGDONE:VMediaView::EVENT_DRAWINGERROR; - Command::getInstance()->postMessageFromOuterSpace(m); - } - Log::getInstance()->log("DrawingThread",Log::DEBUG,"finishing interrupt=%d",(int)_interrupted); - } - virtual void threadPostStopCleanup() {} - -}; - - -VMediaView::VMediaView(VMediaList *p) -{ - Log::getInstance()->log("VMediaView::VMediaView", Log::DEBUG, "p=%p", this); - audioEnabled=false; - pictureEnabled=false; - parent=p; - ireader=new ImageReader(1,MediaPlayer::getInstance()); - reader=new VPreader(this,ireader); - //the surface handling - Video* video = Video::getInstance(); - setSize(video->getScreenWidth(), video->getScreenHeight()); - createBuffer(); - sfc2=getSurface(); - surface=NULL; - //create the second surface - createBuffer(); - sfc1=getSurface(); - originalh=area.h; - originalw=area.w; - //disable the surface - area.h=1; - area.w=1; - //banners and infos - pictureBanner=NULL; - slideshow=false; - pictureError=NULL; - currentPicture=NULL; - info=NULL; - pictureLoading=false; - - //picture settings - showtime=INITIAL_SHOWTIME; - rotate=WJpeg::ROT_0; - currentScale=1; - options=MediaOptions::getInstance(); - VColourTuner::initFactors(); - int st=options->getIntOption("SlideShowInterval"); - if (st > 0) showtime=st; - ctl.area.w=originalw; - ctl.area.h=originalh; - ctl.enlarge=false; - ctl.scaleafter=options->getIntOption("ScaleFactor"); - const char * mode=options->getStringOption("PictureMode"); - if (strcmp(mode,"clip") == 0) ctl.mode=WJpeg::CROP; - else if (strcmp(mode,"letter") == 0) ctl.mode=WJpeg::LETTER; - else if (strcmp(mode,"clipfactor") == 0) ctl.mode=WJpeg::CROPPERCENT; - ctl.scaleAmount=options->getIntOption("PictureSize"); - if (ctl.scaleAmount < 10) ctl.scaleAmount=10; - if (ctl.scaleAmount > 200) ctl.scaleAmount=200; - ctl2=ctl; - cropmode=ctl.mode; - //current control is the one used for DISPLAY (not the one for drawing - this is the other one) - //this is closely coupled to the surface - i.e. ctl is for sfc1, ctl2 for sfc2 - currentControl=&ctl; - bannerEnabled=true; - pictureShowing=false; - - //audio player - - barBlue.set(0, 0, 150, 150); - audioError=NULL; - currentAudio=NULL; - justPlaying=false; - playall=false; - retriggerAudioInfo=false; - audioBanner=NULL; - drawingThread=new DrawingThread(this); -} - -VMediaView::~VMediaView() -{ - Log::getInstance()->log("VMediaView::~VMediaView", Log::DEBUG, "p=%p,secondSfc=%s", this,(secondSurface()?"true":"false")); - destroyPictureBanner(); - if (currentPicture) delete currentPicture; - Timers::getInstance()->cancelTimer(this,1); - Timers::getInstance()->cancelTimer(this,2); - Timers::getInstance()->cancelTimer(this,3); - Timers::getInstance()->cancelTimer(this,4); - Timers::getInstance()->cancelTimer(this,5); - destroyInfo(); - destroyAudioBanner(); - if (getPlayer(false)) { - AudioPlayer::getInstance(NULL,false)->shutdown(); - } - if (currentAudio) delete currentAudio; - drawingThread->stop(); - ireader->shutdown(); - delete ireader; - delete reader; - if (secondSurface()) { - delete sfc1; - sfc1=NULL; - } - else { - delete sfc2; - sfc2=NULL; - } - MediaPlayer::getInstance()->closeMediaChannel(1); - MediaPlayer::getInstance()->closeMediaChannel(2); - Log::getInstance()->log("VMediaView::~VMediaView", Log::DEBUG, "done p=%p", this); -} - -void VMediaView::setPictureMode(bool act) { - Log::getInstance()->log("VMediaView", Log::DEBUG, "set pictureMode %d p=%d, a=%d", (int)act,(int)pictureEnabled,(int)audioEnabled); - if ( act) { - if ( ! pictureEnabled && ! audioEnabled) { - area.h=originalh; - area.w=originalw; - showPictureBanner(); - parent->updateAll(); - } - if (! pictureEnabled) { - //we newly enable the picture - so clear the screen - draw(); - BoxStack::getInstance()->update(this); - if (slideshow) startSlideshow(); - } - } - else - { - if ( pictureEnabled) { - destroyPictureBanner(); - stopSlideshow(false); -#ifdef DRAWING_THREAD - drawingThread->stop(); -#endif - if (! audioEnabled) { - destroyInfo(); - area.w=1; - area.h=1; - draw(); - parent->updateAll(); - } - } - pictureShowing=false; - } - pictureEnabled=act; -} - -//we can disable audio without automatically hiding us -//this will become strange - we are visible but do not handle -//keys - so if you call setAudioMode(false,false) be sure -//to call setAudioMode(false,true) afterwards -//it is only here to give the list a chance to start a new play -//when we call directoryDone -void VMediaView::setAudioMode(bool act,bool doHiding) { - Log::getInstance()->log("VMediaView", Log::DEBUG, "setAudioMode %d p=%d, a=%d", (int)act,(int)pictureEnabled,(int)audioEnabled); - if ( act) { - if (! audioEnabled) { - if (! pictureEnabled) { - area.w=originalw; - area.h=originalh; - draw(); //empty screen if no picture - parent->updateAll(); - } - destroyPictureBanner(); - - } - audioEnabled=true; - showAudioBanner(); - showAudioInfo(); - } - else - { - if ( audioEnabled) { - audioEnabled=false; - destroyInfo(); - destroyAudioBanner(); - } - if (! pictureEnabled && doHiding) { - area.w=1; - area.h=1; - draw(); - parent->updateAll(); - } - else { - //picture now on top - if (havePictureBanner && ! pictureBanner) showPictureBanner(); - } - } -} - - - -void VMediaView::draw() -{ - Log::getInstance()->log("VMediaView::draw", Log::DEBUG, "pictureError=%s,p=%p", pictureError,this); - - if (pictureShowing ) fillColour(pictureBack); - else fillColour(Colour::BLACK); - if (pictureError) { - drawText(pictureError,100,area.h/2,Colour::LIGHTTEXT); - return; - } -} - - -int VMediaView::handleCommand(int command) -{ - Log::getInstance()->log("VMediaView::handleCommand", Log::DEBUG, "cmd=%d,p=%p", command,this); - if ( !audioEnabled && ! pictureEnabled ) { - //only handle YELLOW - if (command == Remote::YELLOW) { - setAudioMode(true); - return 1; - } - return 0; - } - if ( ! audioEnabled) { - //------------------------- command in mode PICTURE (i.e. picture is on top) ---------------- - //picture on top - int rt=1; - switch(command) - { - case Remote::DF_UP: - case Remote::UP: - case Remote::SKIPBACK: - rotate=WJpeg::ROT_0; - showPicture(VMediaList::MV_PREV,slideshow,true); - rt= 2; - break; - case Remote::FORWARD: - if (showtime > 1) showtime--; - updatePictureBanner(true); - break; - case Remote::DF_DOWN: - case Remote::DOWN: - case Remote::SKIPFORWARD: - rotate=WJpeg::ROT_0; - showPicture(VMediaList::MV_NEXT,slideshow,true); - rt= 2; - break; - case Remote::REVERSE: - if (showtime < 50 ) showtime++; - updatePictureBanner(true); - break; - case Remote::OK: - { - if (pictureBanner) { - destroyPictureBanner(); - destroyInfo(); - havePictureBanner=false; - } - else { - havePictureBanner=true; - showPictureBanner(pictureLoading); - } - rt= 2; - } - break; - case Remote::PLAY: - { - slideshow=true; - rotate=WJpeg::ROT_0; - showPicture(VMediaList::MV_NEXT,slideshow,true); - rt= 2; - } - break; - case Remote::PAUSE: - if (slideshow) { - stopSlideshow(true); - updatePictureBanner(); - } - else { - slideshow=true; - rotate=WJpeg::ROT_0; - showPicture(VMediaList::MV_NEXT,slideshow,true); - } - rt= 2; - break; - case Remote::STOP: - stopSlideshow(true); - showtime=INITIAL_SHOWTIME; - updatePictureBanner(); - rt= 2; - break; - case Remote::RED: - switch(rotate) { - case WJpeg::ROT_0: - rotate=WJpeg::ROT_90; - break; - case WJpeg::ROT_90: - rotate=WJpeg::ROT_180; - break; - case WJpeg::ROT_180: - rotate=WJpeg::ROT_270; - break; - case WJpeg::ROT_270: - rotate=WJpeg::ROT_0; - break; - } - showPicture(VMediaList::MV_NONE,slideshow,true); - rt=2; - break; - case Remote::GREEN: - if (info) destroyInfo(); - else showPictureInfo(); - rt=2; - break; - case Remote::BLUE: - switch (cropmode) { - case WJpeg::CROP: - cropmode=WJpeg::LETTER; - break; - case WJpeg::LETTER: - cropmode=WJpeg::CROPPERCENT; - break; - default: - cropmode=WJpeg::CROP; - break; - } - showPicture(VMediaList::MV_NONE,slideshow,true); - rt=2; - break; - case Remote::MENU: { - stopSlideshow(true); - destroyPictureBanner(); - destroyInfo(); - VColourTuner *ct=new VColourTuner(); - BoxStack::getInstance()->add(ct); - ct->draw(); - BoxStack::getInstance()->update(ct); - rt=2; - - } break; - case Remote::BACK: - { - setPictureMode(false); - rt= 2; - } - break; - case Remote::YELLOW: - { - setAudioMode(true); - } - } - return rt; - } - else - { - int rt=1; - bool updateInfo=false; - //------------------------- command in mode AUDIO (i.e. audio is on top) ---------------- - switch(command) - { - case Remote::YELLOW: - setAudioMode(false); - rt=2; - break; - case Remote::DF_UP: - case Remote::UP: - play(playall,false,VMediaList::MV_PREV); - rt= 2; - break; - case Remote::FORWARD: - if (! audioError) getPlayer()->fastForward(); - updateInfo=true; - rt=2; - break; - case Remote::DF_DOWN: - case Remote::DOWN: - play(playall,false,VMediaList::MV_NEXT); - rt= 2; - break; - case Remote::SKIPFORWARD: - if (! audioError) getPlayer()->skipForward(10); - rt=2; - break; - case Remote::SKIPBACK: - if (! audioError) getPlayer()->skipBackward(10); - rt=2; - break; - case Remote::REVERSE: - rt=2; - break; - case Remote::ZERO: - if (! audioError) getPlayer()->jumpToPercent(0); - rt=2; - break; - case Remote::ONE: - if (! audioError) getPlayer()->jumpToPercent(10); - rt=2; - break; - case Remote::TWO: - if (! audioError) getPlayer()->jumpToPercent(20); - rt=2; - break; - case Remote::THREE: - if (! audioError) getPlayer()->jumpToPercent(30); - rt=2; - break; - case Remote::FOUR: - if (! audioError) getPlayer()->jumpToPercent(40); - rt=2; - break; - case Remote::FIVE: - if (! audioError) getPlayer()->jumpToPercent(50); - rt=2; - break; - case Remote::SIX: - if (! audioError) getPlayer()->jumpToPercent(60); - rt=2; - break; - case Remote::SEVEN: - if (! audioError) getPlayer()->jumpToPercent(70); - rt=2; - break; - case Remote::EIGHT: - if (! audioError) getPlayer()->jumpToPercent(80); - rt=2; - break; - case Remote::NINE: - if (! audioError) getPlayer()->jumpToPercent(90); - rt=2; - break; - case Remote::OK: - case Remote::GREEN: - { - if (info) { - destroyInfo(); - retriggerAudioInfo=false; - } - else { - retriggerAudioInfo=true; - showAudioInfo(); - } - if (getPlayer()->getState() == AudioPlayer::S_ERROR) { - if (playall) play(playall,false,VMediaList::MV_NEXT); - } - rt= 2; - } - break; - case Remote::PLAY: - { - if (! audioError) getPlayer()->unpause(); - updateInfo=true; - if (getPlayer()->getState() != AudioPlayer::S_ERROR) ; - else if (playall) play(playall,false,VMediaList::MV_NEXT); - rt= 2; - } - break; - case Remote::PAUSE: - if (! audioError) getPlayer()->pause(); - updateInfo=true; - rt= 2; - break; - case Remote::STOP: - getPlayer()->stop(); - justPlaying=false; - updateInfo=true; - rt= 2; - break; - case Remote::BACK: - { - getPlayer()->stop(); - playall=false; - retriggerAudioInfo=false; - justPlaying=false; - setAudioMode(false); - if (! pictureShowing) setPictureMode(false); //could have been delayed - rt= 2; - } - break; - } - if (audioEnabled && updateInfo) updateAudioInfo(); - return rt; - } -} - -void VMediaView::processMessage(Message* m) -{ - Log::getInstance()->log("VMediaView::processMessage", Log::DEBUG, "cmd=%lu,p=%lu", m->message,m->parameter); - if (m->message == Message::MOUSE_MOVE) - { - ; - } - else if (m->message == Message::MOUSE_LBDOWN) - { - - //check if press is outside this view! then simulate cancel - int x=(m->parameter>>16)-getScreenX(); - int y=(m->parameter&0xFFFF)-getScreenY(); - if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight()) - { - BoxStack::getInstance()->handleCommand(Remote::BACK); //simulate cancel press - } - } - else if (m->message = Message::PLAYER_EVENT) { - switch (m->parameter) { - case EVENT_SLIDESHOW: - if (! pictureEnabled) break; //old timer msg... - //if (! audioEnabled) { - if (true) { - if (slideshow) { - rotate=WJpeg::ROT_0; - showPicture(VMediaList::MV_NEXT,true,false); - startSlideshow(); - } - } - break; - case EVENT_DRAWINGERROR: - drawingDone(true); - break; - case EVENT_DRAWINGDONE: - drawingDone(false); - break; - case EVENT_DIRECTORYDONE: - //just disable audio without (poetntially) hiding us - //this gives the list a chance to decide whether audio - //should be on top afterwards without showing the list - //immediately - setAudioMode(false,false); - parent->directoryDone(); - if (! pictureShowing) setPictureMode(false); - if (! justPlaying) setAudioMode(false); - break; - case AudioPlayer::STREAM_ERR: - case AudioPlayer::STREAM_END: - if (playall) play(playall,false,VMediaList::MV_NEXT); - else { - setAudioMode(false); - justPlaying=false; - } - break; - case AudioPlayer::SHORT_UPDATE: - if (info && audioEnabled ) { - drawAudioClocks(); - BoxStack::getInstance()->update(info,&clocksRegion); - BoxStack::getInstance()->update(info,&barRegion); - Timers::getInstance()->setTimerD(this, 4, 1); - } - break; - case AudioPlayer::NEW_SONG: - if (audioEnabled) updateAudioInfo(); - break; - case AudioPlayer::CONNECTION_LOST: - if (audioEnabled) destroyInfo(); - if (AudioPlayer *player=getPlayer(false)) { - player->shutdown(); - player=NULL; - } - Command::getInstance()->connectionLost(); - break; - } - } -} - - -VMediaView * VMediaView::createViewer(VMediaList * mparent) { - Log::getInstance()->log("VMediaView::createViewer", Log::DEBUG, "p=%p", - mparent); - VMediaView *vmn=new VMediaView(mparent); - BoxStack::getInstance()->add(vmn); - vmn->draw(); - BoxStack::getInstance()->update(vmn); - return vmn; -} - -void VMediaView::startSlideshow() { - slideshow=true; - if (! pictureLoading) Timers::getInstance()->setTimerD(this,1,showtime); -} - -void VMediaView::stopSlideshow(bool hard) { - if (hard) slideshow=false; - Timers::getInstance()->cancelTimer(this,1); -} - - -void VMediaView::showPicture(ULONG move,bool bslideshow,bool activateBanner) { - pictureShowing=true; - setPictureMode(true); - stopSlideshow(true); -#ifdef DRAWING_THREAD - drawingThread->stop(); -#endif - slideshow=bslideshow; - Media *newPicture=parent->getMedia(MEDIA_TYPE_PICTURE,move); - if ( ! newPicture) { - pictureShowing=false; - if (!audioEnabled) { - //within the event handler first directoryDone is called - //and afterwards everything is stopped if nothing new - sendCommandMsg(EVENT_DIRECTORYDONE); - } - slideshow=false; - } - else - { - pictureShowing=true; - if (currentPicture) { - delete currentPicture; - currentPicture=NULL; - } - currentPicture=newPicture; - loadPicture(currentPicture,activateBanner); - } -} - -//the real picture drawing method -//will start drawing on a new surface and will switch it wehn done - -int VMediaView::loadPicture(Media *md,bool activateBanner) { - pictureError=NULL; - if (! md) return 1; - if (! md->getURI()) { - pictureError=tr("No media found"); - Log::getInstance()->log("VMediaView::load",Log::ERR,"no URI in media"); - return 1; - } - Log::getInstance()->log("VMediaView::load", Log::DEBUG, "filename=%s,p=%p", - md->getURI()->getName(),this); - //do we have a pictureBanner? - havePictureBanner=pictureBanner!=NULL || activateBanner; - pictureLoading=true; -#ifdef DRAWING_THREAD - if (!audioEnabled && havePictureBanner ) showPictureBanner(true); - drawingThread->stop(); -#else - showPictureBanner(true); -#endif - ireader->stop(); - ULLONG size=0; - int rtok=reader->initRead(md->getURI(),&size,ctl.scaleAmount); //scaleAmount is the same for both... - if (rtok == 0) { - //now we can really draw - //get the surface for drawing - Surface * drawSurface=NULL; - WJpeg::JpegControl *drawCtl=NULL; - getDrawingParam(drawSurface,drawCtl); - drawCtl->error[0]=0; - drawCtl->rotation=rotate; - drawCtl->mode=cropmode; - drawCtl->compressedSize=size; -#ifdef DRAWING_THREAD - bool ok=drawingThread->start(drawCtl,drawSurface,reader,pictureBack); - if (! ok) { - Log::getInstance()->log("VMediaView::load", Log::ERR, "unable to start drawing thread"); - pictureError=tr("JpegError"); - pictureLoading=false; - destroyPictureBanner(); - return 1; - } - return 0; -#else - //here we could hand this over to the drawing thread - bool ok=WJpeg::drawJpeg(drawCtl,drawSurface,reader,pictureBack); - drawingDone(!ok); - return ok?0:1; -#endif - } - pictureLoading=false; - return 1; -} - -void VMediaView::drawingDone(bool hasError) { - pictureLoading=false; - destroyPictureBanner(); //disable loading indication (or real banner) - if (! hasError) { - switchSurface(); - Log::getInstance()->log("VMediaView::drawingDone", Log::DEBUG, "success: sfc now=%p",surface); - pictureError=NULL; - //only show the pictureBanner if it was there before - if (havePictureBanner && ! audioEnabled) showPictureBanner(); - Log::getInstance()->log("VMediaView::load", Log::DEBUG, "success" ); - updatePictureInfo(); //will only do somethng if pictureEnabled - } - else - { - pictureError=tr("JpegError"); - if (pictureEnabled) { - draw(); - destroyInfo(); - } - } - MediaPlayer::getInstance()->closeMediaChannel(1); -#ifndef DRAWING_THREAD - if (audioEnabled) showAudioBanner(); -#endif - if (slideshow) startSlideshow(); - BoxStack::getInstance()->update(this); -} - -void VMediaView::showPictureBanner(bool loading) { - //we are in the main thread - so we can (and must) safely hard destroy/create the banner - Timers::getInstance()->cancelTimer(this,2); - if (! currentPicture) { - //hmm... - destroyPictureBanner(false); - return; - } - if (pictureBanner) destroyPictureBanner(false); - if (! pictureEnabled || ! bannerEnabled) return; - pictureBanner= new VPictureBanner(loading, slideshow); - pictureBanner->fillColour(infoBack); - if (! loading) { - int len=strlen(currentPicture->getFileName())+Media::TIMEBUFLEN+20; - char *buf=new char[len]; - char tbuf[Media::TIMEBUFLEN]; - SNPRINTF(buf,len,"%c%02ds%c %s %s " , - slideshow?' ':'[', - showtime, - slideshow?' ':']', - currentPicture->getTimeString(tbuf), - currentPicture->getFileName() - ); - pictureBanner->setText(buf); - delete [] buf; - } - else { - char *buf=new char[strlen(currentPicture->getDisplayName())+50]; - SNPRINTF(buf,50,"%s %s",tr("Loading"), currentPicture->getDisplayName()); - pictureBanner->setText(buf); - delete buf; - } - pictureBanner->draw(); - if (! loading ) Timers::getInstance()->setTimerD(this,2,8); - BoxStack::getInstance()->add(pictureBanner); - BoxStack::getInstance()->update(pictureBanner); - } - -void VMediaView::destroyPictureBanner(bool fromTimer) { - if (pictureBanner) { - if (fromTimer) sendViewMsg(pictureBanner); - else BoxStack::getInstance()->remove(pictureBanner); - pictureBanner=NULL; - if (! fromTimer) Timers::getInstance()->cancelTimer(this,2); - } -} -void VMediaView::updatePictureBanner(bool loading) { - if (pictureBanner ) { - showPictureBanner(loading); - } -} -void VMediaView::timercall(int clientref) { - Log::getInstance()->log("VMediaView::timercall", Log::DEBUG, "id=%d",clientref); - switch(clientref) - { - case 4: - { - sendCommandMsg(AudioPlayer::SHORT_UPDATE); - break; - } - case 3: - { - Log::getInstance()->log("VMediaView::timercall", Log::DEBUG, "infoEnd"); - destroyInfo(true); - if (audioEnabled) { - //we only did show the audio error info if audio is enabled - bool stillError=false; - if (AudioPlayer * player=getPlayer(false)) { - stillError=player->getState()==AudioPlayer::S_ERROR; - } - if (stillError) { - sendCommandMsg(AudioPlayer::STREAM_END); - } - } - break; - } - case 1: - if (! slideshow) return; - Log::getInstance()->log("VMediaView::timercall", Log::DEBUG, "slideshow"); - sendCommandMsg(EVENT_SLIDESHOW); - break; - case 2: - Log::getInstance()->log("VMediaView::timercall", Log::DEBUG, "pictureBannerEnd"); - destroyPictureBanner(true); - break; - } - - } - -#define INFOBUF 2000 -void VMediaView::showPictureInfo(){ - if (! pictureEnabled || audioEnabled) return; - if (info) destroyInfo(); - if (! currentPicture) return; - - info=new VInfo(); - info->setTitleText(currentPicture->getFileName()); - info->setDropThrough(); - info->setSize(500, 300); - info->createBuffer(); - info->setBorderOn(1); - info->setTitleBarOn(1); - - if (Video::getInstance()->getFormat() == Video::PAL) - info->setPosition(100, 180); - else - info->setPosition(100, 150); - char buf[INFOBUF]; - char tbuf[Media::TIMEBUFLEN]; - //modes should come from mediaoptions... - const char *mode=NULL; - switch (currentControl->mode) { - case WJpeg::CROPPERCENT: - mode="clipfactor"; - break; - case WJpeg::LETTER: - mode="letter"; - break; - default: - mode="clip"; - break; - } - const char *dirname=parent->getDirname(MEDIA_TYPE_PICTURE); - int ldir=0; - const char *prfx=""; - if (dirname) ldir=strlen(dirname); - if (ldir > 35){ - dirname=dirname+ldir-35; - prfx="..."; - } - SNPRINTF(buf,INFOBUF,"%s= %s%s\n%s= %u x %u\n%s= %lu kBytes\n%s= %s\n%s= %u\n%s= %u%%\n%s= %s", - tr("Directory"), prfx,dirname, - tr("Format(px)"),currentControl->picw,currentControl->pich, - tr("Filesize"),currentControl->compressedSize/1000, - tr("Time"),currentPicture->getTimeString(tbuf), - tr("Rotation"),90*currentControl->finalRotation, - tr("Scale"),currentControl->scale, - tr("Picture Mode"),mode ); - info->setMainText(buf); - info->draw(); - BoxStack::getInstance()->add(info); - BoxStack::getInstance()->update(info); - Timers::getInstance()->setTimerD(this,3,8); -} -void VMediaView::updatePictureInfo(){ - if (info) { - showPictureInfo(); - } -} -void VMediaView::destroyInfo(bool fromTimer){ - if (info) { - if (! fromTimer) { - Log::getInstance()->log("VMediaView",Log::DEBUG,"remove info %p",info); - BoxStack::getInstance()->remove(info); - } - else { - Log::getInstance()->log("VMediaView",Log::DEBUG,"trigger remove info %p",info); - sendViewMsg(info); - } - info=NULL; - } - if (! fromTimer) Timers::getInstance()->cancelTimer(this,3); - if (! fromTimer) Timers::getInstance()->cancelTimer(this,4); -} - -void VMediaView::sendViewMsg(Boxx *v) { - Message* m = new Message(); - m->message = Message::CLOSE_ME; - m->to = BoxStack::getInstance(); - m->from = v; - m->parameter=(ULONG)v; - Command::getInstance()->postMessageFromOuterSpace(m); -} -void VMediaView::sendCommandMsg(int command) { - Message* m = new Message(); - //we misuse PLAYER_EVENT here - m->message = Message::PLAYER_EVENT; - m->to = this; - m->from = this; - m->parameter= command; - Command::getInstance()->postMessageFromOuterSpace(m); -} - -void VMediaView::enableBanner(bool enable) { - bannerEnabled=false; - updatePictureBanner(); -} - -void VMediaView::getDrawingParam(Surface *&sfc,WJpeg::JpegControl *&c){ - if (secondSurface()) { - //we currently display on sfc2 - sfc=sfc1; - c=&ctl; - } - else { - sfc=sfc2; - c=&ctl2; - } -} -void VMediaView::switchSurface(){ - if (secondSurface()) { - //now we switch to sfc1 - surface=sfc1; - currentControl=&ctl; - } - else { - surface=sfc2; - currentControl=&ctl2; - } -} - -AudioPlayer * VMediaView::getPlayer(bool createIfNeeded) -{ - AudioPlayer* rt=AudioPlayer::getInstance(this,false); - if (! createIfNeeded && rt == NULL) return NULL; - if (rt == NULL) { - rt=AudioPlayer::getInstance(this); - rt->run(); - } - return rt; -} - -bool VMediaView::isAudioPlaying() { - Log::getInstance()->log("VMediaView::isPlaying", Log::DEBUG, "rt=%s", justPlaying?"true":"false"); - return justPlaying; -} - - - - - - -int VMediaView::play(bool all,bool activate,ULONG move,bool showInfo) { - int rt=0; - if (getPlayer(false)) getPlayer(false)->stop(); - justPlaying=false; - if (currentAudio) delete currentAudio; - currentAudio=NULL; - playall=all; - currentAudio=parent->getMedia(MEDIA_TYPE_AUDIO,move); - audioError=NULL; - if ( ! currentAudio || ! currentAudio->getURI()) { - Log::getInstance()->log("VMediaView::load", Log::ERR, "no URI in media"); - audioError=tr("no audio file"); - if (audioEnabled) sendCommandMsg(EVENT_DIRECTORYDONE); - rt= -1; - } - else { - Log::getInstance()->log("VMediaView::load", Log::DEBUG, "filename=%s,p=%p", - currentAudio->getURI()->getName(),this); - int wseq=getPlayer()->play(currentAudio->getURI()); - if (getPlayer()->waitForSequence(5,wseq)<0) { - audioError=tr("unable to open audio file"); - rt= -1; - } - else { - justPlaying=true; - rt=0; - } - Log::getInstance()->log("VMediaView", Log::DEBUG, "player started for %s",currentAudio->getURI()->getName()); - } - if (activate && ! audioEnabled){ - if (showInfo) retriggerAudioInfo=true; - setAudioMode(true); - } - else { - //avoid duplicate creation of banner and info - showAudioBanner(); - showAudioInfo(); - } - if (activate && (! currentPicture || ! pictureShowing)) draw(); - BoxStack::getInstance()->update(this); - return rt; -} - -void VMediaView::showAudioInfo() { - if (! audioEnabled) return; - Timers::getInstance()->cancelTimer(this,4); - Timers::getInstance()->cancelTimer(this,3); - if (info) destroyInfo(); - if (! retriggerAudioInfo) return; - drawAudioInfo(); - bool playerError=getPlayer()->getState()==AudioPlayer::S_ERROR || audioError; - if (! playerError) Timers::getInstance()->setTimerD(this,3,AUDIOBANNER_TIME); - else Timers::getInstance()->setTimerD(this,3,AUDIOERROR_TIME); - if (! playerError) Timers::getInstance()->setTimerD(this,4, 1); - BoxStack::getInstance()->update(info); - } - -void VMediaView::updateAudioInfo() { - if (info) { - showAudioInfo(); - } -} - -void VMediaView::drawAudioInfo(){ - Log::getInstance()->log("VMediaView",Log::DEBUG, "draw banner for %p",info); - const char *title=NULL; - char *playerTitle=NULL; - bool playerError=false; - int numlines=0; - int len=0; - int num=0; - const char *pl=tr("Playlist"); - const char *first=NULL; - char *playerInfo=NULL; - if (audioError) { - if (getPlayer()->getState() == AudioPlayer::S_PLAY) audioError=NULL; - } - if (! currentAudio && ! audioError) audioError=tr("no audio file"); - if (audioError ) { - title=tr("MediaError"); - } - else { - playerTitle=getPlayer()->getTitle(); - if (playerTitle) title=playerTitle; - else title=currentAudio->getDisplayName(); - num=parent->getNumEntries(MEDIA_TYPE_AUDIO,currentAudio->index); - playerError=getPlayer()->getState() == AudioPlayer::S_ERROR; - //1more line for long dirs - numlines=playall?5:4; - } - if (playerError) { - numlines=3; - first=tr("Unable to play audio file"); - len=strlen(first)+3; - } - else if (audioError) { - numlines=3; - first=audioError; - len=strlen(first)+3; - } - else { - playerInfo=getPlayer()->getID3Info();drawText(tr("Loading"),5,3,Colour::LIGHTTEXT); - len=strlen(currentAudio->getDisplayName())+strlen(pl)+30+strlen(parent->getDirname(MEDIA_TYPE_AUDIO))+Media::TIMEBUFLEN+110; - if (playerInfo) { - for (UINT i=0;igetScreenHeight(); - UINT vwidth=Video::getInstance()->getScreenWidth(); - if (height > vheight-2*AUDIOBANNER_BOTTOM_MARGIN) - height=vheight-2*AUDIOBANNER_BOTTOM_MARGIN; - info->setSize(vwidth -2*AUDIOBANNER_X_MARGIN, height); - info->createBuffer(); - if (Video::getInstance()->getFormat() == Video::PAL) - { - info->setPosition(AUDIOBANNER_X_MARGIN, vheight-height-AUDIOBANNER_BOTTOM_MARGIN); - } - else - { - info->setPosition(AUDIOBANNER_X_MARGIN, vheight-height-AUDIOBANNER_BOTTOM_MARGIN); - } - - info->setTitleBarOn(0); - info->setDropThrough(); - //from vvideorec - //set the regions for the closcks and bars on banner - barRegion.x = info->getWidth()-AUDIOBARLEN-20; - barRegion.y = info->getHeight() - 30; // FIXME, need to be - 1? and below? - barRegion.w = info->getWidth()-AUDIOBARLEN+10; - barRegion.h = 30; - - clocksRegion.x = 130; - clocksRegion.y = barRegion.y + 3; - clocksRegion.w = 190; - clocksRegion.h = surface->getFontHeight(); - Log::getInstance()->log("VMediaView",Log::DEBUG,"created AudioInfo %p",info); - BoxStack::getInstance()->add(info); - char *buf=new char [len]; - if (playerError || audioError) { - SNPRINTF(buf,len,"%s",first); - } - else { - char tbuf[Media::TIMEBUFLEN]; - if (playall) { - SNPRINTF(buf,len,"%s\n" - "%s:%s\n" - "%s: %d/%d\n" - "%s:%s\n" - "%s:%s\n", - first, - tr("FileName"),currentAudio->getDisplayName(), - pl,num,parent->getNumEntries(MEDIA_TYPE_AUDIO), - tr("Directory"),parent->getDirname(MEDIA_TYPE_AUDIO), - tr("Time"),currentAudio->getTimeString(tbuf)); - } - else { - SNPRINTF(buf,len,"%s\n" - "%s:%s\n" - "%s:%s\n" - "%s:%s\n", - first, - tr("FileName"),currentAudio->getDisplayName(), - tr("Directory"),parent->getDirname(MEDIA_TYPE_AUDIO), - tr("Time"),currentAudio->getTimeString(tbuf)); - } - } - Log::getInstance()->log("VMediaView",Log::DEBUG,"info: (%d)%s",strlen(buf),buf); - //now the real drawing functions - info->draw(); - //title - info->rectangle(0, 0, info->getWidth(), 30, Colour::TITLEBARBACKGROUND); - info->drawText(title, 5, 5, Colour::LIGHTTEXT); - info->drawPara(buf,5,32,Colour::LIGHTTEXT); - delete [] buf; - if (! audioError) { - WSymbol w; - info->TEMPADD(&w); - int x=10; - int ybottom=info->getHeight(); - info->rectangle(0, ybottom - barRegion.h, info->getWidth(), barRegion.h, Colour::TITLEBARBACKGROUND); - bool drawSymbol=true; - switch(getPlayer()->getState()) { - case AudioPlayer::S_PAUSE: - w.nextSymbol = WSymbol::PAUSE; - break; - case AudioPlayer::S_PLAY: - w.nextSymbol = WSymbol::PLAY; - break; - case AudioPlayer::S_DONE: - if (playall) - w.nextSymbol = WSymbol::PLAY; - else - drawSymbol=false; - break; - case AudioPlayer::S_BACK: - w.nextSymbol = WSymbol::FBWD ; - break; - case AudioPlayer::S_FF: - w.nextSymbol = WSymbol::FFWD ; - break; - default: - drawSymbol=false; - break; - } - if (drawSymbol) { - w.setPosition(x, ybottom-24); - w.draw(); - } - else { - //stop symbol - info->rectangle(x, ybottom - 23, 18, 16, Colour::LIGHTTEXT); - } - x+=30; - drawAudioClocks(); - } - if (playerInfo) delete playerInfo; - if (playerTitle) delete playerTitle; -} - -void VMediaView::drawAudioClocks() { - if (! info || ! audioEnabled) return; - Log::getInstance()->log("VMediaView::drawAudioClocks", Log::DEBUG, ""); - //draw clocks and bar - info->rectangle(clocksRegion, Colour::TITLEBARBACKGROUND); - - time_t currentSec = (time_t)(getPlayer()->getCurrentTimes()); - time_t lengthSec=(time_t)(getPlayer()->getSonglen()); - struct tm cpos; - struct tm slen; -/* gmtime_r(¤tSec,&cpos); - gmtime_r(&lengthSec,&slen);*/ - cpos.tm_hour=currentSec/3600; - cpos.tm_min=(currentSec-cpos.tm_hour*3600)/60; - cpos.tm_sec=(currentSec-cpos.tm_hour*3600-cpos.tm_min*60); - slen.tm_hour=lengthSec/3600;; - slen.tm_min=(lengthSec-slen.tm_hour*3600)/60; - slen.tm_sec=(lengthSec-slen.tm_hour*3600-slen.tm_min*60); - - char buffer[100]; - if (currentSec >= lengthSec) - { - SNPRINTF(buffer,99, "-:--:-- / -:--:-- %03dk",getPlayer()->getCurrentBitrate()/1000); - } - else - { - SNPRINTF(buffer, 99, "%01i:%02i:%02i / %01i:%02i:%02i %03dk", cpos.tm_hour, cpos.tm_min, cpos.tm_sec, slen.tm_hour, slen.tm_min, slen.tm_sec, - getPlayer()->getCurrentBitrate()/1000); - //Log::getInstance()->log("VMediaView", Log::DEBUG, buffer); - } - - info->drawText(buffer, clocksRegion.x, clocksRegion.y, Colour::LIGHTTEXT); - - // Draw progress bar - int progBarXbase = 0; - int barlen=250; - - info->rectangle(barRegion.x + progBarXbase, barRegion.y + 3, barlen+10, 24, Colour::LIGHTTEXT); - info->rectangle(barRegion.x + progBarXbase + 2, barRegion.y + 5, barlen+6, 20, barBlue); - - if (currentSec > lengthSec) currentSec=lengthSec; - if (lengthSec == 0) return; - - // Draw yellow portion - int progressWidth = (barlen+2) * currentSec / lengthSec; - info->rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 7, progressWidth, 16, Colour::SELECTHIGHLIGHT); - -} - -void VMediaView::showAudioBanner() { - destroyAudioBanner(); - if (! audioEnabled) return; - audioBanner=new VInfo(); - Log::getInstance()->log("VMediaView",Log::DEBUG,"creating AudioBanner %p", audioBanner); - Video *v=Video::getInstance(); - audioBanner->setSize(v->getScreenWidth()-100, 36); - audioBanner->createBuffer(); - audioBanner->setPosition(50, v->getScreenHeight()-50); - audioBanner->fillColour(audioBannerBack); - audioBanner->setTitleBarOn(0); - audioBanner->setDropThrough(); - if ( ! currentAudio || ! currentAudio->getDisplayName() || audioError) { - audioBanner->drawText(tr("AudioPlayer - not playing"),5,3,Colour::LIGHTTEXT); - } - else { - char * buf=new char[strlen(currentAudio->getDisplayName())+50]; - SNPRINTF(buf,50,"%s %s",tr("AudioPlayer"),currentAudio->getDisplayName()); - audioBanner->drawText(buf,5,3,Colour::LIGHTTEXT); - delete buf; - } - BoxStack::getInstance()->add(audioBanner); - BoxStack::getInstance()->update(audioBanner); -} - -void VMediaView::destroyAudioBanner() { - if (audioBanner) { - Log::getInstance()->log("VMediaView",Log::DEBUG,"deleting AudioBanner %p",audioBanner); - BoxStack::getInstance()->remove(audioBanner); - audioBanner=NULL; - } -} +/* + Copyright 2004-2005 Chris Tallon, Andreas Vogel + + 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 + +#include "vmediaview.h" + +#include "vpicturebanner.h" +#include "vcolourtuner.h" +#include "audioplayer.h" +#include "timers.h" +#include "boxx.h" +#include "wselectlist.h" +#include "remote.h" +#include "wsymbol.h" +#include "boxstack.h" +#include "vdr.h" +#include "media.h" +#include "video.h" +#include "vinfo.h" +#include "i18n.h" +#include "message.h" +#include "command.h" +#include "mediaoptions.h" +#include "mediaplayer.h" +#include "log.h" + +const int VMediaView::EVENT_SLIDESHOW=100; +const int VMediaView::EVENT_DRAWINGDONE=101; +const int VMediaView::EVENT_DRAWINGERROR=102; +const int VMediaView::EVENT_DIRECTORYDONE=103; + + +/** + * the combined user interface for pictures and audio + * has 2 surfaces to enable drawing in a separate thread + * the info display can either show a picture info or and audio info + * if the audio player comes on top it disables the picture info display + * basically we have 3 modes: + * - HIDDEN viewer is hidden (pictureEnabled=false, audioEnabled=false) + * if justPlaying=true the audioplayer is still running + * - PICTURE picture viewer on top ("slide show") - pictureEnabled=true, audioEnabled=false + * no AudioBanner visible + * if justPlaying=true the audio player runs in bg + * - AUDIO audioPlayer on top ("playlist mode") - pictureEnabled=true, audioEnabled=true + * the picture viewer is currently halted (should be able to continue if no AudioInfo) + * no picture banner (we should have an audio banner to indicate the audio player on top!) + * state transitions (via setPictureMode, setAudioMode) + * - show Picture -> pictureEnabled=true, + * only called from command handler if audioEnabled=false + * call from mediaList only possible if audioEnabled=false + * *** TODO: would be better to have separate function for external call/internal call + * - play [Audio] -> if activate is set -> audioEnabled=true (-> Mode AUDIO) + * used for calls from medialist + * internal calls do not set activate! + * - YELLOW key - toggle + * if audioActive==true -> audioActive=false (new mode depends on pictureEnabled - either HIDDEN or PICTURE) + * if audioActive==false -> audioActive=true (new mode AUDIO) + * *** open handling if no audio file there (ignore key???) - currently empty player + * - BACK if mode AUDIO -> audioEnabled=false, justPlaying=false + * - BACK if mode PICTURE -> pictureEnabled=false; + * - player StreamEnd -> audioEnabled=false (new mode depends on pciture - either HIDDEN or PICTURE) + * info/banner handling: + * - AudioInfo - alternativ to pictureInfo + * off when disabling audio or after timer or with OK in AUDIO mode + * on currently any command or player event (end/new song) when audioEnabled==true and retriggerAudioInfo=true + * on on OK in AUDIO mode + * - Picture Info + * off when disabling Picture or after timer, OK, green + * on only on green when pictureEnabled==true + * - PictureBanner + * 2 modes: loading/normal + * normal: + * off when disabling picture, OK, after timer + * on when enabling picture, with OK + * loading: + * off when disabling picture and when loading done + * on on start loading + * *** open: do not show when audio enabled and slide show running (currently slideshow is paused) + * - AudioBanner + * always shown when audioEnabled=true + * on when enabling Audio + * off when disabling audio + * update in play + * timers: 1 - slide show + * 2 - pictureBanner + * 3 - info showtime + * 4 - audio short update + */ + +#define DRAWING_THREAD + +Colour VMediaView::pictureBack=Colour(140,140,140); +Colour VMediaView::infoBack=Colour(110,110,110); +Colour VMediaView::audioBannerBack=Colour(110,110,110,20); +//the jpeg reader + +//how long do we wait for a single picture chunk +//the unit is 100ms (the timeout on the server side) +#define MAXTRY 100 +class VPreader : public JpegReader { + private: + ImageReader *reader; + VMediaView * parent; + ULLONG size; + bool dobreak;; + public: + VPreader(VMediaView *p,ImageReader *r){ + parent=p; + size=0; + reader=r; + dobreak=false; + }; + virtual ULONG readChunk(ULONG offset,ULONG len,char ** buf) { + Log::getInstance()->log("VMediaView::jpegReader", Log::DEBUG, "read chunk o=%d,len=%d,buf=%p", + offset,len,*buf); + UINT numrec=0; + int rt=0; + for (int trycount=0;trycount < MAXTRY && ! dobreak && numrec == 0 && rt == 0;trycount++) { + if (dobreak) { + *buf=NULL; + rt=-1; + } + else { + rt=reader->getImageChunk((ULLONG)offset,(UINT)len,&numrec,(UCHAR **)buf); + } + } + Log::getInstance()->log("VMediaView::jpegReader", Log::DEBUG, "got n=%d,buf=%p,rt=%d", + numrec,*buf,rt); + return numrec; + } + virtual int initRead(const MediaURI *uri,ULLONG *sz,ULONG factor=100) { + Log::getInstance()->log("VMediaView::jpegReader", Log::DEBUG, "load image %s",uri->getDisplayName()); + Video* video = Video::getInstance(); + dobreak=false; + int rt=MediaPlayer::getInstance()->openMedium(1,uri,sz,video->getScreenWidth()*factor/100, video->getScreenHeight()*factor/100); + if (rt < 0) *sz=0; + size=*sz; + Log::getInstance()->log("VMediaView::jpegReader", Log::DEBUG, "load image %s returned %llu",uri->getDisplayName(),size); + return rt; + } + virtual ULONG getSize() {return size;} + //seems to be thread safe (more or less...) + void setBreak() { + dobreak=true; + } +}; + +/** + * a separate thread for drawing pictures + * will be started with the sfc and the draw control + * will send a player event when done + */ +class DrawingThread : public Thread_TYPE { + private: + VMediaView *_parent; + VPreader * _reader; + WJpeg::JpegControl *_ctl; + Surface *_sfc; + Colour _colour; + bool _interrupted; + public: + DrawingThread(VMediaView *parent) { + _parent=parent; + _reader=NULL; + _ctl=NULL; + _sfc=NULL; + _interrupted=false; + } + virtual ~DrawingThread(){} + bool isRunning() { + return (threadIsActive()!=0); + } + bool start(WJpeg::JpegControl *ctl,Surface *sfc,VPreader *reader,Colour &c) { + if (isRunning()) { + return false; + } + _interrupted=false; + _ctl=ctl; + _sfc=sfc; + _reader=reader; + _colour=c; + return (threadStart() == 1); + } + bool stop() { + Log::getInstance()->log("DrawingThread",Log::DEBUG,"stop initiated"); + if (threadIsActive()) { + _interrupted=true; + if (_reader) _reader->setBreak(); + threadStop(); + } + Log::getInstance()->log("DrawingThread",Log::DEBUG,"stop done"); + _reader=NULL; + return true; + } + protected: + virtual void threadMethod() { + Log::getInstance()->log("DrawingThread",Log::DEBUG,"started"); + bool rt=WJpeg::drawJpeg(_ctl,_sfc,_reader,_colour); + _reader=NULL; + if (! _interrupted) { + Message* m = new Message(); + //we misuse PLAYER_EVENT here + m->message = Message::PLAYER_EVENT; + m->to = _parent; + m->from = _parent; + m->parameter= rt?VMediaView::EVENT_DRAWINGDONE:VMediaView::EVENT_DRAWINGERROR; + Command::getInstance()->postMessageFromOuterSpace(m); + } + Log::getInstance()->log("DrawingThread",Log::DEBUG,"finishing interrupt=%d",(int)_interrupted); + } + virtual void threadPostStopCleanup() {} + +}; + + +VMediaView::VMediaView(VMediaList *p) +{ + Log::getInstance()->log("VMediaView::VMediaView", Log::DEBUG, "p=%p", this); + audioEnabled=false; + pictureEnabled=false; + parent=p; + ireader=new ImageReader(1,MediaPlayer::getInstance()); + reader=new VPreader(this,ireader); + //the surface handling + Video* video = Video::getInstance(); + setSize(video->getScreenWidth(), video->getScreenHeight()); + createBuffer(); + sfc2=getSurface(); + surface=NULL; + //create the second surface + createBuffer(); + sfc1=getSurface(); + originalh=area.h; + originalw=area.w; + //disable the surface + area.h=1; + area.w=1; + //banners and infos + pictureBanner=NULL; + slideshow=false; + pictureError=NULL; + currentPicture=NULL; + info=NULL; + pictureLoading=false; + + //picture settings + showtime=INITIAL_SHOWTIME; + rotate=WJpeg::ROT_0; + currentScale=1; + options=MediaOptions::getInstance(); + VColourTuner::initFactors(); + int st=options->getIntOption("SlideShowInterval"); + if (st > 0) showtime=st; + ctl.area.w=originalw; + ctl.area.h=originalh; + ctl.enlarge=false; + ctl.scaleafter=options->getIntOption("ScaleFactor"); + const char * mode=options->getStringOption("PictureMode"); + if (strcmp(mode,"clip") == 0) ctl.mode=WJpeg::CROP; + else if (strcmp(mode,"letter") == 0) ctl.mode=WJpeg::LETTER; + else if (strcmp(mode,"clipfactor") == 0) ctl.mode=WJpeg::CROPPERCENT; + ctl.scaleAmount=options->getIntOption("PictureSize"); + if (ctl.scaleAmount < 10) ctl.scaleAmount=10; + if (ctl.scaleAmount > 200) ctl.scaleAmount=200; + ctl2=ctl; + cropmode=ctl.mode; + //current control is the one used for DISPLAY (not the one for drawing - this is the other one) + //this is closely coupled to the surface - i.e. ctl is for sfc1, ctl2 for sfc2 + currentControl=&ctl; + bannerEnabled=true; + pictureShowing=false; + + //audio player + + barBlue.set(0, 0, 150, 150); + audioError=NULL; + currentAudio=NULL; + justPlaying=false; + playall=false; + retriggerAudioInfo=false; + audioBanner=NULL; + drawingThread=new DrawingThread(this); +} + +VMediaView::~VMediaView() +{ + Log::getInstance()->log("VMediaView::~VMediaView", Log::DEBUG, "p=%p,secondSfc=%s", this,(secondSurface()?"true":"false")); + destroyPictureBanner(); + if (currentPicture) delete currentPicture; + Timers::getInstance()->cancelTimer(this,1); + Timers::getInstance()->cancelTimer(this,2); + Timers::getInstance()->cancelTimer(this,3); + Timers::getInstance()->cancelTimer(this,4); + Timers::getInstance()->cancelTimer(this,5); + destroyInfo(); + destroyAudioBanner(); + if (getPlayer(false)) { + AudioPlayer::getInstance(NULL,false)->shutdown(); + } + if (currentAudio) delete currentAudio; + drawingThread->stop(); + ireader->shutdown(); + delete ireader; + delete reader; + if (secondSurface()) { + delete sfc1; + sfc1=NULL; + } + else { + delete sfc2; + sfc2=NULL; + } + MediaPlayer::getInstance()->closeMediaChannel(1); + MediaPlayer::getInstance()->closeMediaChannel(2); + Log::getInstance()->log("VMediaView::~VMediaView", Log::DEBUG, "done p=%p", this); +} + +void VMediaView::setPictureMode(bool act) { + Log::getInstance()->log("VMediaView", Log::DEBUG, "set pictureMode %d p=%d, a=%d", (int)act,(int)pictureEnabled,(int)audioEnabled); + if ( act) { + if ( ! pictureEnabled && ! audioEnabled) { + area.h=originalh; + area.w=originalw; + showPictureBanner(); + parent->updateAll(); + } + if (! pictureEnabled) { + //we newly enable the picture - so clear the screen + draw(); + BoxStack::getInstance()->update(this); + if (slideshow) startSlideshow(); + } + } + else + { + if ( pictureEnabled) { + destroyPictureBanner(); + stopSlideshow(false); +#ifdef DRAWING_THREAD + drawingThread->stop(); +#endif + if (! audioEnabled) { + destroyInfo(); + area.w=1; + area.h=1; + draw(); + parent->updateAll(); + } + } + pictureShowing=false; + } + pictureEnabled=act; +} + +//we can disable audio without automatically hiding us +//this will become strange - we are visible but do not handle +//keys - so if you call setAudioMode(false,false) be sure +//to call setAudioMode(false,true) afterwards +//it is only here to give the list a chance to start a new play +//when we call directoryDone +void VMediaView::setAudioMode(bool act,bool doHiding) { + Log::getInstance()->log("VMediaView", Log::DEBUG, "setAudioMode %d p=%d, a=%d", (int)act,(int)pictureEnabled,(int)audioEnabled); + if ( act) { + if (! audioEnabled) { + if (! pictureEnabled) { + area.w=originalw; + area.h=originalh; + draw(); //empty screen if no picture + parent->updateAll(); + } + destroyPictureBanner(); + + } + audioEnabled=true; + showAudioBanner(); + showAudioInfo(); + } + else + { + if ( audioEnabled) { + audioEnabled=false; + destroyInfo(); + destroyAudioBanner(); + } + if (! pictureEnabled && doHiding) { + area.w=1; + area.h=1; + draw(); + parent->updateAll(); + } + else { + //picture now on top + if (havePictureBanner && ! pictureBanner) showPictureBanner(); + } + } +} + + + +void VMediaView::draw() +{ + Log::getInstance()->log("VMediaView::draw", Log::DEBUG, "pictureError=%s,p=%p", pictureError,this); + + if (pictureShowing ) fillColour(pictureBack); + else fillColour(Colour::BLACK); + if (pictureError) { + drawText(pictureError,100,area.h/2,Colour::LIGHTTEXT); + return; + } +} + + +int VMediaView::handleCommand(int command) +{ + Log::getInstance()->log("VMediaView::handleCommand", Log::DEBUG, "cmd=%d,p=%p", command,this); + if ( !audioEnabled && ! pictureEnabled ) { + //only handle YELLOW + if (command == Remote::YELLOW) { + setAudioMode(true); + return 1; + } + return 0; + } + if ( ! audioEnabled) { + //------------------------- command in mode PICTURE (i.e. picture is on top) ---------------- + //picture on top + int rt=1; + switch(command) + { + case Remote::DF_UP: + case Remote::UP: + case Remote::SKIPBACK: + rotate=WJpeg::ROT_0; + showPicture(VMediaList::MV_PREV,slideshow,true); + rt= 2; + break; + case Remote::FORWARD: + if (showtime > 1) showtime--; + updatePictureBanner(true); + break; + case Remote::DF_DOWN: + case Remote::DOWN: + case Remote::SKIPFORWARD: + rotate=WJpeg::ROT_0; + showPicture(VMediaList::MV_NEXT,slideshow,true); + rt= 2; + break; + case Remote::REVERSE: + if (showtime < 50 ) showtime++; + updatePictureBanner(true); + break; + case Remote::OK: + { + if (pictureBanner) { + destroyPictureBanner(); + destroyInfo(); + havePictureBanner=false; + } + else { + havePictureBanner=true; + showPictureBanner(pictureLoading); + } + rt= 2; + } + break; + case Remote::PLAY: + { + slideshow=true; + rotate=WJpeg::ROT_0; + showPicture(VMediaList::MV_NEXT,slideshow,true); + rt= 2; + } + break; + case Remote::PAUSE: + if (slideshow) { + stopSlideshow(true); + updatePictureBanner(); + } + else { + slideshow=true; + rotate=WJpeg::ROT_0; + showPicture(VMediaList::MV_NEXT,slideshow,true); + } + rt= 2; + break; + case Remote::STOP: + stopSlideshow(true); + showtime=INITIAL_SHOWTIME; + updatePictureBanner(); + rt= 2; + break; + case Remote::RED: + switch(rotate) { + case WJpeg::ROT_0: + rotate=WJpeg::ROT_90; + break; + case WJpeg::ROT_90: + rotate=WJpeg::ROT_180; + break; + case WJpeg::ROT_180: + rotate=WJpeg::ROT_270; + break; + case WJpeg::ROT_270: + rotate=WJpeg::ROT_0; + break; + } + showPicture(VMediaList::MV_NONE,slideshow,true); + rt=2; + break; + case Remote::GREEN: + if (info) destroyInfo(); + else showPictureInfo(); + rt=2; + break; + case Remote::BLUE: + switch (cropmode) { + case WJpeg::CROP: + cropmode=WJpeg::LETTER; + break; + case WJpeg::LETTER: + cropmode=WJpeg::CROPPERCENT; + break; + default: + cropmode=WJpeg::CROP; + break; + } + showPicture(VMediaList::MV_NONE,slideshow,true); + rt=2; + break; + case Remote::MENU: { + stopSlideshow(true); + destroyPictureBanner(); + destroyInfo(); + VColourTuner *ct=new VColourTuner(); + BoxStack::getInstance()->add(ct); + ct->draw(); + BoxStack::getInstance()->update(ct); + rt=2; + + } break; + case Remote::BACK: + { + setPictureMode(false); + rt= 2; + } + break; + case Remote::YELLOW: + { + setAudioMode(true); + } + } + return rt; + } + else + { + int rt=1; + bool updateInfo=false; + //------------------------- command in mode AUDIO (i.e. audio is on top) ---------------- + switch(command) + { + case Remote::YELLOW: + setAudioMode(false); + rt=2; + break; + case Remote::DF_UP: + case Remote::UP: + play(playall,false,VMediaList::MV_PREV); + rt= 2; + break; + case Remote::FORWARD: + if (! audioError) getPlayer()->fastForward(); + updateInfo=true; + rt=2; + break; + case Remote::DF_DOWN: + case Remote::DOWN: + play(playall,false,VMediaList::MV_NEXT); + rt= 2; + break; + case Remote::SKIPFORWARD: + if (! audioError) getPlayer()->skipForward(10); + rt=2; + break; + case Remote::SKIPBACK: + if (! audioError) getPlayer()->skipBackward(10); + rt=2; + break; + case Remote::REVERSE: + rt=2; + break; + case Remote::ZERO: + if (! audioError) getPlayer()->jumpToPercent(0); + rt=2; + break; + case Remote::ONE: + if (! audioError) getPlayer()->jumpToPercent(10); + rt=2; + break; + case Remote::TWO: + if (! audioError) getPlayer()->jumpToPercent(20); + rt=2; + break; + case Remote::THREE: + if (! audioError) getPlayer()->jumpToPercent(30); + rt=2; + break; + case Remote::FOUR: + if (! audioError) getPlayer()->jumpToPercent(40); + rt=2; + break; + case Remote::FIVE: + if (! audioError) getPlayer()->jumpToPercent(50); + rt=2; + break; + case Remote::SIX: + if (! audioError) getPlayer()->jumpToPercent(60); + rt=2; + break; + case Remote::SEVEN: + if (! audioError) getPlayer()->jumpToPercent(70); + rt=2; + break; + case Remote::EIGHT: + if (! audioError) getPlayer()->jumpToPercent(80); + rt=2; + break; + case Remote::NINE: + if (! audioError) getPlayer()->jumpToPercent(90); + rt=2; + break; + case Remote::OK: + case Remote::GREEN: + { + if (info) { + destroyInfo(); + retriggerAudioInfo=false; + } + else { + retriggerAudioInfo=true; + showAudioInfo(); + } + if (getPlayer()->getState() == AudioPlayer::S_ERROR) { + if (playall) play(playall,false,VMediaList::MV_NEXT); + } + rt= 2; + } + break; + case Remote::PLAY: + { + if (! audioError) getPlayer()->unpause(); + updateInfo=true; + if (getPlayer()->getState() != AudioPlayer::S_ERROR) ; + else if (playall) play(playall,false,VMediaList::MV_NEXT); + rt= 2; + } + break; + case Remote::PAUSE: + if (! audioError) getPlayer()->pause(); + updateInfo=true; + rt= 2; + break; + case Remote::STOP: + getPlayer()->stop(); + justPlaying=false; + updateInfo=true; + rt= 2; + break; + case Remote::BACK: + { + getPlayer()->stop(); + playall=false; + retriggerAudioInfo=false; + justPlaying=false; + setAudioMode(false); + if (! pictureShowing) setPictureMode(false); //could have been delayed + rt= 2; + } + break; + } + if (audioEnabled && updateInfo) updateAudioInfo(); + return rt; + } +} + +void VMediaView::processMessage(Message* m) +{ + Log::getInstance()->log("VMediaView::processMessage", Log::DEBUG, "cmd=%lu,p=%lu", m->message,m->parameter); + if (m->message == Message::MOUSE_MOVE) + { + ; + } + else if (m->message == Message::MOUSE_LBDOWN) + { + + //check if press is outside this view! then simulate cancel + int x=(m->parameter>>16)-getScreenX(); + int y=(m->parameter&0xFFFF)-getScreenY(); + if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight()) + { + BoxStack::getInstance()->handleCommand(Remote::BACK); //simulate cancel press + } + } + else if (m->message = Message::PLAYER_EVENT) { + switch (m->parameter) { + case EVENT_SLIDESHOW: + if (! pictureEnabled) break; //old timer msg... + //if (! audioEnabled) { + if (true) { + if (slideshow) { + rotate=WJpeg::ROT_0; + showPicture(VMediaList::MV_NEXT,true,false); + startSlideshow(); + } + } + break; + case EVENT_DRAWINGERROR: + drawingDone(true); + break; + case EVENT_DRAWINGDONE: + drawingDone(false); + break; + case EVENT_DIRECTORYDONE: + //just disable audio without (poetntially) hiding us + //this gives the list a chance to decide whether audio + //should be on top afterwards without showing the list + //immediately + setAudioMode(false,false); + parent->directoryDone(); + if (! pictureShowing) setPictureMode(false); + if (! justPlaying) setAudioMode(false); + break; + case AudioPlayer::STREAM_ERR: + case AudioPlayer::STREAM_END: + if (playall) play(playall,false,VMediaList::MV_NEXT); + else { + setAudioMode(false); + justPlaying=false; + } + break; + case AudioPlayer::SHORT_UPDATE: + if (info && audioEnabled ) { + drawAudioClocks(); + BoxStack::getInstance()->update(info,&clocksRegion); + BoxStack::getInstance()->update(info,&barRegion); + Timers::getInstance()->setTimerD(this, 4, 1); + } + break; + case AudioPlayer::NEW_SONG: + if (audioEnabled) updateAudioInfo(); + break; + case AudioPlayer::CONNECTION_LOST: + if (audioEnabled) destroyInfo(); + if (AudioPlayer *player=getPlayer(false)) { + player->shutdown(); + player=NULL; + } + Command::getInstance()->connectionLost(); + break; + } + } +} + + +VMediaView * VMediaView::createViewer(VMediaList * mparent) { + Log::getInstance()->log("VMediaView::createViewer", Log::DEBUG, "p=%p", + mparent); + VMediaView *vmn=new VMediaView(mparent); + BoxStack::getInstance()->add(vmn); + vmn->draw(); + BoxStack::getInstance()->update(vmn); + return vmn; +} + +void VMediaView::startSlideshow() { + slideshow=true; + if (! pictureLoading) Timers::getInstance()->setTimerD(this,1,showtime); +} + +void VMediaView::stopSlideshow(bool hard) { + if (hard) slideshow=false; + Timers::getInstance()->cancelTimer(this,1); +} + + +void VMediaView::showPicture(ULONG move,bool bslideshow,bool activateBanner) { + pictureShowing=true; + setPictureMode(true); + stopSlideshow(true); +#ifdef DRAWING_THREAD + drawingThread->stop(); +#endif + slideshow=bslideshow; + Media *newPicture=parent->getMedia(MEDIA_TYPE_PICTURE,move); + if ( ! newPicture) { + pictureShowing=false; + if (!audioEnabled) { + //within the event handler first directoryDone is called + //and afterwards everything is stopped if nothing new + sendCommandMsg(EVENT_DIRECTORYDONE); + } + slideshow=false; + } + else + { + pictureShowing=true; + if (currentPicture) { + delete currentPicture; + currentPicture=NULL; + } + currentPicture=newPicture; + loadPicture(currentPicture,activateBanner); + } +} + +//the real picture drawing method +//will start drawing on a new surface and will switch it wehn done + +int VMediaView::loadPicture(Media *md,bool activateBanner) { + pictureError=NULL; + if (! md) return 1; + if (! md->getURI()) { + pictureError=tr("No media found"); + Log::getInstance()->log("VMediaView::load",Log::ERR,"no URI in media"); + return 1; + } + Log::getInstance()->log("VMediaView::load", Log::DEBUG, "filename=%s,p=%p", + md->getURI()->getName(),this); + //do we have a pictureBanner? + havePictureBanner=pictureBanner!=NULL || activateBanner; + pictureLoading=true; +#ifdef DRAWING_THREAD + if (!audioEnabled && havePictureBanner ) showPictureBanner(true); + drawingThread->stop(); +#else + showPictureBanner(true); +#endif + ireader->stop(); + ULLONG size=0; + int rtok=reader->initRead(md->getURI(),&size,ctl.scaleAmount); //scaleAmount is the same for both... + if (rtok == 0) { + //now we can really draw + //get the surface for drawing + Surface * drawSurface=NULL; + WJpeg::JpegControl *drawCtl=NULL; + getDrawingParam(drawSurface,drawCtl); + drawCtl->error[0]=0; + drawCtl->rotation=rotate; + drawCtl->mode=cropmode; + drawCtl->compressedSize=size; +#ifdef DRAWING_THREAD + bool ok=drawingThread->start(drawCtl,drawSurface,reader,pictureBack); + if (! ok) { + Log::getInstance()->log("VMediaView::load", Log::ERR, "unable to start drawing thread"); + pictureError=tr("JpegError"); + pictureLoading=false; + destroyPictureBanner(); + return 1; + } + return 0; +#else + //here we could hand this over to the drawing thread + bool ok=WJpeg::drawJpeg(drawCtl,drawSurface,reader,pictureBack); + drawingDone(!ok); + return ok?0:1; +#endif + } + pictureLoading=false; + return 1; +} + +void VMediaView::drawingDone(bool hasError) { + pictureLoading=false; + destroyPictureBanner(); //disable loading indication (or real banner) + if (! hasError) { + switchSurface(); + Log::getInstance()->log("VMediaView::drawingDone", Log::DEBUG, "success: sfc now=%p",surface); + pictureError=NULL; + //only show the pictureBanner if it was there before + if (havePictureBanner && ! audioEnabled) showPictureBanner(); + Log::getInstance()->log("VMediaView::load", Log::DEBUG, "success" ); + updatePictureInfo(); //will only do somethng if pictureEnabled + } + else + { + pictureError=tr("JpegError"); + if (pictureEnabled) { + draw(); + destroyInfo(); + } + } + MediaPlayer::getInstance()->closeMediaChannel(1); +#ifndef DRAWING_THREAD + if (audioEnabled) showAudioBanner(); +#endif + if (slideshow) startSlideshow(); + BoxStack::getInstance()->update(this); +} + +void VMediaView::showPictureBanner(bool loading) { + //we are in the main thread - so we can (and must) safely hard destroy/create the banner + Timers::getInstance()->cancelTimer(this,2); + if (! currentPicture) { + //hmm... + destroyPictureBanner(false); + return; + } + if (pictureBanner) destroyPictureBanner(false); + if (! pictureEnabled || ! bannerEnabled) return; + pictureBanner= new VPictureBanner(loading, slideshow); + pictureBanner->fillColour(infoBack); + if (! loading) { + int len=strlen(currentPicture->getFileName())+Media::TIMEBUFLEN+20; + char *buf=new char[len]; + char tbuf[Media::TIMEBUFLEN]; + SNPRINTF(buf,len,"%c%02ds%c %s %s " , + slideshow?' ':'[', + showtime, + slideshow?' ':']', + currentPicture->getTimeString(tbuf), + currentPicture->getFileName() + ); + pictureBanner->setText(buf); + delete [] buf; + } + else { + char *buf=new char[strlen(currentPicture->getDisplayName())+50]; + SNPRINTF(buf,50,"%s %s",tr("Loading"), currentPicture->getDisplayName()); + pictureBanner->setText(buf); + delete buf; + } + pictureBanner->draw(); + if (! loading ) Timers::getInstance()->setTimerD(this,2,8); + BoxStack::getInstance()->add(pictureBanner); + BoxStack::getInstance()->update(pictureBanner); + } + +void VMediaView::destroyPictureBanner(bool fromTimer) { + if (pictureBanner) { + if (fromTimer) sendViewMsg(pictureBanner); + else BoxStack::getInstance()->remove(pictureBanner); + pictureBanner=NULL; + if (! fromTimer) Timers::getInstance()->cancelTimer(this,2); + } +} +void VMediaView::updatePictureBanner(bool loading) { + if (pictureBanner ) { + showPictureBanner(loading); + } +} +void VMediaView::timercall(int clientref) { + Log::getInstance()->log("VMediaView::timercall", Log::DEBUG, "id=%d",clientref); + switch(clientref) + { + case 4: + { + sendCommandMsg(AudioPlayer::SHORT_UPDATE); + break; + } + case 3: + { + Log::getInstance()->log("VMediaView::timercall", Log::DEBUG, "infoEnd"); + destroyInfo(true); + if (audioEnabled) { + //we only did show the audio error info if audio is enabled + bool stillError=false; + if (AudioPlayer * player=getPlayer(false)) { + stillError=player->getState()==AudioPlayer::S_ERROR; + } + if (stillError) { + sendCommandMsg(AudioPlayer::STREAM_END); + } + } + break; + } + case 1: + if (! slideshow) return; + Log::getInstance()->log("VMediaView::timercall", Log::DEBUG, "slideshow"); + sendCommandMsg(EVENT_SLIDESHOW); + break; + case 2: + Log::getInstance()->log("VMediaView::timercall", Log::DEBUG, "pictureBannerEnd"); + destroyPictureBanner(true); + break; + } + + } + +#define INFOBUF 2000 +void VMediaView::showPictureInfo(){ + if (! pictureEnabled || audioEnabled) return; + if (info) destroyInfo(); + if (! currentPicture) return; + + info=new VInfo(); + info->setTitleText(currentPicture->getFileName()); + info->setDropThrough(); + info->setSize(500, 300); + info->createBuffer(); + info->setBorderOn(1); + info->setTitleBarOn(1); + + if (Video::getInstance()->getFormat() == Video::PAL) + info->setPosition(100, 180); + else + info->setPosition(100, 150); + char buf[INFOBUF]; + char tbuf[Media::TIMEBUFLEN]; + //modes should come from mediaoptions... + const char *mode=NULL; + switch (currentControl->mode) { + case WJpeg::CROPPERCENT: + mode="clipfactor"; + break; + case WJpeg::LETTER: + mode="letter"; + break; + default: + mode="clip"; + break; + } + const char *dirname=parent->getDirname(MEDIA_TYPE_PICTURE); + int ldir=0; + const char *prfx=""; + if (dirname) ldir=strlen(dirname); + if (ldir > 35){ + dirname=dirname+ldir-35; + prfx="..."; + } + SNPRINTF(buf,INFOBUF,"%s= %s%s\n%s= %u x %u\n%s= %lu kBytes\n%s= %s\n%s= %u\n%s= %u%%\n%s= %s", + tr("Directory"), prfx,dirname, + tr("Format(px)"),currentControl->picw,currentControl->pich, + tr("Filesize"),currentControl->compressedSize/1000, + tr("Time"),currentPicture->getTimeString(tbuf), + tr("Rotation"),90*currentControl->finalRotation, + tr("Scale"),currentControl->scale, + tr("Picture Mode"),mode ); + info->setMainText(buf); + info->draw(); + BoxStack::getInstance()->add(info); + BoxStack::getInstance()->update(info); + Timers::getInstance()->setTimerD(this,3,8); +} +void VMediaView::updatePictureInfo(){ + if (info) { + showPictureInfo(); + } +} +void VMediaView::destroyInfo(bool fromTimer){ + if (info) { + if (! fromTimer) { + Log::getInstance()->log("VMediaView",Log::DEBUG,"remove info %p",info); + BoxStack::getInstance()->remove(info); + } + else { + Log::getInstance()->log("VMediaView",Log::DEBUG,"trigger remove info %p",info); + sendViewMsg(info); + } + info=NULL; + } + if (! fromTimer) Timers::getInstance()->cancelTimer(this,3); + if (! fromTimer) Timers::getInstance()->cancelTimer(this,4); +} + +void VMediaView::sendViewMsg(Boxx *v) { + Message* m = new Message(); + m->message = Message::CLOSE_ME; + m->to = BoxStack::getInstance(); + m->from = v; + m->parameter=(ULONG)v; + Command::getInstance()->postMessageFromOuterSpace(m); +} +void VMediaView::sendCommandMsg(int command) { + Message* m = new Message(); + //we misuse PLAYER_EVENT here + m->message = Message::PLAYER_EVENT; + m->to = this; + m->from = this; + m->parameter= command; + Command::getInstance()->postMessageFromOuterSpace(m); +} + +void VMediaView::enableBanner(bool enable) { + bannerEnabled=false; + updatePictureBanner(); +} + +void VMediaView::getDrawingParam(Surface *&sfc,WJpeg::JpegControl *&c){ + if (secondSurface()) { + //we currently display on sfc2 + sfc=sfc1; + c=&ctl; + } + else { + sfc=sfc2; + c=&ctl2; + } +} +void VMediaView::switchSurface(){ + if (secondSurface()) { + //now we switch to sfc1 + surface=sfc1; + currentControl=&ctl; + } + else { + surface=sfc2; + currentControl=&ctl2; + } +} + +AudioPlayer * VMediaView::getPlayer(bool createIfNeeded) +{ + AudioPlayer* rt=AudioPlayer::getInstance(this,false); + if (! createIfNeeded && rt == NULL) return NULL; + if (rt == NULL) { + rt=AudioPlayer::getInstance(this); + rt->run(); + } + return rt; +} + +bool VMediaView::isAudioPlaying() { + Log::getInstance()->log("VMediaView::isPlaying", Log::DEBUG, "rt=%s", justPlaying?"true":"false"); + return justPlaying; +} + + + + + + +int VMediaView::play(bool all,bool activate,ULONG move,bool showInfo) { + int rt=0; + if (getPlayer(false)) getPlayer(false)->stop(); + justPlaying=false; + if (currentAudio) delete currentAudio; + currentAudio=NULL; + playall=all; + currentAudio=parent->getMedia(MEDIA_TYPE_AUDIO,move); + audioError=NULL; + if ( ! currentAudio || ! currentAudio->getURI()) { + Log::getInstance()->log("VMediaView::load", Log::ERR, "no URI in media"); + audioError=tr("no audio file"); + if (audioEnabled) sendCommandMsg(EVENT_DIRECTORYDONE); + rt= -1; + } + else { + Log::getInstance()->log("VMediaView::load", Log::DEBUG, "filename=%s,p=%p", + currentAudio->getURI()->getName(),this); + int wseq=getPlayer()->play(currentAudio->getURI()); + if (getPlayer()->waitForSequence(5,wseq)<0) { + audioError=tr("unable to open audio file"); + rt= -1; + } + else { + justPlaying=true; + rt=0; + } + Log::getInstance()->log("VMediaView", Log::DEBUG, "player started for %s",currentAudio->getURI()->getName()); + } + if (activate && ! audioEnabled){ + if (showInfo) retriggerAudioInfo=true; + setAudioMode(true); + } + else { + //avoid duplicate creation of banner and info + showAudioBanner(); + showAudioInfo(); + } + if (activate && (! currentPicture || ! pictureShowing)) draw(); + BoxStack::getInstance()->update(this); + return rt; +} + +void VMediaView::showAudioInfo() { + if (! audioEnabled) return; + Timers::getInstance()->cancelTimer(this,4); + Timers::getInstance()->cancelTimer(this,3); + if (info) destroyInfo(); + if (! retriggerAudioInfo) return; + drawAudioInfo(); + bool playerError=getPlayer()->getState()==AudioPlayer::S_ERROR || audioError; + if (! playerError) Timers::getInstance()->setTimerD(this,3,AUDIOBANNER_TIME); + else Timers::getInstance()->setTimerD(this,3,AUDIOERROR_TIME); + if (! playerError) Timers::getInstance()->setTimerD(this,4, 1); + BoxStack::getInstance()->update(info); + } + +void VMediaView::updateAudioInfo() { + if (info) { + showAudioInfo(); + } +} + +void VMediaView::drawAudioInfo(){ + Log::getInstance()->log("VMediaView",Log::DEBUG, "draw banner for %p",info); + const char *title=NULL; + char *playerTitle=NULL; + bool playerError=false; + int numlines=0; + int len=0; + int num=0; + const char *pl=tr("Playlist"); + const char *first=NULL; + char *playerInfo=NULL; + if (audioError) { + if (getPlayer()->getState() == AudioPlayer::S_PLAY) audioError=NULL; + } + if (! currentAudio && ! audioError) audioError=tr("no audio file"); + if (audioError ) { + title=tr("MediaError"); + } + else { + playerTitle=getPlayer()->getTitle(); + if (playerTitle) title=playerTitle; + else title=currentAudio->getDisplayName(); + num=parent->getNumEntries(MEDIA_TYPE_AUDIO,currentAudio->index); + playerError=getPlayer()->getState() == AudioPlayer::S_ERROR; + //1more line for long dirs + numlines=playall?5:4; + } + if (playerError) { + numlines=3; + first=tr("Unable to play audio file"); + len=strlen(first)+3; + } + else if (audioError) { + numlines=3; + first=audioError; + len=strlen(first)+3; + } + else { + playerInfo=getPlayer()->getID3Info();drawText(tr("Loading"),5,3,Colour::LIGHTTEXT); + len=strlen(currentAudio->getDisplayName())+strlen(pl)+30+strlen(parent->getDirname(MEDIA_TYPE_AUDIO))+Media::TIMEBUFLEN+110; + if (playerInfo) { + for (UINT i=0;igetScreenHeight(); + UINT vwidth=Video::getInstance()->getScreenWidth(); + if (height > vheight-2*AUDIOBANNER_BOTTOM_MARGIN) + height=vheight-2*AUDIOBANNER_BOTTOM_MARGIN; + info->setSize(vwidth -2*AUDIOBANNER_X_MARGIN, height); + info->createBuffer(); + if (Video::getInstance()->getFormat() == Video::PAL) + { + info->setPosition(AUDIOBANNER_X_MARGIN, vheight-height-AUDIOBANNER_BOTTOM_MARGIN); + } + else + { + info->setPosition(AUDIOBANNER_X_MARGIN, vheight-height-AUDIOBANNER_BOTTOM_MARGIN); + } + + info->setTitleBarOn(0); + info->setDropThrough(); + //from vvideorec + //set the regions for the closcks and bars on banner + barRegion.x = info->getWidth()-AUDIOBARLEN-20; + barRegion.y = info->getHeight() - 30; // FIXME, need to be - 1? and below? + barRegion.w = info->getWidth()-AUDIOBARLEN+10; + barRegion.h = 30; + + clocksRegion.x = 130; + clocksRegion.y = barRegion.y + 3; + clocksRegion.w = 190; + clocksRegion.h = getFontHeight(); + Log::getInstance()->log("VMediaView",Log::DEBUG,"created AudioInfo %p",info); + BoxStack::getInstance()->add(info); + char *buf=new char [len]; + if (playerError || audioError) { + SNPRINTF(buf,len,"%s",first); + } + else { + char tbuf[Media::TIMEBUFLEN]; + if (playall) { + SNPRINTF(buf,len,"%s\n" + "%s:%s\n" + "%s: %d/%d\n" + "%s:%s\n" + "%s:%s\n", + first, + tr("FileName"),currentAudio->getDisplayName(), + pl,num,parent->getNumEntries(MEDIA_TYPE_AUDIO), + tr("Directory"),parent->getDirname(MEDIA_TYPE_AUDIO), + tr("Time"),currentAudio->getTimeString(tbuf)); + } + else { + SNPRINTF(buf,len,"%s\n" + "%s:%s\n" + "%s:%s\n" + "%s:%s\n", + first, + tr("FileName"),currentAudio->getDisplayName(), + tr("Directory"),parent->getDirname(MEDIA_TYPE_AUDIO), + tr("Time"),currentAudio->getTimeString(tbuf)); + } + } + Log::getInstance()->log("VMediaView",Log::DEBUG,"info: (%d)%s",strlen(buf),buf); + //now the real drawing functions + info->draw(); + //title + info->rectangle(0, 0, info->getWidth(), 30, Colour::TITLEBARBACKGROUND); + info->drawText(title, 5, 5, Colour::LIGHTTEXT); + info->drawPara(buf,5,32,Colour::LIGHTTEXT); + delete [] buf; + if (! audioError) { + WSymbol w; + info->TEMPADD(&w); + int x=10; + int ybottom=info->getHeight(); + info->rectangle(0, ybottom - barRegion.h, info->getWidth(), barRegion.h, Colour::TITLEBARBACKGROUND); + bool drawSymbol=true; + switch(getPlayer()->getState()) { + case AudioPlayer::S_PAUSE: + w.nextSymbol = WSymbol::PAUSE; + break; + case AudioPlayer::S_PLAY: + w.nextSymbol = WSymbol::PLAY; + break; + case AudioPlayer::S_DONE: + if (playall) + w.nextSymbol = WSymbol::PLAY; + else + drawSymbol=false; + break; + case AudioPlayer::S_BACK: + w.nextSymbol = WSymbol::FBWD ; + break; + case AudioPlayer::S_FF: + w.nextSymbol = WSymbol::FFWD ; + break; + default: + drawSymbol=false; + break; + } + if (drawSymbol) { + w.setPosition(x, ybottom-24); + w.draw(); + } + else { + //stop symbol + info->rectangle(x, ybottom - 23, 18, 16, Colour::LIGHTTEXT); + } + x+=30; + drawAudioClocks(); + } + if (playerInfo) delete playerInfo; + if (playerTitle) delete playerTitle; +} + +void VMediaView::drawAudioClocks() { + if (! info || ! audioEnabled) return; + Log::getInstance()->log("VMediaView::drawAudioClocks", Log::DEBUG, ""); + //draw clocks and bar + info->rectangle(clocksRegion, Colour::TITLEBARBACKGROUND); + + time_t currentSec = (time_t)(getPlayer()->getCurrentTimes()); + time_t lengthSec=(time_t)(getPlayer()->getSonglen()); + struct tm cpos; + struct tm slen; +/* gmtime_r(¤tSec,&cpos); + gmtime_r(&lengthSec,&slen);*/ + cpos.tm_hour=currentSec/3600; + cpos.tm_min=(currentSec-cpos.tm_hour*3600)/60; + cpos.tm_sec=(currentSec-cpos.tm_hour*3600-cpos.tm_min*60); + slen.tm_hour=lengthSec/3600;; + slen.tm_min=(lengthSec-slen.tm_hour*3600)/60; + slen.tm_sec=(lengthSec-slen.tm_hour*3600-slen.tm_min*60); + + char buffer[100]; + if (currentSec >= lengthSec) + { + SNPRINTF(buffer,99, "-:--:-- / -:--:-- %03dk",getPlayer()->getCurrentBitrate()/1000); + } + else + { + SNPRINTF(buffer, 99, "%01i:%02i:%02i / %01i:%02i:%02i %03dk", cpos.tm_hour, cpos.tm_min, cpos.tm_sec, slen.tm_hour, slen.tm_min, slen.tm_sec, + getPlayer()->getCurrentBitrate()/1000); + //Log::getInstance()->log("VMediaView", Log::DEBUG, buffer); + } + + info->drawText(buffer, clocksRegion.x, clocksRegion.y, Colour::LIGHTTEXT); + + // Draw progress bar + int progBarXbase = 0; + int barlen=250; + + info->rectangle(barRegion.x + progBarXbase, barRegion.y + 3, barlen+10, 24, Colour::LIGHTTEXT); + info->rectangle(barRegion.x + progBarXbase + 2, barRegion.y + 5, barlen+6, 20, barBlue); + + if (currentSec > lengthSec) currentSec=lengthSec; + if (lengthSec == 0) return; + + // Draw yellow portion + int progressWidth = (barlen+2) * currentSec / lengthSec; + info->rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 7, progressWidth, 16, Colour::SELECTHIGHLIGHT); + +} + +void VMediaView::showAudioBanner() { + destroyAudioBanner(); + if (! audioEnabled) return; + audioBanner=new VInfo(); + Log::getInstance()->log("VMediaView",Log::DEBUG,"creating AudioBanner %p", audioBanner); + Video *v=Video::getInstance(); + audioBanner->setSize(v->getScreenWidth()-100, 36); + audioBanner->createBuffer(); + audioBanner->setPosition(50, v->getScreenHeight()-50); + audioBanner->fillColour(audioBannerBack); + audioBanner->setTitleBarOn(0); + audioBanner->setDropThrough(); + if ( ! currentAudio || ! currentAudio->getDisplayName() || audioError) { + audioBanner->drawText(tr("AudioPlayer - not playing"),5,3,Colour::LIGHTTEXT); + } + else { + char * buf=new char[strlen(currentAudio->getDisplayName())+50]; + SNPRINTF(buf,50,"%s %s",tr("AudioPlayer"),currentAudio->getDisplayName()); + audioBanner->drawText(buf,5,3,Colour::LIGHTTEXT); + delete buf; + } + BoxStack::getInstance()->add(audioBanner); + BoxStack::getInstance()->update(audioBanner); +} + +void VMediaView::destroyAudioBanner() { + if (audioBanner) { + Log::getInstance()->log("VMediaView",Log::DEBUG,"deleting AudioBanner %p",audioBanner); + BoxStack::getInstance()->remove(audioBanner); + audioBanner=NULL; + } +} diff --git a/vopts.cc b/vopts.cc index bacf587..bb40d73 100644 --- a/vopts.cc +++ b/vopts.cc @@ -1,371 +1,376 @@ -/* - Copyright 2007 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 "vopts.h" - -#include "colour.h" -#include "video.h" -#include "audio.h" -#include "remote.h" -#include "boxstack.h" -#include "woptionpane.h" -#include "wremoteconfig.h" -#include "log.h" -#include "option.h" -#include "vdr.h" -#include "command.h" -#include "mediaoptions.h" - -//#include "vdr.h" -//#include "command.h" - -VOpts::VOpts() -{ - setTitleBarOn(1); - setTitleBarColour(Colour::TITLEBARBACKGROUND); - setTitleText(tr("Options")); - - setSize(520, 360); - createBuffer(); - if (Video::getInstance()->getFormat() == Video::PAL) - setPosition(100, 110); - else - setPosition(90, 90); - - tabbar.setPosition(6, 32); - tabbar.setSize(getWidth() - 12, getHeight() - 34); - add(&tabbar); - - Option* option; - WOptionPane* wop; - - // --- edit options start here - - static const char* options1[] = {"Old", "New"}; - static const char* options3[] = {"RGB+composite", "S-Video"}; - static const char* options4[] = {"4:3", "16:9"}; - static const char* options5[] = {"Chop sides", "Letterbox"}; - static const char* options6[] = {"On", "Off", "Last state"}; - static const char* options7[] = {"All", "FTA only"}; - static const char* options15[] = {"Alphabetical", "Chronological"}; - - static const char* options13[] = {"1024", "2048", "4096", "8192", "16384", "32768", "65536"}; - static const char* options14[] = {"No", "Yes"}; - - // Get list of languages from VDR and construct options table - LangCode = VDR::getInstance()->getLanguageList(); - options2 = new const char*[LangCode.size()]; - options2keys = new const char*[LangCode.size()]; - I18n::lang_code_list::const_iterator iter; - UINT LangNumber = 0; - for (iter = LangCode.begin(); iter != LangCode.end(); ++iter,++LangNumber) - { - options2[LangNumber] = iter->second.c_str(); - options2keys[LangNumber] = iter->first.c_str(); - } - - numPanes = 4; - panes = new Boxx*[numPanes]; - - wop = new WOptionPane(); - tabbar.addTab(tr("General"), wop); - panes[0] = wop; - - option = new Option(1, "Remote control type", "General", "Remote type", Option::TYPE_TEXT, 2, 0, 0, options1); - options.push_back(option); - wop->addOptionLine(option); - option = new Option(2, "Language", "General", "LangCode", Option::TYPE_KEYED_TEXT, LangCode.size(), 0, 0, options2, options2keys); - options.push_back(option); - wop->addOptionLine(option); - option = new Option(3, "TV connection type", "TV", "Connection", Option::TYPE_TEXT, 2, 0, 0, options3); - options.push_back(option); - wop->addOptionLine(option); - option = new Option(4, "TV aspect ratio", "TV", "Aspect", Option::TYPE_TEXT, 2, 0, 0, options4); - options.push_back(option); - wop->addOptionLine(option); - option = new Option(5, "16:9 on 4:3 display mode", "TV", "Widemode", Option::TYPE_TEXT, 2, 0, 0, options5); - options.push_back(option); - wop->addOptionLine(option); - option = new Option(6, "Power state after bootup", "General", "Power After Boot", Option::TYPE_TEXT, 3, 0, 0, options6); - options.push_back(option); - wop->addOptionLine(option); - option = new Option(7, "Display channels", "General", "Channels", Option::TYPE_TEXT, 2, 0, 0, options7); - options.push_back(option); - wop->addOptionLine(option); - option = new Option(15, "Recordings sort order", "General", "Recordings Sort Order", Option::TYPE_TEXT, 2, 0, 0, options15); - options.push_back(option); - wop->addOptionLine(option); - - Remote::getInstance()->addOptionsToPanes(0,&options,wop); - Video::getInstance()->addOptionsToPanes(0,&options,wop); - Audio::getInstance()->addOptionsToPanes(0,&options,wop); - - -/* WRemoteConfig* wrc = new WRemoteConfig(); - tabbar.addTab(tr("Remote Control"), wrc);*/ - Remote::getInstance()->addOptionPagesToWTB(&tabbar); - // panes[1] = wrc; - - Video::getInstance()->addOptionPagesToWTB(&tabbar); - Audio::getInstance()->addOptionPagesToWTB(&tabbar); - MediaOptions::getInstance()->addOptionPagesToWTB(&tabbar); - - - wop = new WOptionPane(); - tabbar.addTab(tr("Timers"), wop); - panes[1] = wop; - - option = new Option(9, "Default start margin (minutes)", "Timers", "Start margin", Option::TYPE_INT, 20, 5, 0, NULL); - options.push_back(option); - wop->addOptionLine(option); - option = new Option(10, "Default end margin (minutes)", "Timers", "End margin", Option::TYPE_INT, 20, 5, 0, NULL); - options.push_back(option); - wop->addOptionLine(option); - option = new Option(11, "Default priority", "Timers", "Priority", Option::TYPE_INT, 100, 99, 0, NULL); - options.push_back(option); - wop->addOptionLine(option); - option = new Option(12, "Default lifetime", "Timers", "Lifetime", Option::TYPE_INT, 100, 99, 0, NULL); - options.push_back(option); - wop->addOptionLine(option); - - Remote::getInstance()->addOptionsToPanes(1,&options,wop); - Video::getInstance()->addOptionsToPanes(1,&options,wop); - Audio::getInstance()->addOptionsToPanes(1,&options,wop); - - - wop = new WOptionPane(); - tabbar.addTab(tr("Advanced"), wop); - panes[2] = wop; - - option = new Option(8, "VDR-Pri 0=OK !See forums!", "General", "Live priority", Option::TYPE_INT, 100, 0, 0, NULL); - options.push_back(option); - wop->addOptionLine(option); - option = new Option(13, "TCP receive window size", "Advanced", "TCP receive window", Option::TYPE_TEXT, 7, 1, 0, options13); - options.push_back(option); - wop->addOptionLine(option); - option = new Option(14, "Use WSS (PAL only)", "General", "WSS", Option::TYPE_TEXT, 2, 0, 0, options14); - options.push_back(option); - wop->addOptionLine(option); - - Remote::getInstance()->addOptionsToPanes(2,&options,wop); - Video::getInstance()->addOptionsToPanes(2,&options,wop); - Audio::getInstance()->addOptionsToPanes(2,&options,wop); -} - -VOpts::~VOpts() -{ - // for (int i = 0; i < numPanes; i++) delete panes[i]; //Move to TabBar, Marten - delete[] panes; - - for(vector::iterator j = options.begin(); j != options.end(); j++) delete *j; - delete[] options2; - delete[] options2keys; -} - -int VOpts::handleCommand(int command) -{ - // either is active, handle back - if (command == Remote::BACK) - { - doSave(); - return 4; - } - else - { - int retval = tabbar.handleCommand(command); - if (retval == 1) - { - BoxStack::getInstance()->update(this); - return 2; - } - else if (retval == 2) - { - // command was taken and actively ignored - return 2; - } - else - { - return 1; // ??? - } - } -} - -void VOpts::doSave() -{ - VDR* vdr = VDR::getInstance(); - - Remote::getInstance()->saveOptionstoServer(); //Remote - Video::getInstance()->saveOptionstoServer(); //Video - Audio::getInstance()->saveOptionstoServer(); //Remote - MediaOptions::getInstance()->saveOptionstoServer(); //Media - - // Damn, and the dynamic idea was going *so* well... - //Whats about a check with typeid operator? - WOptionPane* wop; - wop = (WOptionPane*)panes[0]; - wop->saveOpts(); - wop = (WOptionPane*)panes[1]; - wop->saveOpts(); - wop = (WOptionPane*)panes[2]; - wop->saveOpts(); - - - for (UINT i = 0; i < options.size(); i++) - { - if (options[i]->configChoice == options[i]->userSetChoice) continue; // no change - - Log::getInstance()->log("Options", Log::DEBUG, "Option %i has changed", i); - - // Save to vdr - - if (options[i]->optionType == Option::TYPE_TEXT) - { - vdr->configSave(options[i]->configSection, options[i]->configKey, options[i]->options[options[i]->userSetChoice]); - } - else if (options[i]->optionType == Option::TYPE_KEYED_TEXT) - { - vdr->configSave(options[i]->configSection, options[i]->configKey, options[i]->optionkeys[options[i]->userSetChoice]); - } - else - { - char buffer[20]; - sprintf(buffer, "%i", options[i]->userSetChoice); - vdr->configSave(options[i]->configSection, options[i]->configKey, buffer); - } - - // Set new setting - if (options[i]->opthandler == NULL) //Ok, noone else is handling this, we are doing it - { - switch(options[i]->id) - { - case 1: - { - if (options[i]->userSetChoice == 1) - { - Log::getInstance()->log("Options", Log::DEBUG, "Setting New Remote"); - Remote::getInstance()->setRemoteType(Remote::NEWREMOTE); - } - else - { - Log::getInstance()->log("Options", Log::DEBUG, "Setting Old Remote"); - Remote::getInstance()->setRemoteType(Remote::OLDREMOTE); - } - break; - } - case 2: - { - Message* m = new Message(); - m->message = Message::CHANGE_LANGUAGE; - m->to = Command::getInstance(); - Command::getInstance()->postMessageNoLock(m); - break; - } - case 3: - { - if (options[i]->userSetChoice == 1) - { - Log::getInstance()->log("Options", Log::DEBUG, "Setting S-Video"); - Video::getInstance()->setConnection(Video::SVIDEO); - } - else - { - Log::getInstance()->log("Options", Log::DEBUG, "Setting RGB/Composite"); - Video::getInstance()->setConnection(Video::COMPOSITERGB); - } - break; - } - case 4: - { - if (options[i]->userSetChoice == 1) - { - Log::getInstance()->log("Options", Log::DEBUG, "Setting 16:9 TV"); - Video::getInstance()->setTVsize(Video::ASPECT16X9); - } - else - { - Log::getInstance()->log("Options", Log::DEBUG, "Setting 4:3 TV"); - Video::getInstance()->setTVsize(Video::ASPECT4X3); - } - break; - } - case 5: - { - if (options[i]->userSetChoice == 1) - { - Log::getInstance()->log("Options", Log::DEBUG, "Setting letterbox"); - Video::getInstance()->setMode(Video::LETTERBOX); - } - else - { - Log::getInstance()->log("Options", Log::DEBUG, "Setting chop-sides"); - Video::getInstance()->setMode(Video::NORMAL); - } - break; - } - case 13: - { - size_t newTCPsize = 2048; - if (options[i]->userSetChoice == 0) newTCPsize = 1024; - else if (options[i]->userSetChoice == 1) newTCPsize = 2048; - else if (options[i]->userSetChoice == 2) newTCPsize = 4096; - else if (options[i]->userSetChoice == 3) newTCPsize = 8192; - else if (options[i]->userSetChoice == 4) newTCPsize = 16384; - else if (options[i]->userSetChoice == 5) newTCPsize = 32768; - else if (options[i]->userSetChoice == 6) newTCPsize = 65536; - Log::getInstance()->log("Options", Log::DEBUG, "Setting TCP window size %i", newTCPsize); - VDR::getInstance()->setReceiveWindow(newTCPsize); - break; - } - } - } - else - { - options[i]->opthandler->handleOptionChanges(options[i]); - } - } -} - -void VOpts::processMessage(Message* m) -{ - if (m->message == Message::MOUSE_MOVE) - { - int x=(m->parameter>>16)-getScreenX(); - int y=(m->parameter&0xFFFF)-getScreenY(); - if (tabbar.mouseMove(x,y)) - { - BoxStack::getInstance()->update(this); - } - - } - else if (m->message == Message::MOUSE_LBDOWN) - { - int x=(m->parameter>>16)-getScreenX(); - int y=(m->parameter&0xFFFF)-getScreenY(); - if (tabbar.mouseLBDOWN(x,y)) - { - BoxStack::getInstance()->update(this); - } - else if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight()) - { - BoxStack::getInstance()->handleCommand(Remote::BACK); //simulate cancel press - } - } -} +/* + Copyright 2007 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 "vopts.h" + +#include "colour.h" +#include "video.h" +#include "audio.h" +#include "remote.h" +#include "boxstack.h" +#include "woptionpane.h" +#include "wremoteconfig.h" +#include "log.h" +#include "option.h" +#include "vdr.h" +#include "command.h" + +#ifdef VOMP_PLATTFORM_MVP +#include "mediaoptions.h" +#endif +//#include "vdr.h" +//#include "command.h" + +VOpts::VOpts() +{ + setTitleBarOn(1); + setTitleBarColour(Colour::TITLEBARBACKGROUND); + setTitleText(tr("Options")); + + + setSize(520, 360); + createBuffer(); + if (Video::getInstance()->getFormat() == Video::PAL) + setPosition(100, 110); + else + setPosition(90, 90); + + tabbar.setPosition(6, 32); + tabbar.setSize(getWidth() - 12, getHeight() - 34); + add(&tabbar); + + Option* option; + WOptionPane* wop; + + // --- edit options start here + + static const char* options1[] = {"Old", "New"}; + static const char* options3[] = {"RGB+composite", "S-Video"}; + static const char* options4[] = {"4:3", "16:9"}; + static const char* options5[] = {"Chop sides", "Letterbox"}; + static const char* options6[] = {"On", "Off", "Last state"}; + static const char* options7[] = {"All", "FTA only"}; + static const char* options15[] = {"Alphabetical", "Chronological"}; + + static const char* options13[] = {"1024", "2048", "4096", "8192", "16384", "32768", "65536"}; + static const char* options14[] = {"No", "Yes"}; + // Get list of languages from VDR and construct options table + LangCode = VDR::getInstance()->getLanguageList(); + options2 = new const char*[LangCode.size()]; + options2keys = new const char*[LangCode.size()]; + I18n::lang_code_list::const_iterator iter; + UINT LangNumber = 0; + for (iter = LangCode.begin(); iter != LangCode.end(); ++iter,++LangNumber) + { + options2[LangNumber] = iter->second.c_str(); + options2keys[LangNumber] = iter->first.c_str(); + } + + numPanes = 4; + panes = new Boxx*[numPanes]; + wop = new WOptionPane(); + tabbar.addTab(tr("General"), wop); + panes[0] = wop; + option = new Option(1, "Remote control type", "General", "Remote type", Option::TYPE_TEXT, 2, 0, 0, options1); + options.push_back(option); + wop->addOptionLine(option); + option = new Option(2, "Language", "General", "LangCode", Option::TYPE_KEYED_TEXT, LangCode.size(), 0, 0, options2, options2keys); + options.push_back(option); + wop->addOptionLine(option); + option = new Option(3, "TV connection type", "TV", "Connection", Option::TYPE_TEXT, 2, 0, 0, options3); + options.push_back(option); + wop->addOptionLine(option); + option = new Option(4, "TV aspect ratio", "TV", "Aspect", Option::TYPE_TEXT, 2, 0, 0, options4); + options.push_back(option); + wop->addOptionLine(option); + option = new Option(5, "16:9 on 4:3 display mode", "TV", "Widemode", Option::TYPE_TEXT, 2, 0, 0, options5); + options.push_back(option); + wop->addOptionLine(option); + option = new Option(6, "Power state after bootup", "General", "Power After Boot", Option::TYPE_TEXT, 3, 0, 0, options6); + options.push_back(option); + wop->addOptionLine(option); + option = new Option(7, "Display channels", "General", "Channels", Option::TYPE_TEXT, 2, 0, 0, options7); + options.push_back(option); + wop->addOptionLine(option); + option = new Option(15, "Recordings sort order", "General", "Recordings Sort Order", Option::TYPE_TEXT, 2, 0, 0, options15); + options.push_back(option); + wop->addOptionLine(option); + + Remote::getInstance()->addOptionsToPanes(0,&options,wop); + Video::getInstance()->addOptionsToPanes(0,&options,wop); + Audio::getInstance()->addOptionsToPanes(0,&options,wop); + + +/* WRemoteConfig* wrc = new WRemoteConfig(); + tabbar.addTab(tr("Remote Control"), wrc);*/ + Remote::getInstance()->addOptionPagesToWTB(&tabbar); + // panes[1] = wrc; + + Video::getInstance()->addOptionPagesToWTB(&tabbar); + Audio::getInstance()->addOptionPagesToWTB(&tabbar); +#ifdef VOMP_PLATTFORM_MVP + MediaOptions::getInstance()->addOptionPagesToWTB(&tabbar); +#endif + + + wop = new WOptionPane(); + tabbar.addTab(tr("Timers"), wop); + panes[1] = wop; + + option = new Option(9, "Default start margin (minutes)", "Timers", "Start margin", Option::TYPE_INT, 20, 5, 0, NULL); + options.push_back(option); + wop->addOptionLine(option); + option = new Option(10, "Default end margin (minutes)", "Timers", "End margin", Option::TYPE_INT, 20, 5, 0, NULL); + options.push_back(option); + wop->addOptionLine(option); + option = new Option(11, "Default priority", "Timers", "Priority", Option::TYPE_INT, 100, 99, 0, NULL); + options.push_back(option); + wop->addOptionLine(option); + option = new Option(12, "Default lifetime", "Timers", "Lifetime", Option::TYPE_INT, 100, 99, 0, NULL); + options.push_back(option); + wop->addOptionLine(option); + + Remote::getInstance()->addOptionsToPanes(1,&options,wop); + Video::getInstance()->addOptionsToPanes(1,&options,wop); + Audio::getInstance()->addOptionsToPanes(1,&options,wop); + + + wop = new WOptionPane(); + tabbar.addTab(tr("Advanced"), wop); + panes[2] = wop; + + option = new Option(8, "VDR-Pri 0=OK !See forums!", "General", "Live priority", Option::TYPE_INT, 100, 0, 0, NULL); + options.push_back(option); + wop->addOptionLine(option); + option = new Option(13, "TCP receive window size", "Advanced", "TCP receive window", Option::TYPE_TEXT, 7, 1, 0, options13); + options.push_back(option); + wop->addOptionLine(option); + option = new Option(14, "Use WSS (PAL only)", "General", "WSS", Option::TYPE_TEXT, 2, 0, 0, options14); + options.push_back(option); + wop->addOptionLine(option); + + Remote::getInstance()->addOptionsToPanes(2,&options,wop); + Video::getInstance()->addOptionsToPanes(2,&options,wop); + Audio::getInstance()->addOptionsToPanes(2,&options,wop); +} + +VOpts::~VOpts() +{ + // for (int i = 0; i < numPanes; i++) delete panes[i]; //Move to TabBar, Marten + delete[] panes; + + for(vector::iterator j = options.begin(); j != options.end(); j++) delete *j; + delete[] options2; + delete[] options2keys; +} + +int VOpts::handleCommand(int command) +{ + // either is active, handle back + if (command == Remote::BACK) + { + doSave(); + return 4; + } + else + { + int retval = tabbar.handleCommand(command); + if (retval == 1) + { + BoxStack::getInstance()->update(this); + return 2; + } + else if (retval == 2) + { + // command was taken and actively ignored + return 2; + } + else + { + return 1; // ??? + } + } +} + +void VOpts::doSave() +{ + VDR* vdr = VDR::getInstance(); + + Remote::getInstance()->saveOptionstoServer(); //Remote + Video::getInstance()->saveOptionstoServer(); //Video + Audio::getInstance()->saveOptionstoServer(); //Remote +#ifdef VOMP_PLATTFORM_MVP + MediaOptions::getInstance()->saveOptionstoServer(); //Media +#endif + + // Damn, and the dynamic idea was going *so* well... + //Whats about a check with typeid operator? + WOptionPane* wop; + wop = (WOptionPane*)panes[0]; + wop->saveOpts(); + wop = (WOptionPane*)panes[1]; + wop->saveOpts(); + wop = (WOptionPane*)panes[2]; + wop->saveOpts(); + + + for (UINT i = 0; i < options.size(); i++) + { + if (options[i]->configChoice == options[i]->userSetChoice) continue; // no change + + Log::getInstance()->log("Options", Log::DEBUG, "Option %i has changed", i); + + // Save to vdr + + if (options[i]->optionType == Option::TYPE_TEXT) + { + vdr->configSave(options[i]->configSection, options[i]->configKey, options[i]->options[options[i]->userSetChoice]); + } + else if (options[i]->optionType == Option::TYPE_KEYED_TEXT) + { + vdr->configSave(options[i]->configSection, options[i]->configKey, options[i]->optionkeys[options[i]->userSetChoice]); + } + else + { + char buffer[20]; + sprintf(buffer, "%i", options[i]->userSetChoice); + vdr->configSave(options[i]->configSection, options[i]->configKey, buffer); + } + + // Set new setting + if (options[i]->opthandler == NULL) //Ok, noone else is handling this, we are doing it + { + switch(options[i]->id) + { + case 1: + { + if (options[i]->userSetChoice == 1) + { + Log::getInstance()->log("Options", Log::DEBUG, "Setting New Remote"); + Remote::getInstance()->setRemoteType(Remote::NEWREMOTE); + } + else + { + Log::getInstance()->log("Options", Log::DEBUG, "Setting Old Remote"); + Remote::getInstance()->setRemoteType(Remote::OLDREMOTE); + } + break; + } + case 2: + { + Message* m = new Message(); + m->message = Message::CHANGE_LANGUAGE; + m->to = Command::getInstance(); + Command::getInstance()->postMessageNoLock(m); + break; + } + case 3: + { + if (options[i]->userSetChoice == 1) + { + Log::getInstance()->log("Options", Log::DEBUG, "Setting S-Video"); + Video::getInstance()->setConnection(Video::SVIDEO); + } + else + { + Log::getInstance()->log("Options", Log::DEBUG, "Setting RGB/Composite"); + Video::getInstance()->setConnection(Video::COMPOSITERGB); + } + break; + } + case 4: + { + if (options[i]->userSetChoice == 1) + { + Log::getInstance()->log("Options", Log::DEBUG, "Setting 16:9 TV"); + Video::getInstance()->setTVsize(Video::ASPECT16X9); + } + else + { + Log::getInstance()->log("Options", Log::DEBUG, "Setting 4:3 TV"); + Video::getInstance()->setTVsize(Video::ASPECT4X3); + } + break; + } + case 5: + { + if (options[i]->userSetChoice == 1) + { + Log::getInstance()->log("Options", Log::DEBUG, "Setting letterbox"); + Video::getInstance()->setMode(Video::LETTERBOX); + } + else + { + Log::getInstance()->log("Options", Log::DEBUG, "Setting chop-sides"); + Video::getInstance()->setMode(Video::NORMAL); + } + break; + } + case 13: + { + size_t newTCPsize = 2048; + if (options[i]->userSetChoice == 0) newTCPsize = 1024; + else if (options[i]->userSetChoice == 1) newTCPsize = 2048; + else if (options[i]->userSetChoice == 2) newTCPsize = 4096; + else if (options[i]->userSetChoice == 3) newTCPsize = 8192; + else if (options[i]->userSetChoice == 4) newTCPsize = 16384; + else if (options[i]->userSetChoice == 5) newTCPsize = 32768; + else if (options[i]->userSetChoice == 6) newTCPsize = 65536; + Log::getInstance()->log("Options", Log::DEBUG, "Setting TCP window size %i", newTCPsize); + VDR::getInstance()->setReceiveWindow(newTCPsize); + break; + } + } + } + else + { + options[i]->opthandler->handleOptionChanges(options[i]); + } + } +} + +void VOpts::processMessage(Message* m) +{ + if (m->message == Message::MOUSE_MOVE) + { + int x=(m->parameter>>16)-getScreenX(); + int y=(m->parameter&0xFFFF)-getScreenY(); + if (tabbar.mouseMove(x,y)) + { + BoxStack::getInstance()->update(this); + } + + } + else if (m->message == Message::MOUSE_LBDOWN) + { + int x=(m->parameter>>16)-getScreenX(); + int y=(m->parameter&0xFFFF)-getScreenY(); + if (tabbar.mouseLBDOWN(x,y)) + { + BoxStack::getInstance()->update(this); + } + else if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight()) + { + BoxStack::getInstance()->handleCommand(Remote::BACK); //simulate cancel press + } + } +} + diff --git a/vradiorec.cc b/vradiorec.cc index 3308bb4..ec0d3a5 100644 --- a/vradiorec.cc +++ b/vradiorec.cc @@ -1,541 +1,547 @@ -/* - Copyright 2004-2006 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 "vradiorec.h" - -#include "command.h" -#include "osd.h" -#include "player.h" -#include "wsymbol.h" -#include "recording.h" -#include "message.h" -#include "vdr.h" -#include "video.h" -#include "timers.h" -#include "playerradio.h" -#include "boxstack.h" -#include "remote.h" -#include "vinfo.h" -#include "i18n.h" -#include "log.h" - -VRadioRec::VRadioRec(Recording* rec) -{ - boxstack = BoxStack::getInstance(); - vdr = VDR::getInstance(); - video = Video::getInstance(); - timers = Timers::getInstance(); - myRec = rec; - playing = false; - startMargin = 0; - endMargin = 0; - - player = new PlayerRadio(Command::getInstance(), this); - - char* cstartMargin = vdr->configLoad("Timers", "Start margin"); - char* cendMargin = vdr->configLoad("Timers", "End margin"); - if (!cstartMargin) - { - startMargin = 300; // 5 mins default - } - else - { - startMargin = atoi(cstartMargin) * 60; - delete[] cstartMargin; - } - - if (!cendMargin) - { - endMargin = 300; // 5 mins default - } - else - { - endMargin = atoi(cendMargin) * 60; - delete[] cendMargin; - } - - Log::getInstance()->log("VRadioRec", Log::DEBUG, "SM: %u EM: %u", startMargin, endMargin); - - setSize(video->getScreenWidth(), video->getScreenHeight()); - createBuffer(); - setPosition(0, 0); - - barRegion.x = 0; - barRegion.y = video->getScreenHeight() - 58; // FIXME, need to be - 1? and below? - barRegion.w = video->getScreenWidth(); - barRegion.h = 58; - - clocksRegion.x = barRegion.x + 140; - clocksRegion.y = barRegion.y + 12; - clocksRegion.w = 170; - clocksRegion.h = surface->getFontHeight(); - - - barBlue.set(0, 0, 150, 150); - - barShowing = false; -} - -void VRadioRec::preDelete() -{ - timers->cancelTimer(this, 1); - timers->cancelTimer(this, 2); -} - -VRadioRec::~VRadioRec() -{ - if (playing) stopPlay(); - - - // kill recInfo in case resumePoint has changed (likely) - myRec->dropRecInfo(); - // FIXME - do this properly - save the resume point back to the server manually and update - // rec->recInfo->resumePoint - this will fix the ~10s offset problem as well -} - -void VRadioRec::draw() -{ - fillColour(Colour::BLACK); -} - -void VRadioRec::go() -{ - Log::getInstance()->log("VRadioRec", Log::DEBUG, "Starting stream: %s", myRec->getFileName()); - ULONG lengthFrames = 0; - bool isPesRecording; - ULLONG lengthBytes = vdr->streamRecording(myRec->getFileName(), &lengthFrames, &isPesRecording); - myRec->IsPesRecording = isPesRecording; - - bool cantStart = false; - - if (!lengthBytes) cantStart = true; - else if (!player->init(lengthBytes, lengthFrames, myRec->IsPesRecording)) cantStart = true; - else - { - doBar(0); - // player->setStartBytes(startBytes); - player->play(); - playing = true; - } - - if (cantStart) - { - stopPlay(); // clean up - - if (!vdr->isConnected()) - { - Command::getInstance()->connectionLost(); - return; - } - - Message* m = new Message(); - m->message = Message::CLOSE_ME; - m->from = this; - m->to = boxstack; - Command::getInstance()->postMessageNoLock(m); - - VInfo* vi = new VInfo(); - vi->setSize(400, 150); - vi->createBuffer(); - if (video->getFormat() == Video::PAL) - vi->setPosition(170, 200); - else - vi->setPosition(160, 150); - vi->setExitable(); - vi->setBorderOn(1); - vi->setTitleBarOn(0); - vi->setOneLiner(tr("Error playing recording")); - vi->draw(); - - m = new Message(); - m->message = Message::ADD_VIEW; - m->to = boxstack; - m->parameter = (ULONG)vi; - Command::getInstance()->postMessageNoLock(m); - } -} - -int VRadioRec::handleCommand(int command) -{ - switch(command) - { - case Remote::PLAY: - { - player->play(); - doBar(0); - return 2; - } - - case Remote::STOP: - case Remote::BACK: - case Remote::MENU: - { - if (playing) stopPlay(); - return 4; - } - case Remote::PAUSE: - { - player->pause(); - doBar(0); - return 2; - } - case Remote::SKIPFORWARD: - { - doBar(3); - player->skipForward(60); - return 2; - } - case Remote::SKIPBACK: - { - doBar(4); - player->skipBackward(60); - return 2; - } - case Remote::YELLOW: - { - doBar(2); - player->skipBackward(10); - return 2; - } - case Remote::BLUE: - { - doBar(1); - player->skipForward(10); - return 2; - } - case Remote::OK: - { - if (barShowing) removeBar(); - else doBar(0); - return 2; - } - - case Remote::ZERO: player->jumpToPercent(0); doBar(0); return 2; - case Remote::ONE: player->jumpToPercent(10); doBar(0); return 2; - case Remote::TWO: player->jumpToPercent(20); doBar(0); return 2; - case Remote::THREE: player->jumpToPercent(30); doBar(0); return 2; - case Remote::FOUR: player->jumpToPercent(40); doBar(0); return 2; - case Remote::FIVE: player->jumpToPercent(50); doBar(0); return 2; - case Remote::SIX: player->jumpToPercent(60); doBar(0); return 2; - case Remote::SEVEN: player->jumpToPercent(70); doBar(0); return 2; - case Remote::EIGHT: player->jumpToPercent(80); doBar(0); return 2; - case Remote::NINE: player->jumpToPercent(90); doBar(0); return 2; - -#ifdef DEV - case Remote::RED: - { - //player->test1(); - - return 2; - } - case Remote::GREEN: - { - //player->test2(); - return 2; - } -#endif - - } - - return 1; -} - -void VRadioRec::processMessage(Message* m) -{ - if (m->message == Message::MOUSE_LBDOWN) - { - int x=(m->parameter>>16)-(int)getScreenX(); - int y=(m->parameter&0xFFFF)-(int)getScreenY(); - if (!barShowing) - { - boxstack->handleCommand(Remote::OK); //simulate rok press - } - else if ((int)barRegion.x<=x && (int)barRegion.y<=y && ((int)barRegion.x+(int)barRegion.w)>=x - && ((int)barRegion.y+(int)barRegion.h)>=y) - { - int progBarXbase = barRegion.x + 300; - if (x>=(int)barRegion.x + progBarXbase + 24 - && x<=(int)barRegion.x + progBarXbase + 4 + 302 - && y>=(int)barRegion.y + 12 - 2 - && y<=(int)barRegion.y + 12 - 2+28) - { - int cx=x-(barRegion.x + progBarXbase + 4); - double percent=((double)cx)/302.*100.; - player->jumpToPercent(percent); - doBar(3); - return; - // int progressWidth = 302 * currentFrameNum / lengthFrames; - // rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 16, progressWidth, 16, Colour::SELECTHIGHLIGHT); - } - } - else - { - boxstack->handleCommand(Remote::OK); //simulate rok press - } - } - else if (m->message == Message::PLAYER_EVENT) - { - if (m->from != player) return; - - Log::getInstance()->log("VRadioRec", Log::DEBUG, "Message received"); - - switch(m->parameter) - { - case Player::CONNECTION_LOST: // connection lost detected - { - // I can't handle this, send it to command - Message* m2 = new Message(); - m2->to = Command::getInstance(); - m2->message = Message::CONNECTION_LOST; - Command::getInstance()->postMessageNoLock(m2); - break; - } - case Player::STOP_PLAYBACK: - { - // FIXME Obselete ish - improve this - Message* m2 = new Message(); // Must be done after this thread finishes, and must break into master mutex - m2->to = Command::getInstance(); - m2->message = Message::STOP_PLAYBACK; - Command::getInstance()->postMessageNoLock(m2); - break; - } - } - } -} - -void VRadioRec::stopPlay() -{ - Log::getInstance()->log("VRadioRec", Log::DEBUG, "Pre stopPlay"); - - removeBar(); - player->stop(); - vdr->stopStreaming(); - delete player; - - playing = false; - - if (!vdr->isConnected()) { Command::getInstance()->connectionLost(); return; } - Log::getInstance()->log("VRadioRec", Log::DEBUG, "Post stopPlay"); -} - -void VRadioRec::doBar(int action) -{ - barShowing = true; - - rectangle(barRegion, barBlue); - - /* Work out what to display - choices: - - Playing > - Paused || - - Specials, informed by parameter - - Skip forward 10s >| - Skip backward 10s |< - Skip forward 1m >>| - Skip backward 1m |<< - - */ - - WSymbol w; - TEMPADD(&w); - w.nextSymbol = 0; - w.setPosition(barRegion.x + 66, barRegion.y + 16); - - UCHAR playerState = 0; - - if (action) - { - if (action == 1) w.nextSymbol = WSymbol::SKIPFORWARD; - else if (action == 2) w.nextSymbol = WSymbol::SKIPBACK; - else if (action == 3) w.nextSymbol = WSymbol::SKIPFORWARD2; - else if (action == 4) w.nextSymbol = WSymbol::SKIPBACK2; - } - else - { - playerState = player->getState(); - if (playerState == Player::S_PAUSE_P) w.nextSymbol = WSymbol::PAUSE; - else if (playerState == Player::S_PAUSE_I) w.nextSymbol = WSymbol::PAUSE; - else w.nextSymbol = WSymbol::PLAY; - } - - w.draw(); - - drawBarClocks(); - - BoxStack::getInstance()->update(this, &barRegion); - - timers->setTimerD(this, 1, 4); // only set the getridofbar timer if not ffwd/fbwd - timers->setTimerD(this, 2, 0, 200000000); -} - -void VRadioRec::timercall(int clientReference) -{ - switch(clientReference) - { - case 1: - { - // Remove bar - removeBar(); - break; - } - case 2: - { - // Update clock - if (!barShowing) break; - drawBarClocks(); - boxstack->update(this, &barRegion); - - timers->setTimerD(this, 2, 0, 200000000); - break; - } - } -} - -void VRadioRec::drawBarClocks() -{ - Log* logger = Log::getInstance(); - logger->log("VRadioRec", Log::DEBUG, "Draw bar clocks"); - - // Draw RTC - // Blank the area first - rectangle(barRegion.x + 624, barRegion.y + 12, 60, 30, barBlue); - char timeString[20]; - time_t t; - time(&t); - struct tm* tms = localtime(&t); - strftime(timeString, 19, "%H:%M", tms); - drawText(timeString, barRegion.x + 624, barRegion.y + 12, Colour::LIGHTTEXT); - - // Draw clocks - - rectangle(clocksRegion, barBlue); - -/* - ULONG currentFrameNum = player->getCurrentFrameNum(); - ULONG lengthFrames; - if (myRec->recInfo->timerEnd > time(NULL)) - { - // chasing playback - // Work out an approximate length in frames (good to 1s...) - lengthFrames = (myRec->recInfo->timerEnd - myRec->recInfo->timerStart) * video->getFPS(); - } - else - { -// lengthFrames = player->getLengthFrames(); - lengthFrames = 0; - } - - hmsf currentFrameHMSF = video->framesToHMSF(currentFrameNum); - hmsf lengthHMSF = video->framesToHMSF(lengthFrames); - - char buffer[100]; - if (currentFrameNum >= lengthFrames) - { - strcpy(buffer, "-:--:-- / -:--:--"); - } - else - { - SNPRINTF(buffer, 99, "%01i:%02i:%02i / %01i:%02i:%02i", currentFrameHMSF.hours, currentFrameHMSF.minutes, currentFrameHMSF.seconds, lengthHMSF.hours, lengthHMSF.minutes, lengthHMSF.seconds); - logger->log("VRadioRec", Log::DEBUG, buffer); - } -*/ - - ULONG currentSeconds = player->getCurrentSeconds(); - ULONG lengthSeconds = player->getLengthSeconds(); - char buffer[100]; - - if (lengthSeconds && (currentSeconds < lengthSeconds)) - { - ULONG dcurrentSeconds = currentSeconds; - ULONG dlengthSeconds = lengthSeconds; - - ULONG currentHours = dcurrentSeconds / 3600; - dcurrentSeconds %= 3600; - ULONG currentMinutes = dcurrentSeconds / 60; - dcurrentSeconds %= 60; - - ULONG lengthHours = dlengthSeconds / 3600; - dlengthSeconds %= 3600; - ULONG lengthMinutes = dlengthSeconds / 60; - dlengthSeconds %= 60; - - SNPRINTF(buffer, 99, "%01lu:%02lu:%02lu / %01lu:%02lu:%02lu", currentHours, currentMinutes, dcurrentSeconds, lengthHours, lengthMinutes, dlengthSeconds); - logger->log("VRadioRec", Log::DEBUG, buffer); - } - else - { - strcpy(buffer, "-:--:-- / -:--:--"); - } - - drawText(buffer, clocksRegion.x, clocksRegion.y, Colour::LIGHTTEXT); - - // Draw progress bar - int progBarXbase = barRegion.x + 300; - - rectangle(barRegion.x + progBarXbase, barRegion.y + 12, 310, 24, Colour::LIGHTTEXT); - rectangle(barRegion.x + progBarXbase + 2, barRegion.y + 14, 306, 20, barBlue); - - if (currentSeconds > lengthSeconds) return; - if (lengthSeconds == 0) return; - - // Draw yellow portion - int progressWidth = 302 * currentSeconds / lengthSeconds; - rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 16, progressWidth, 16, Colour::SELECTHIGHLIGHT); -/* - - if (myRec->recInfo->timerEnd > time(NULL)) // if chasing - { - int nrWidth = (int)(302 * ((double)(lengthFrames - 0) / lengthFrames)); // 0 inserted instead of getlengthframes - - Log::getInstance()->log("GVASDF", Log::DEBUG, "Length Frames: %lu", lengthFrames); -// Log::getInstance()->log("GVASDF", Log::DEBUG, "Player lf: %lu", player->getLengthFrames()); - Log::getInstance()->log("GVASDF", Log::DEBUG, "NR WDITH: %i", nrWidth); - rectangle(barRegion.x + progBarXbase + 4 + 302 - nrWidth, barRegion.y + 16, nrWidth, 16, Colour::RED); - } -*/ - - logger->log("VRadioRec", Log::DEBUG, "blips"); - - // Now calc position for start margin blips - int posPix; - - posPix = 302 * startMargin / lengthSeconds; - logger->log("VRadioRec", Log::DEBUG, "posPix %i", posPix); - - rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 - 2, 2, 2, Colour::LIGHTTEXT); - rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 + 24, 2, 2, Colour::LIGHTTEXT); - - posPix = 302 * (lengthSeconds - endMargin) / lengthSeconds; - - rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 - 2, 2, 2, Colour::LIGHTTEXT); - rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 + 24, 2, 2, Colour::LIGHTTEXT); -} - -void VRadioRec::removeBar() -{ - if (!barShowing) return; - timers->cancelTimer(this, 2); - barShowing = false; - rectangle(barRegion, Colour::BLACK); - boxstack->update(this, &barRegion); -} - +/* + Copyright 2004-2006 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 "vradiorec.h" + +#include "command.h" +#include "osd.h" +#include "player.h" +#include "wsymbol.h" +#include "recording.h" +#include "message.h" +#include "vdr.h" +#include "video.h" +#include "timers.h" +#include "playerradio.h" +#include "boxstack.h" +#include "remote.h" +#include "vinfo.h" +#include "i18n.h" +#include "log.h" + +VRadioRec::VRadioRec(Recording* rec) +{ + boxstack = BoxStack::getInstance(); + vdr = VDR::getInstance(); + video = Video::getInstance(); + timers = Timers::getInstance(); + myRec = rec; + playing = false; + startMargin = 0; + endMargin = 0; + + player = new PlayerRadio(Command::getInstance(), this); + + char* cstartMargin = vdr->configLoad("Timers", "Start margin"); + char* cendMargin = vdr->configLoad("Timers", "End margin"); + if (!cstartMargin) + { + startMargin = 300; // 5 mins default + } + else + { + startMargin = atoi(cstartMargin) * 60; + delete[] cstartMargin; + } + + if (!cendMargin) + { + endMargin = 300; // 5 mins default + } + else + { + endMargin = atoi(cendMargin) * 60; + delete[] cendMargin; + } + + Log::getInstance()->log("VRadioRec", Log::DEBUG, "SM: %u EM: %u", startMargin, endMargin); + + setSize(video->getScreenWidth(), video->getScreenHeight()); + createBuffer(); + setPosition(0, 0); + + barRegion.x = 0; + barRegion.y = video->getScreenHeight() - 58; // FIXME, need to be - 1? and below? + barRegion.w = video->getScreenWidth(); + barRegion.h = 58; + + clocksRegion.x = barRegion.x + 140; + clocksRegion.y = barRegion.y + 12; + clocksRegion.w = 170; + clocksRegion.h = getFontHeight(); + + + barBlue.set(0, 0, 150, 150); + + barShowing = false; +} + +void VRadioRec::preDelete() +{ + timers->cancelTimer(this, 1); + timers->cancelTimer(this, 2); +} + +VRadioRec::~VRadioRec() +{ + if (playing) stopPlay(); + + + // kill recInfo in case resumePoint has changed (likely) + myRec->dropRecInfo(); + // FIXME - do this properly - save the resume point back to the server manually and update + // rec->recInfo->resumePoint - this will fix the ~10s offset problem as well +} + +void VRadioRec::draw() +{ + fillColour(Colour::BLACK); +} + +void VRadioRec::go() +{ + Log::getInstance()->log("VRadioRec", Log::DEBUG, "Starting stream: %s", myRec->getFileName()); + ULONG lengthFrames = 0; + bool isPesRecording; + ULLONG lengthBytes = vdr->streamRecording(myRec->getFileName(), &lengthFrames, &isPesRecording); + myRec->IsPesRecording = isPesRecording; + + bool cantStart = false; + + if (!lengthBytes) cantStart = true; + else if (!player->init(lengthBytes, lengthFrames, myRec->IsPesRecording)) cantStart = true; + else + { + doBar(0); + // player->setStartBytes(startBytes); + player->play(); + playing = true; + } + + if (cantStart) + { + stopPlay(); // clean up + + if (!vdr->isConnected()) + { + Command::getInstance()->connectionLost(); + return; + } + + Message* m = new Message(); + m->message = Message::CLOSE_ME; + m->from = this; + m->to = boxstack; + Command::getInstance()->postMessageNoLock(m); + + VInfo* vi = new VInfo(); + vi->setSize(400, 150); + vi->createBuffer(); + if (video->getFormat() == Video::PAL) + vi->setPosition(170, 200); + else + vi->setPosition(160, 150); + vi->setExitable(); + vi->setBorderOn(1); + vi->setTitleBarOn(0); + vi->setOneLiner(tr("Error playing recording")); + vi->draw(); + + m = new Message(); + m->message = Message::ADD_VIEW; + m->to = boxstack; + m->parameter = (ULONG)vi; + Command::getInstance()->postMessageNoLock(m); + } +} + +int VRadioRec::handleCommand(int command) +{ + switch(command) + { + case Remote::PLAY: + { + player->play(); + doBar(0); + return 2; + } + case Remote::PLAYPAUSE: + { + player->playpause(); + doBar(0); + return 2; + } + + case Remote::STOP: + case Remote::BACK: + case Remote::MENU: + { + if (playing) stopPlay(); + return 4; + } + case Remote::PAUSE: + { + player->pause(); + doBar(0); + return 2; + } + case Remote::SKIPFORWARD: + { + doBar(3); + player->skipForward(60); + return 2; + } + case Remote::SKIPBACK: + { + doBar(4); + player->skipBackward(60); + return 2; + } + case Remote::YELLOW: + { + doBar(2); + player->skipBackward(10); + return 2; + } + case Remote::BLUE: + { + doBar(1); + player->skipForward(10); + return 2; + } + case Remote::OK: + { + if (barShowing) removeBar(); + else doBar(0); + return 2; + } + + case Remote::ZERO: player->jumpToPercent(0); doBar(0); return 2; + case Remote::ONE: player->jumpToPercent(10); doBar(0); return 2; + case Remote::TWO: player->jumpToPercent(20); doBar(0); return 2; + case Remote::THREE: player->jumpToPercent(30); doBar(0); return 2; + case Remote::FOUR: player->jumpToPercent(40); doBar(0); return 2; + case Remote::FIVE: player->jumpToPercent(50); doBar(0); return 2; + case Remote::SIX: player->jumpToPercent(60); doBar(0); return 2; + case Remote::SEVEN: player->jumpToPercent(70); doBar(0); return 2; + case Remote::EIGHT: player->jumpToPercent(80); doBar(0); return 2; + case Remote::NINE: player->jumpToPercent(90); doBar(0); return 2; + +#ifdef DEV + case Remote::RED: + { + //player->test1(); + + return 2; + } + case Remote::GREEN: + { + //player->test2(); + return 2; + } +#endif + + } + + return 1; +} + +void VRadioRec::processMessage(Message* m) +{ + if (m->message == Message::MOUSE_LBDOWN) + { + int x=(m->parameter>>16)-(int)getScreenX(); + int y=(m->parameter&0xFFFF)-(int)getScreenY(); + if (!barShowing) + { + boxstack->handleCommand(Remote::OK); //simulate rok press + } + else if ((int)barRegion.x<=x && (int)barRegion.y<=y && ((int)barRegion.x+(int)barRegion.w)>=x + && ((int)barRegion.y+(int)barRegion.h)>=y) + { + int progBarXbase = barRegion.x + 300; + if (x>=(int)barRegion.x + progBarXbase + 24 + && x<=(int)barRegion.x + progBarXbase + 4 + 302 + && y>=(int)barRegion.y + 12 - 2 + && y<=(int)barRegion.y + 12 - 2+28) + { + int cx=x-(barRegion.x + progBarXbase + 4); + double percent=((double)cx)/302.*100.; + player->jumpToPercent(percent); + doBar(3); + return; + // int progressWidth = 302 * currentFrameNum / lengthFrames; + // rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 16, progressWidth, 16, Colour::SELECTHIGHLIGHT); + } + } + else + { + boxstack->handleCommand(Remote::OK); //simulate rok press + } + } + else if (m->message == Message::PLAYER_EVENT) + { + if (m->from != player) return; + + Log::getInstance()->log("VRadioRec", Log::DEBUG, "Message received"); + + switch(m->parameter) + { + case Player::CONNECTION_LOST: // connection lost detected + { + // I can't handle this, send it to command + Message* m2 = new Message(); + m2->to = Command::getInstance(); + m2->message = Message::CONNECTION_LOST; + Command::getInstance()->postMessageNoLock(m2); + break; + } + case Player::STOP_PLAYBACK: + { + // FIXME Obselete ish - improve this + Message* m2 = new Message(); // Must be done after this thread finishes, and must break into master mutex + m2->to = Command::getInstance(); + m2->message = Message::STOP_PLAYBACK; + Command::getInstance()->postMessageNoLock(m2); + break; + } + } + } +} + +void VRadioRec::stopPlay() +{ + Log::getInstance()->log("VRadioRec", Log::DEBUG, "Pre stopPlay"); + + removeBar(); + player->stop(); + vdr->stopStreaming(); + delete player; + + playing = false; + + if (!vdr->isConnected()) { Command::getInstance()->connectionLost(); return; } + Log::getInstance()->log("VRadioRec", Log::DEBUG, "Post stopPlay"); +} + +void VRadioRec::doBar(int action) +{ + barShowing = true; + + rectangle(barRegion, barBlue); + + /* Work out what to display - choices: + + Playing > + Paused || + + Specials, informed by parameter + + Skip forward 10s >| + Skip backward 10s |< + Skip forward 1m >>| + Skip backward 1m |<< + + */ + + WSymbol w; + TEMPADD(&w); + w.nextSymbol = 0; + w.setPosition(barRegion.x + 66, barRegion.y + 16); + + UCHAR playerState = 0; + + if (action) + { + if (action == 1) w.nextSymbol = WSymbol::SKIPFORWARD; + else if (action == 2) w.nextSymbol = WSymbol::SKIPBACK; + else if (action == 3) w.nextSymbol = WSymbol::SKIPFORWARD2; + else if (action == 4) w.nextSymbol = WSymbol::SKIPBACK2; + } + else + { + playerState = player->getState(); + if (playerState == Player::S_PAUSE_P) w.nextSymbol = WSymbol::PAUSE; + else if (playerState == Player::S_PAUSE_I) w.nextSymbol = WSymbol::PAUSE; + else w.nextSymbol = WSymbol::PLAY; + } + + w.draw(); + + drawBarClocks(); + + BoxStack::getInstance()->update(this, &barRegion); + + timers->setTimerD(this, 1, 4); // only set the getridofbar timer if not ffwd/fbwd + timers->setTimerD(this, 2, 0, 200000000); +} + +void VRadioRec::timercall(int clientReference) +{ + switch(clientReference) + { + case 1: + { + // Remove bar + removeBar(); + break; + } + case 2: + { + // Update clock + if (!barShowing) break; + drawBarClocks(); + boxstack->update(this, &barRegion); + + timers->setTimerD(this, 2, 0, 200000000); + break; + } + } +} + +void VRadioRec::drawBarClocks() +{ + Log* logger = Log::getInstance(); + logger->log("VRadioRec", Log::DEBUG, "Draw bar clocks"); + + // Draw RTC + // Blank the area first + rectangle(barRegion.x + 624, barRegion.y + 12, 60, 30, barBlue); + char timeString[20]; + time_t t; + time(&t); + struct tm* tms = localtime(&t); + strftime(timeString, 19, "%H:%M", tms); + drawText(timeString, barRegion.x + 624, barRegion.y + 12, Colour::LIGHTTEXT); + + // Draw clocks + + rectangle(clocksRegion, barBlue); + +/* + ULONG currentFrameNum = player->getCurrentFrameNum(); + ULONG lengthFrames; + if (myRec->recInfo->timerEnd > time(NULL)) + { + // chasing playback + // Work out an approximate length in frames (good to 1s...) + lengthFrames = (myRec->recInfo->timerEnd - myRec->recInfo->timerStart) * video->getFPS(); + } + else + { +// lengthFrames = player->getLengthFrames(); + lengthFrames = 0; + } + + hmsf currentFrameHMSF = video->framesToHMSF(currentFrameNum); + hmsf lengthHMSF = video->framesToHMSF(lengthFrames); + + char buffer[100]; + if (currentFrameNum >= lengthFrames) + { + strcpy(buffer, "-:--:-- / -:--:--"); + } + else + { + SNPRINTF(buffer, 99, "%01i:%02i:%02i / %01i:%02i:%02i", currentFrameHMSF.hours, currentFrameHMSF.minutes, currentFrameHMSF.seconds, lengthHMSF.hours, lengthHMSF.minutes, lengthHMSF.seconds); + logger->log("VRadioRec", Log::DEBUG, buffer); + } +*/ + + ULONG currentSeconds = player->getCurrentSeconds(); + ULONG lengthSeconds = player->getLengthSeconds(); + char buffer[100]; + + if (lengthSeconds && (currentSeconds < lengthSeconds)) + { + ULONG dcurrentSeconds = currentSeconds; + ULONG dlengthSeconds = lengthSeconds; + + ULONG currentHours = dcurrentSeconds / 3600; + dcurrentSeconds %= 3600; + ULONG currentMinutes = dcurrentSeconds / 60; + dcurrentSeconds %= 60; + + ULONG lengthHours = dlengthSeconds / 3600; + dlengthSeconds %= 3600; + ULONG lengthMinutes = dlengthSeconds / 60; + dlengthSeconds %= 60; + + SNPRINTF(buffer, 99, "%01lu:%02lu:%02lu / %01lu:%02lu:%02lu", currentHours, currentMinutes, dcurrentSeconds, lengthHours, lengthMinutes, dlengthSeconds); + logger->log("VRadioRec", Log::DEBUG, buffer); + } + else + { + strcpy(buffer, "-:--:-- / -:--:--"); + } + + drawText(buffer, clocksRegion.x, clocksRegion.y, Colour::LIGHTTEXT); + + // Draw progress bar + int progBarXbase = barRegion.x + 300; + + rectangle(barRegion.x + progBarXbase, barRegion.y + 12, 310, 24, Colour::LIGHTTEXT); + rectangle(barRegion.x + progBarXbase + 2, barRegion.y + 14, 306, 20, barBlue); + + if (currentSeconds > lengthSeconds) return; + if (lengthSeconds == 0) return; + + // Draw yellow portion + int progressWidth = 302 * currentSeconds / lengthSeconds; + rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 16, progressWidth, 16, Colour::SELECTHIGHLIGHT); +/* + + if (myRec->recInfo->timerEnd > time(NULL)) // if chasing + { + int nrWidth = (int)(302 * ((double)(lengthFrames - 0) / lengthFrames)); // 0 inserted instead of getlengthframes + + Log::getInstance()->log("GVASDF", Log::DEBUG, "Length Frames: %lu", lengthFrames); +// Log::getInstance()->log("GVASDF", Log::DEBUG, "Player lf: %lu", player->getLengthFrames()); + Log::getInstance()->log("GVASDF", Log::DEBUG, "NR WDITH: %i", nrWidth); + rectangle(barRegion.x + progBarXbase + 4 + 302 - nrWidth, barRegion.y + 16, nrWidth, 16, Colour::RED); + } +*/ + + logger->log("VRadioRec", Log::DEBUG, "blips"); + + // Now calc position for start margin blips + int posPix; + + posPix = 302 * startMargin / lengthSeconds; + logger->log("VRadioRec", Log::DEBUG, "posPix %i", posPix); + + rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 - 2, 2, 2, Colour::LIGHTTEXT); + rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 + 24, 2, 2, Colour::LIGHTTEXT); + + posPix = 302 * (lengthSeconds - endMargin) / lengthSeconds; + + rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 - 2, 2, 2, Colour::LIGHTTEXT); + rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 + 24, 2, 2, Colour::LIGHTTEXT); +} + +void VRadioRec::removeBar() +{ + if (!barShowing) return; + timers->cancelTimer(this, 2); + barShowing = false; + rectangle(barRegion, Colour::BLACK); + boxstack->update(this, &barRegion); +} + diff --git a/vrecmove.cc b/vrecmove.cc index 4a6a7fe..0ba3b16 100644 --- a/vrecmove.cc +++ b/vrecmove.cc @@ -72,7 +72,7 @@ void VRecMove::setParent(void* tparent) parent = tparent; } -void VRecMove::addDirs(Directory* dir, char* prefix) +void VRecMove::addDirs(Directory* dir,const char* prefix) { Directory* currentDir; for(DirectoryList::iterator i = dir->dirList.begin(); i != dir->dirList.end(); i++) diff --git a/vrecmove.h b/vrecmove.h index c38afe4..1c6fa93 100644 --- a/vrecmove.h +++ b/vrecmove.h @@ -46,7 +46,7 @@ class VRecMove : public TBBoxx void* parent; RecMan* recman; WSelectList sl; - void addDirs(Directory* dir, char* prefix); + void addDirs(Directory* dir,const char* prefix); }; #endif diff --git a/vrecording.cc b/vrecording.cc index f655ae0..104ff5b 100644 --- a/vrecording.cc +++ b/vrecording.cc @@ -1,388 +1,388 @@ -/* - Copyright 2004-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 "vrecording.h" - -#include "remote.h" -#include "recinfo.h" -#include "vquestion.h" -#include "vinfo.h" -#include "vdr.h" -#include "colour.h" -#include "video.h" -#include "i18n.h" -#include "command.h" -#include "vrecmove.h" -#include "boxstack.h" -#include "recman.h" -#include "vrecordinglist.h" -#include "recording.h" -#include "message.h" -#include "log.h" - -VRecording::VRecording(RecMan* trecman, Recording* trec) -{ - rec = trec; - recman = trecman; - - Log::getInstance()->log("VRecording", Log::DEBUG, "%s", rec->getProgName()); - rec->loadRecInfo(); - Log::getInstance()->log("VRecording", Log::DEBUG, "%s", rec->getProgName()); - - setSize(570, 420); - createBuffer(); - if (Video::getInstance()->getFormat() == Video::PAL) - { - setPosition(80, 70); - } - else - { - setPosition(70, 35); - } - - setTitleBarOn(1); - setBorderOn(1); - setTitleText(rec->getProgName()); - setTitleBarColour(Colour::TITLEBARBACKGROUND); - - summary.setPosition(10, 30 + 5); - summary.setSize(area.w - 20, area.h - 30 - 15 - 50); - summary.setParaMode(true); - - if (rec->recInfo &&strlen(rec->recInfo->summary)) - summary.setText(rec->recInfo->summary); - else - summary.setText(tr("Summary unavailable")); - - add(&summary); - - - buttonPlay.setPosition(10, area.h - 40); - buttonResume.setPosition(150, area.h - 40); - buttonMove.setPosition(290, area.h - 40); - buttonDelete.setPosition(430, area.h - 40); - - int sfh = Surface::getFontHeight(); - - buttonPlay.setSize(130, sfh); - buttonResume.setSize(130, sfh); - buttonMove.setSize(130, sfh); - buttonDelete.setSize(130, sfh); - - buttonRegion.x = 10; - buttonRegion.y = area.h - 40; - buttonRegion.w = 550; - buttonRegion.h = sfh; - - buttonPlay.setText(tr("Play")); - buttonResume.setText(tr("Resume")); - buttonMove.setText(tr("Move")); - buttonDelete.setText(tr("Delete")); - - add(&buttonPlay); - add(&buttonResume); - add(&buttonMove); - add(&buttonDelete); - - buttonPlay.setActive(1); - selected = 1; -} - -VRecording::~VRecording() -{ -} - -void VRecording::setParent(VRecordingList* tvRecList) -{ - vRecList = tvRecList; -} - -void VRecording::draw() -{ - TBBoxx::draw(); -} - -int VRecording::handleCommand(int command) -{ - switch(command) - { - case Remote::LEFT: - case Remote::DF_LEFT: - case Remote::DF_UP: - case Remote::UP: - { - doLeft(); - return 2; - } - case Remote::RIGHT: - case Remote::DF_RIGHT: - case Remote::DF_DOWN: - case Remote::DOWN: - { - doRight(); - return 2; - } - case Remote::OK: - { - - if (selected == 1) - { - Message* m = new Message(); // Must be done after this view deleted - m->from = this; - m->to = vRecList; - m->message = Message::PLAY_SELECTED_RECORDING; - Command::getInstance()->postMessageNoLock(m); - return 4; - } - - if (selected == 2) - { - Message* m = new Message(); // Must be done after this view deleted - m->from = this; - m->to = vRecList; - m->message = Message::RESUME_SELECTED_RECORDING; - Command::getInstance()->postMessageNoLock(m); - return 4; - } - - if (selected == 3) - { - VRecMove* vrm = new VRecMove(recman); - vrm->setParent(this); - vrm->draw(); - BoxStack::getInstance()->add(vrm); - BoxStack::getInstance()->update(vrm); - return 2; - } - - if (selected == 4) - { - VQuestion* v = new VQuestion(this); - v->setSize(260, 180); - v->createBuffer(); - v->setTitleBarColour(Colour::DANGER); - v->setTitleBarOn(1); - v->setBorderOn(1); - v->setTitleText(tr("Delete recording")); - v->setMainText(tr("Are you sure you want to delete this recording?")); - v->setDefault(VQuestion::NO); - if (Video::getInstance()->getFormat() == Video::PAL) - { - v->setPosition(230, 160); - } - else - { - v->setPosition(220, 140); - } - - v->draw(); - BoxStack::getInstance()->add(v); - BoxStack::getInstance()->update(v); - return 2; - } - } - - case Remote::BACK: - { - return 4; - } - } - // stop command getting to any more views - return 1; -} - -void VRecording::doRight() -{ - switch(selected) - { - case 1: - buttonPlay.setActive(0); - buttonResume.setActive(1); - buttonPlay.draw(); - buttonResume.draw(); - break; - case 2: - buttonResume.setActive(0); - buttonMove.setActive(1); - buttonResume.draw(); - buttonMove.draw(); - break; - case 3: - buttonMove.setActive(0); - buttonDelete.setActive(1); - buttonMove.draw(); - buttonDelete.draw(); - break; - case 4: - buttonDelete.setActive(0); - buttonPlay.setActive(1); - buttonDelete.draw(); - buttonPlay.draw(); - break; - } - - if (++selected == 5) selected = 1; - - BoxStack::getInstance()->update(this, &buttonRegion); -} - -void VRecording::doLeft() -{ - switch(selected) - { - case 1: - buttonPlay.setActive(0); - buttonDelete.setActive(1); - buttonPlay.draw(); - buttonDelete.draw(); - break; - case 2: - buttonResume.setActive(0); - buttonPlay.setActive(1); - buttonResume.draw(); - buttonPlay.draw(); - break; - case 3: - buttonMove.setActive(0); - buttonResume.setActive(1); - buttonMove.draw(); - buttonResume.draw(); - break; - case 4: - buttonDelete.setActive(0); - buttonMove.setActive(1); - buttonDelete.draw(); - buttonMove.draw(); - break; - } - - if (--selected == 0) selected = 4; - - BoxStack::getInstance()->update(this, &buttonRegion); -} - -void VRecording::processMessage(Message* m) -{ - if (m->message == Message::MOUSE_MOVE) - { - if (buttonPlay.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) - { - buttonPlay.setActive(1); - buttonResume.setActive(0); - buttonMove.setActive(0); - buttonDelete.setActive(0); - selected=1; - draw(); - BoxStack::getInstance()->update(this); - } - else if (buttonResume.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) - { - buttonPlay.setActive(0); - buttonResume.setActive(1); - buttonMove.setActive(0); - buttonDelete.setActive(0); - selected=2; - draw(); - BoxStack::getInstance()->update(this); - } - else if (buttonMove.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) - { - buttonPlay.setActive(0); - buttonResume.setActive(0); - buttonMove.setActive(1); - buttonDelete.setActive(0); - selected=3; - draw(); - BoxStack::getInstance()->update(this); - } - else if (buttonDelete.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) - { - buttonPlay.setActive(0); - buttonResume.setActive(0); - buttonMove.setActive(0); - buttonDelete.setActive(1); - selected=4; - draw(); - BoxStack::getInstance()->update(this); - } - } - else if (m->message == Message::MOUSE_LBDOWN) - { - if (buttonPlay.mouseLBDOWN((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) - { - BoxStack::getInstance()->handleCommand(Remote::OK); //simulate OK press - } - else if (buttonResume.mouseLBDOWN((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) - { - BoxStack::getInstance()->handleCommand(Remote::OK); //simulate OK press - } - else if (buttonMove.mouseLBDOWN((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) - { - BoxStack::getInstance()->handleCommand(Remote::OK); //simulate OK press - } - else if (buttonDelete.mouseLBDOWN((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) - { - BoxStack::getInstance()->handleCommand(Remote::OK); //simulate OK press - } - else - { - //check if press is outside this view! then simulate cancel - int x=(m->parameter>>16)-getScreenX(); - int y=(m->parameter&0xFFFF)-getScreenY(); - if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight()) - { - BoxStack::getInstance()->handleCommand(Remote::BACK); //simulate cancel press - } - } - } - else if (m->message == Message::QUESTION_YES) - { - if (selected == 4) - { - Message* m2 = new Message(); // Delete self - m2->from = this; - m2->to = BoxStack::getInstance(); - m2->message = Message::CLOSE_ME; - Command::getInstance()->postMessageNoLock(m2); - - m2 = new Message(); // OK. Want this to delete before this message does its job - m2->from = this; - m2->to = vRecList; - m2->message = Message::DELETE_SELECTED_RECORDING; - Command::getInstance()->postMessageNoLock(m2); - } - } - else if (m->message == Message::MOVE_RECORDING) - { - Message* m2 = new Message(); // Delete self - m2->from = this; - m2->to = BoxStack::getInstance(); - m2->message = Message::CLOSE_ME; - Command::getInstance()->postMessageNoLock(m2); - - m2 = new Message(); - m2->from = this; - m2->to = vRecList; - m2->message = Message::MOVE_RECORDING; - m2->parameter = m->parameter; - Command::getInstance()->postMessageNoLock(m2); - } -} - +/* + Copyright 2004-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 "vrecording.h" + +#include "remote.h" +#include "recinfo.h" +#include "vquestion.h" +#include "vinfo.h" +#include "vdr.h" +#include "colour.h" +#include "video.h" +#include "i18n.h" +#include "command.h" +#include "vrecmove.h" +#include "boxstack.h" +#include "recman.h" +#include "vrecordinglist.h" +#include "recording.h" +#include "message.h" +#include "log.h" + +VRecording::VRecording(RecMan* trecman, Recording* trec) +{ + rec = trec; + recman = trecman; + + Log::getInstance()->log("VRecording", Log::DEBUG, "%s", rec->getProgName()); + rec->loadRecInfo(); + Log::getInstance()->log("VRecording", Log::DEBUG, "%s", rec->getProgName()); + + setSize(570, 420); + createBuffer(); + if (Video::getInstance()->getFormat() == Video::PAL) + { + setPosition(80, 70); + } + else + { + setPosition(70, 35); + } + + setTitleBarOn(1); + setBorderOn(1); + setTitleText(rec->getProgName()); + setTitleBarColour(Colour::TITLEBARBACKGROUND); + + summary.setPosition(10, 30 + 5); + summary.setSize(area.w - 20, area.h - 30 - 15 - 50); + summary.setParaMode(true); + + if (rec->recInfo &&strlen(rec->recInfo->summary)) + summary.setText(rec->recInfo->summary); + else + summary.setText(tr("Summary unavailable")); + + add(&summary); + + + buttonPlay.setPosition(10, area.h - 40); + buttonResume.setPosition(150, area.h - 40); + buttonMove.setPosition(290, area.h - 40); + buttonDelete.setPosition(430, area.h - 40); + + int sfh = getFontHeight(); + + buttonPlay.setSize(130, sfh); + buttonResume.setSize(130, sfh); + buttonMove.setSize(130, sfh); + buttonDelete.setSize(130, sfh); + + buttonRegion.x = 10; + buttonRegion.y = area.h - 40; + buttonRegion.w = 550; + buttonRegion.h = sfh; + + buttonPlay.setText(tr("Play")); + buttonResume.setText(tr("Resume")); + buttonMove.setText(tr("Move")); + buttonDelete.setText(tr("Delete")); + + add(&buttonPlay); + add(&buttonResume); + add(&buttonMove); + add(&buttonDelete); + + buttonPlay.setActive(1); + selected = 1; +} + +VRecording::~VRecording() +{ +} + +void VRecording::setParent(VRecordingList* tvRecList) +{ + vRecList = tvRecList; +} + +void VRecording::draw() +{ + TBBoxx::draw(); +} + +int VRecording::handleCommand(int command) +{ + switch(command) + { + case Remote::LEFT: + case Remote::DF_LEFT: + case Remote::DF_UP: + case Remote::UP: + { + doLeft(); + return 2; + } + case Remote::RIGHT: + case Remote::DF_RIGHT: + case Remote::DF_DOWN: + case Remote::DOWN: + { + doRight(); + return 2; + } + case Remote::OK: + { + + if (selected == 1) + { + Message* m = new Message(); // Must be done after this view deleted + m->from = this; + m->to = vRecList; + m->message = Message::PLAY_SELECTED_RECORDING; + Command::getInstance()->postMessageNoLock(m); + return 4; + } + + if (selected == 2) + { + Message* m = new Message(); // Must be done after this view deleted + m->from = this; + m->to = vRecList; + m->message = Message::RESUME_SELECTED_RECORDING; + Command::getInstance()->postMessageNoLock(m); + return 4; + } + + if (selected == 3) + { + VRecMove* vrm = new VRecMove(recman); + vrm->setParent(this); + vrm->draw(); + BoxStack::getInstance()->add(vrm); + BoxStack::getInstance()->update(vrm); + return 2; + } + + if (selected == 4) + { + VQuestion* v = new VQuestion(this); + v->setSize(260, 180); + v->createBuffer(); + v->setTitleBarColour(Colour::DANGER); + v->setTitleBarOn(1); + v->setBorderOn(1); + v->setTitleText(tr("Delete recording")); + v->setMainText(tr("Are you sure you want to delete this recording?")); + v->setDefault(VQuestion::NO); + if (Video::getInstance()->getFormat() == Video::PAL) + { + v->setPosition(230, 160); + } + else + { + v->setPosition(220, 140); + } + + v->draw(); + BoxStack::getInstance()->add(v); + BoxStack::getInstance()->update(v); + return 2; + } + } + + case Remote::BACK: + { + return 4; + } + } + // stop command getting to any more views + return 1; +} + +void VRecording::doRight() +{ + switch(selected) + { + case 1: + buttonPlay.setActive(0); + buttonResume.setActive(1); + buttonPlay.draw(); + buttonResume.draw(); + break; + case 2: + buttonResume.setActive(0); + buttonMove.setActive(1); + buttonResume.draw(); + buttonMove.draw(); + break; + case 3: + buttonMove.setActive(0); + buttonDelete.setActive(1); + buttonMove.draw(); + buttonDelete.draw(); + break; + case 4: + buttonDelete.setActive(0); + buttonPlay.setActive(1); + buttonDelete.draw(); + buttonPlay.draw(); + break; + } + + if (++selected == 5) selected = 1; + + BoxStack::getInstance()->update(this, &buttonRegion); +} + +void VRecording::doLeft() +{ + switch(selected) + { + case 1: + buttonPlay.setActive(0); + buttonDelete.setActive(1); + buttonPlay.draw(); + buttonDelete.draw(); + break; + case 2: + buttonResume.setActive(0); + buttonPlay.setActive(1); + buttonResume.draw(); + buttonPlay.draw(); + break; + case 3: + buttonMove.setActive(0); + buttonResume.setActive(1); + buttonMove.draw(); + buttonResume.draw(); + break; + case 4: + buttonDelete.setActive(0); + buttonMove.setActive(1); + buttonDelete.draw(); + buttonMove.draw(); + break; + } + + if (--selected == 0) selected = 4; + + BoxStack::getInstance()->update(this, &buttonRegion); +} + +void VRecording::processMessage(Message* m) +{ + if (m->message == Message::MOUSE_MOVE) + { + if (buttonPlay.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) + { + buttonPlay.setActive(1); + buttonResume.setActive(0); + buttonMove.setActive(0); + buttonDelete.setActive(0); + selected=1; + draw(); + BoxStack::getInstance()->update(this); + } + else if (buttonResume.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) + { + buttonPlay.setActive(0); + buttonResume.setActive(1); + buttonMove.setActive(0); + buttonDelete.setActive(0); + selected=2; + draw(); + BoxStack::getInstance()->update(this); + } + else if (buttonMove.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) + { + buttonPlay.setActive(0); + buttonResume.setActive(0); + buttonMove.setActive(1); + buttonDelete.setActive(0); + selected=3; + draw(); + BoxStack::getInstance()->update(this); + } + else if (buttonDelete.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) + { + buttonPlay.setActive(0); + buttonResume.setActive(0); + buttonMove.setActive(0); + buttonDelete.setActive(1); + selected=4; + draw(); + BoxStack::getInstance()->update(this); + } + } + else if (m->message == Message::MOUSE_LBDOWN) + { + if (buttonPlay.mouseLBDOWN((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) + { + BoxStack::getInstance()->handleCommand(Remote::OK); //simulate OK press + } + else if (buttonResume.mouseLBDOWN((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) + { + BoxStack::getInstance()->handleCommand(Remote::OK); //simulate OK press + } + else if (buttonMove.mouseLBDOWN((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) + { + BoxStack::getInstance()->handleCommand(Remote::OK); //simulate OK press + } + else if (buttonDelete.mouseLBDOWN((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) + { + BoxStack::getInstance()->handleCommand(Remote::OK); //simulate OK press + } + else + { + //check if press is outside this view! then simulate cancel + int x=(m->parameter>>16)-getScreenX(); + int y=(m->parameter&0xFFFF)-getScreenY(); + if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight()) + { + BoxStack::getInstance()->handleCommand(Remote::BACK); //simulate cancel press + } + } + } + else if (m->message == Message::QUESTION_YES) + { + if (selected == 4) + { + Message* m2 = new Message(); // Delete self + m2->from = this; + m2->to = BoxStack::getInstance(); + m2->message = Message::CLOSE_ME; + Command::getInstance()->postMessageNoLock(m2); + + m2 = new Message(); // OK. Want this to delete before this message does its job + m2->from = this; + m2->to = vRecList; + m2->message = Message::DELETE_SELECTED_RECORDING; + Command::getInstance()->postMessageNoLock(m2); + } + } + else if (m->message == Message::MOVE_RECORDING) + { + Message* m2 = new Message(); // Delete self + m2->from = this; + m2->to = BoxStack::getInstance(); + m2->message = Message::CLOSE_ME; + Command::getInstance()->postMessageNoLock(m2); + + m2 = new Message(); + m2->from = this; + m2->to = vRecList; + m2->message = Message::MOVE_RECORDING; + m2->parameter = m->parameter; + Command::getInstance()->postMessageNoLock(m2); + } +} + diff --git a/vrecordinglist.cc b/vrecordinglist.cc index bf96f2b..f46050c 100644 --- a/vrecordinglist.cc +++ b/vrecordinglist.cc @@ -1,558 +1,548 @@ -/* - Copyright 2004-2007 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 "vrecordinglist.h" - -#include "recman.h" -#include "directory.h" -#include "recording.h" -#include "remote.h" -#include "wsymbol.h" -#include "boxstack.h" -#include "vrecordingmenu.h" -#include "vrecording.h" -#include "vdr.h" -#include "vvideorec.h" -#include "vradiorec.h" -#include "colour.h" -#include "video.h" -#include "i18n.h" -#include "command.h" -#include "vinfo.h" -#include "log.h" - -VRecordingList::VRecordingList() -{ - boxstack = BoxStack::getInstance(); - recman = NULL; - loading = true; - - setSize(570, 420); - createBuffer(); - if (Video::getInstance()->getFormat() == Video::PAL) - { - setPosition(80, 70); - } - else - { - setPosition(70, 35); - } - - setTitleBarOn(1); - setTitleBarColour(Colour::TITLEBARBACKGROUND); - - sl.setPosition(10, 30 + 5); - sl.setSize(area.w - 20, area.h - 30 - 15 - 30); - add(&sl); -} - -VRecordingList::~VRecordingList() -{ - delete recman; -} - -void VRecordingList::drawData(bool doIndexPop) -{ - int saveIndex = sl.getCurrentOption(); - int saveTop = sl.getTopOption(); - - sl.clear(); - sl.addColumn(0); - sl.addColumn(110); - - int first = 1; - - char tempA[300]; // FIXME this is guesswork! - char tempB[300]; // FIXME - struct tm* btime; - - Directory* currentSubDir; - DirectoryList::iterator i; - DirectoryList* dirList = recman->getDirectories(); - for (i = dirList->begin(); i != dirList->end(); i++) - { - currentSubDir = *i; - SNPRINTF(tempA, 299, tr(" %lu\t%s"), currentSubDir->getNumRecordings(), currentSubDir->name); - currentSubDir->index = sl.addOption(tempA, 0, first); - first = 0; - } - - // FIXME convert the whole program to time_t's - - Recording* currentRec; - RecordingList::iterator j; - RecordingList* recList = recman->getRecordings(); - for (j = recList->begin(); j != recList->end(); j++) - { - currentRec = *j; - time_t recStartTime = (time_t)currentRec->getStartTime(); - btime = localtime(&recStartTime); -//NMT does not like this too! - //#ifndef _MSC_VER -// strftime(tempA, 299, "%0d/%0m %0H:%0M ", btime); -//#else - strftime(tempA, 299, "%d/%m %H:%M ", btime); -//#endif - sprintf(tempB, "%s\t%s", tempA, currentRec->getProgName()); - currentRec->index = sl.addOption(tempB, 0, first); - first = 0; - } - - if (doIndexPop) - { - sl.hintSetCurrent(slIndexStack.top()); - slIndexStack.pop(); - } - else - { - sl.hintSetCurrent(saveIndex); - sl.hintSetTop(saveTop); - } - sl.draw(); - doShowingBar(); -} - -void VRecordingList::draw(bool doIndexPop) -{ - if (!loading) - { - if (recman->isSubDir()) - { - char title[300]; - SNPRINTF(title, 299, tr("Recordings - %s"), recman->getCurDirName()); - setTitleText(title, 364); - } - else - { - setTitleText(tr("Recordings")); - } - } - - TBBoxx::draw(); - - if (loading) - { - drawText(tr("Loading..."), 240, 180, Colour::LIGHTTEXT); - } - else - { - char freeSpace[50]; - int gigFree = recman->getFreeSpace() / 1024; - SNPRINTF(freeSpace, 49, tr("%lu%% used, %iGB free"), recman->getUsedPercent(), gigFree); - drawTextRJ(freeSpace, 560, 5, Colour::LIGHTTEXT); - - // Symbols - - WSymbol w; - TEMPADD(&w); - - w.nextSymbol = WSymbol::UP; - w.setPosition(20, 385); - w.draw(); - - w.nextSymbol = WSymbol::DOWN; - w.setPosition(50, 385); - w.draw(); - - w.nextSymbol = WSymbol::SKIPBACK; - w.setPosition(85, 385); - w.draw(); - - w.nextSymbol = WSymbol::SKIPFORWARD; - w.setPosition(115, 385); - w.draw(); - - w.nextSymbol = WSymbol::PLAY; - w.setPosition(150, 385); - w.draw(); - - drawTextRJ(tr("[ok] = menu"), 560, 385, Colour::LIGHTTEXT); - - // All static stuff done - - drawData(doIndexPop); - } -} - -void VRecordingList::doShowingBar() -{ - int topOption = sl.getTopOption() + 1; - if (sl.getNumOptions() == 0) topOption = 0; - - rectangle(220, 385, 180, 25, Colour::VIEWBACKGROUND); - char showing[200]; - sprintf(showing, tr("%i to %i of %i"), topOption, sl.getBottomOption(), sl.getNumOptions()); - drawText(showing, 220, 385, Colour::LIGHTTEXT); -} - -void VRecordingList::processMessage(Message* m) -{ - Log::getInstance()->log("VRecordingList", Log::DEBUG, "Got message value %lu", m->message); - - if (m->message == Message::MOUSE_MOVE) - { - if (sl.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) - { - sl.draw(); - doShowingBar(); - boxstack->update(this); - } - } - else if (m->message == Message::MOUSE_LBDOWN) - { - if (sl.mouseLBDOWN((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) - { - boxstack->handleCommand(Remote::OK); //simulate OK press - } - else - { - //check if press is outside this view! then simulate cancel - int x=(m->parameter>>16)-getScreenX(); - int y=(m->parameter&0xFFFF)-getScreenY(); - if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight()) - { - boxstack->handleCommand(Remote::BACK); //simulate cancel press - } - } - } - else if (m->message == Message::DELETE_SELECTED_RECORDING) - { - Log::getInstance()->log("VRecordingList", Log::DEBUG, "Doing delete selected"); - doDeleteSelected(); - } - else if (m->message == Message::MOVE_RECORDING) - { - Log::getInstance()->log("VRecordingList", Log::DEBUG, "Doing move recording"); - doMoveRecording((Directory*)m->parameter); - } - else if (m->message == Message::PLAY_SELECTED_RECORDING) - { - doPlay(false); - } - else if (m->message == Message::RESUME_SELECTED_RECORDING) - { - doPlay(true); - } -} - -void VRecordingList::doDeleteSelected() -{ - Recording* toDelete = getCurrentOptionRecording(); - - if (!toDelete) return; - - Log::getInstance()->log("VRecordingList", Log::DEBUG, "FOUND: %i %s %s", toDelete->index, toDelete->getProgName(), toDelete->getFileName()); - - int success = recman->deleteRecording(toDelete); - if (!VDR::getInstance()->isConnected()) - { - Command::getInstance()->connectionLost(); - return; - } - - if (success != 1) - { - VInfo* vi = new VInfo(); - vi->setSize(360, 200); - vi->createBuffer(); - if (Video::getInstance()->getFormat() == Video::PAL) - vi->setPosition(190, 170); - else - vi->setPosition(180, 120); - vi->setOneLiner(tr("Failed to delete recording")); - vi->setExitable(); - vi->setBorderOn(1); - vi->setTitleBarColour(Colour::DANGER); - vi->okButton(); - vi->draw(); - boxstack->add(vi); - boxstack->update(vi); - } - else - { - draw(); - boxstack->update(this); - } - -} - -void VRecordingList::doMoveRecording(Directory* toDir) -{ - Recording* toMove = getCurrentOptionRecording(); - if (!toMove || !toDir) return; - - Log::getInstance()->log("VRecordingList", Log::DEBUG, "MOVE: %s %s", toMove->getProgName(), toDir->name); - - int success = recman->moveRecording(toMove, toDir); - if (!VDR::getInstance()->isConnected()) - { - Command::getInstance()->connectionLost(); - return; - } - - if (success != 1) - { - VInfo* vi = new VInfo(); - vi->setSize(360, 200); - vi->createBuffer(); - if (Video::getInstance()->getFormat() == Video::PAL) - vi->setPosition(190, 170); - else - vi->setPosition(180, 120); - vi->setOneLiner(tr("Failed to move recording")); - vi->setExitable(); - vi->setBorderOn(1); - vi->setTitleBarColour(Colour::DANGER); - vi->okButton(); - vi->draw(); - boxstack->add(vi); - boxstack->update(vi); - } - else - { - draw(); - boxstack->update(this); - } -} - -int VRecordingList::doPlay(bool resume) -{ - Recording* toPlay = getCurrentOptionRecording(); - if (toPlay) - { - toPlay->loadRecInfo(); // check if still need this - toPlay->loadMarks(); - bool ish264; - - bool isRadio = toPlay->isRadio(ish264); - - if (isRadio) - { - VRadioRec* radrec = new VRadioRec(toPlay); - radrec->draw(); - boxstack->add(radrec); - boxstack->update(radrec); - radrec->go(); - } - else - { - if (ish264 && !Video::getInstance()->supportsh264()) { - VInfo* vi = new VInfo(); - vi->setSize(360, 200); - vi->createBuffer(); - if (Video::getInstance()->getFormat() == Video::PAL) - vi->setPosition(190, 170); - else - vi->setPosition(180, 120); - vi->setOneLiner(tr("H264 video not supported")); - vi->setExitable(); - vi->setBorderOn(1); - vi->setTitleBarColour(Colour::DANGER); - vi->okButton(); - vi->draw(); - boxstack->add(vi); - boxstack->update(vi); - - } else { - VVideoRec* vidrec = new VVideoRec(toPlay, ish264); - vidrec->draw(); - boxstack->add(vidrec); - boxstack->update(vidrec); - vidrec->go(resume); - } - } - return 1; - } - // should not get to here - return 0; -} - -Recording* VRecordingList::getCurrentOptionRecording() -{ - Recording* currentRec; - RecordingList::iterator j; - RecordingList* recList = recman->getRecordings(); - for (j = recList->begin(); j != recList->end(); j++) - { - currentRec = *j; - if (currentRec->index == sl.getCurrentOption()) return currentRec; - } - - return NULL; -} - -int VRecordingList::handleCommand(int command) -{ - switch(command) - { - case Remote::DF_UP: - case Remote::UP: - { - sl.up(); - sl.draw(); - - doShowingBar(); - boxstack->update(this); - return 2; - } - case Remote::DF_DOWN: - case Remote::DOWN: - { - sl.down(); - sl.draw(); - - doShowingBar(); - boxstack->update(this); - return 2; - } - case Remote::SKIPBACK: - { - sl.pageUp(); - sl.draw(); - - doShowingBar(); - boxstack->update(this); - return 2; - } - case Remote::SKIPFORWARD: - { - sl.pageDown(); - sl.draw(); - - doShowingBar(); - boxstack->update(this); - return 2; - } - case Remote::OK: - { - if (sl.getNumOptions() == 0) return 2; - - // Check to see if it is a sub directory - Directory* currentSubDir; - DirectoryList::iterator i; - DirectoryList* dirList = recman->getDirectories(); - for (i = dirList->begin(); i != dirList->end(); i++) - { - currentSubDir = *i; - if (currentSubDir->index == sl.getCurrentOption()) - { - if (recman->down(currentSubDir)) - { - slIndexStack.push(sl.getCurrentOption()); - sl.clear(); - draw(); - boxstack->update(this); - } - return 2; - } - } - - // check to see if it's a recording - Recording* current = getCurrentOptionRecording(); - if (current) - { - Log::getInstance()->log("VRecordingList", Log::DEBUG, "Found the option you pointed at. %s %s", current->getProgName(), current->getFileName()); - -/* - VRecordingMenu* v = new VRecordingMenu(recman); - v->setParent(this); - v->setRecording(current); - v->draw(); - boxstack->add(v); - boxstack->update(v); -*/ - VRecording* vr = new VRecording(recman, current); - vr->setParent(this); - vr->draw(); - boxstack->add(vr); - boxstack->update(vr); - - return 2; - } - // should not get to here - return 1; - } - case Remote::BACK: - { - if (recman->isSubDir()) - { - recman->up(); - sl.clear(); - draw(true); - boxstack->update(this); - return 2; - } - else - { - return 4; - } - } - case Remote::PLAY: - { - if (doPlay(true)) return 2; - return 1; - } - case Remote::LEFT: - case Remote::RIGHT: - case Remote::ZERO: - { - reSort(); - return 2; - } - } - // stop command getting to any more views - return 1; -} - -bool VRecordingList::load() -{ - VDR* vdr = VDR::getInstance(); - - recman = new RecMan(); - bool success = vdr->getRecordingsList(recman); - if (success) - { - loading = false; - - char* defaultSortOrder = vdr->configLoad("General", "Recordings Sort Order"); - if (defaultSortOrder) - { - if (!STRCASECMP(defaultSortOrder, "Chronological")) recman->setSortOrderChron(); - delete[] defaultSortOrder; - } - - recman->sort(); - - draw(); - boxstack->update(this); - } - - return success; -} - -void VRecordingList::reSort() -{ - recman->toggleSortOrder(); - recman->sort(); - sl.clear(); - draw(); - boxstack->update(this); -} - +/* + Copyright 2004-2007 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 "vrecordinglist.h" + +#include "recman.h" +#include "directory.h" +#include "recording.h" +#include "remote.h" +#include "wsymbol.h" +#include "boxstack.h" +#include "vrecordingmenu.h" +#include "vrecording.h" +#include "vdr.h" +#include "vvideorec.h" +#include "vradiorec.h" +#include "colour.h" +#include "video.h" +#include "i18n.h" +#include "command.h" +#include "vinfo.h" +#include "log.h" + +VRecordingList::VRecordingList() +{ + boxstack = BoxStack::getInstance(); + recman = NULL; + loading = true; + + setSize(570, 420); + createBuffer(); + if (Video::getInstance()->getFormat() == Video::PAL) + { + setPosition(80, 70); + } + else + { + setPosition(70, 35); + } + + setTitleBarOn(1); + setTitleBarColour(Colour::TITLEBARBACKGROUND); + + sl.setPosition(10, 30 + 5); + sl.setSize(area.w - 20, area.h - 30 - 15 - 30); + add(&sl); +} + +VRecordingList::~VRecordingList() +{ + delete recman; +} + +void VRecordingList::drawData(bool doIndexPop) +{ + int saveIndex = sl.getCurrentOption(); + int saveTop = sl.getTopOption(); + sl.clear(); + sl.addColumn(0); + sl.addColumn(110); + + int first = 1; + + char tempA[300]; // FIXME this is guesswork! + char tempB[300]; // FIXME + struct tm* btime; + + Directory* currentSubDir; + DirectoryList::iterator i; + DirectoryList* dirList = recman->getDirectories(); + for (i = dirList->begin(); i != dirList->end(); i++) + { + currentSubDir = *i; + SNPRINTF(tempA, 299, tr(" %lu\t%s"), currentSubDir->getNumRecordings(), currentSubDir->name); + currentSubDir->index = sl.addOption(tempA, 0, first); + first = 0; + } + // FIXME convert the whole program to time_t's + + Recording* currentRec; + RecordingList::iterator j; + RecordingList* recList = recman->getRecordings(); + for (j = recList->begin(); j != recList->end(); j++) + { + currentRec = *j; + time_t recStartTime = (time_t)currentRec->getStartTime(); + btime = localtime(&recStartTime); +//NMT does not like this too! + //#ifndef _MSC_VER +// strftime(tempA, 299, "%0d/%0m %0H:%0M ", btime); +//#else + strftime(tempA, 299, "%d/%m %H:%M ", btime); +//#endif + sprintf(tempB, "%s\t%s", tempA, currentRec->getProgName()); + currentRec->index = sl.addOption(tempB, 0, first); + first = 0; + } + + if (doIndexPop) + { + sl.hintSetCurrent(slIndexStack.top()); + slIndexStack.pop(); + } + else + { + sl.hintSetCurrent(saveIndex); + sl.hintSetTop(saveTop); + } + sl.draw(); + doShowingBar(); +} + +void VRecordingList::draw(bool doIndexPop) +{ + if (!loading) + { + if (recman->isSubDir()) + { + char title[300]; + SNPRINTF(title, 299, tr("Recordings - %s"), recman->getCurDirName()); + setTitleText(title, 364); + } + else + { + setTitleText(tr("Recordings")); + } + } + + TBBoxx::draw(); + + if (loading) + { + drawText(tr("Loading..."), 240, 180, Colour::LIGHTTEXT); + } + else + { + char freeSpace[50]; + int gigFree = recman->getFreeSpace() / 1024; + SNPRINTF(freeSpace, 49, tr("%lu%% used, %iGB free"), recman->getUsedPercent(), gigFree); + drawTextRJ(freeSpace, 560, 5, Colour::LIGHTTEXT); + // Symbols + + WSymbol w; + TEMPADD(&w); + w.nextSymbol = WSymbol::UP; + w.setPosition(20, 385); + w.draw(); + w.nextSymbol = WSymbol::DOWN; + w.setPosition(50, 385); + w.draw(); + w.nextSymbol = WSymbol::SKIPBACK; + w.setPosition(85, 385); + w.draw(); + w.nextSymbol = WSymbol::SKIPFORWARD; + w.setPosition(115, 385); + w.draw(); + w.nextSymbol = WSymbol::PLAY; + w.setPosition(150, 385); + w.draw(); + drawTextRJ(tr("[ok] = menu"), 560, 385, Colour::LIGHTTEXT); + + // All static stuff done + drawData(doIndexPop); + } +} + +void VRecordingList::doShowingBar() +{ + int topOption = sl.getTopOption() + 1; + if (sl.getNumOptions() == 0) topOption = 0; + + rectangle(220, 385, 180, 25, Colour::VIEWBACKGROUND); + char showing[200]; + sprintf(showing, tr("%i to %i of %i"), topOption, sl.getBottomOption(), sl.getNumOptions()); + drawText(showing, 220, 385, Colour::LIGHTTEXT); +} + +void VRecordingList::processMessage(Message* m) +{ + Log::getInstance()->log("VRecordingList", Log::DEBUG, "Got message value %lu", m->message); + + if (m->message == Message::MOUSE_MOVE) + { + if (sl.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) + { + sl.draw(); + doShowingBar(); + boxstack->update(this); + } + } + else if (m->message == Message::MOUSE_LBDOWN) + { + if (sl.mouseLBDOWN((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) + { + boxstack->handleCommand(Remote::OK); //simulate OK press + } + else + { + //check if press is outside this view! then simulate cancel + int x=(m->parameter>>16)-getScreenX(); + int y=(m->parameter&0xFFFF)-getScreenY(); + if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight()) + { + boxstack->handleCommand(Remote::BACK); //simulate cancel press + } + } + } + else if (m->message == Message::DELETE_SELECTED_RECORDING) + { + Log::getInstance()->log("VRecordingList", Log::DEBUG, "Doing delete selected"); + doDeleteSelected(); + } + else if (m->message == Message::MOVE_RECORDING) + { + Log::getInstance()->log("VRecordingList", Log::DEBUG, "Doing move recording"); + doMoveRecording((Directory*)m->parameter); + } + else if (m->message == Message::PLAY_SELECTED_RECORDING) + { + doPlay(false); + } + else if (m->message == Message::RESUME_SELECTED_RECORDING) + { + doPlay(true); + } +} + +void VRecordingList::doDeleteSelected() +{ + Recording* toDelete = getCurrentOptionRecording(); + + if (!toDelete) return; + + Log::getInstance()->log("VRecordingList", Log::DEBUG, "FOUND: %i %s %s", toDelete->index, toDelete->getProgName(), toDelete->getFileName()); + + int success = recman->deleteRecording(toDelete); + if (!VDR::getInstance()->isConnected()) + { + Command::getInstance()->connectionLost(); + return; + } + + if (success != 1) + { + VInfo* vi = new VInfo(); + vi->setSize(360, 200); + vi->createBuffer(); + if (Video::getInstance()->getFormat() == Video::PAL) + vi->setPosition(190, 170); + else + vi->setPosition(180, 120); + vi->setOneLiner(tr("Failed to delete recording")); + vi->setExitable(); + vi->setBorderOn(1); + vi->setTitleBarColour(Colour::DANGER); + vi->okButton(); + vi->draw(); + boxstack->add(vi); + boxstack->update(vi); + } + else + { + draw(); + boxstack->update(this); + } + +} + +void VRecordingList::doMoveRecording(Directory* toDir) +{ + Recording* toMove = getCurrentOptionRecording(); + if (!toMove || !toDir) return; + + Log::getInstance()->log("VRecordingList", Log::DEBUG, "MOVE: %s %s", toMove->getProgName(), toDir->name); + + int success = recman->moveRecording(toMove, toDir); + if (!VDR::getInstance()->isConnected()) + { + Command::getInstance()->connectionLost(); + return; + } + + if (success != 1) + { + VInfo* vi = new VInfo(); + vi->setSize(360, 200); + vi->createBuffer(); + if (Video::getInstance()->getFormat() == Video::PAL) + vi->setPosition(190, 170); + else + vi->setPosition(180, 120); + vi->setOneLiner(tr("Failed to move recording")); + vi->setExitable(); + vi->setBorderOn(1); + vi->setTitleBarColour(Colour::DANGER); + vi->okButton(); + vi->draw(); + boxstack->add(vi); + boxstack->update(vi); + } + else + { + draw(); + boxstack->update(this); + } +} + +int VRecordingList::doPlay(bool resume) +{ + Recording* toPlay = getCurrentOptionRecording(); + if (toPlay) + { + toPlay->loadRecInfo(); // check if still need this + toPlay->loadMarks(); + bool ish264; + + bool isRadio = toPlay->isRadio(ish264); + + if (isRadio) + { + VRadioRec* radrec = new VRadioRec(toPlay); + radrec->draw(); + boxstack->add(radrec); + boxstack->update(radrec); + radrec->go(); + } + else + { + if (ish264 && !Video::getInstance()->supportsh264()) { + VInfo* vi = new VInfo(); + vi->setSize(360, 200); + vi->createBuffer(); + if (Video::getInstance()->getFormat() == Video::PAL) + vi->setPosition(190, 170); + else + vi->setPosition(180, 120); + vi->setOneLiner(tr("H264 video not supported")); + vi->setExitable(); + vi->setBorderOn(1); + vi->setTitleBarColour(Colour::DANGER); + vi->okButton(); + vi->draw(); + boxstack->add(vi); + boxstack->update(vi); + + } else { + VVideoRec* vidrec = new VVideoRec(toPlay, ish264); + vidrec->draw(); + boxstack->add(vidrec); + boxstack->update(vidrec); + vidrec->go(resume); + } + } + return 1; + } + // should not get to here + return 0; +} + +Recording* VRecordingList::getCurrentOptionRecording() +{ + Recording* currentRec; + RecordingList::iterator j; + RecordingList* recList = recman->getRecordings(); + for (j = recList->begin(); j != recList->end(); j++) + { + currentRec = *j; + if (currentRec->index == sl.getCurrentOption()) return currentRec; + } + + return NULL; +} + +int VRecordingList::handleCommand(int command) +{ + switch(command) + { + case Remote::DF_UP: + case Remote::UP: + { + sl.up(); + sl.draw(); + + doShowingBar(); + boxstack->update(this); + return 2; + } + case Remote::DF_DOWN: + case Remote::DOWN: + { + sl.down(); + sl.draw(); + + doShowingBar(); + boxstack->update(this); + return 2; + } + case Remote::SKIPBACK: + { + sl.pageUp(); + sl.draw(); + + doShowingBar(); + boxstack->update(this); + return 2; + } + case Remote::SKIPFORWARD: + { + sl.pageDown(); + sl.draw(); + + doShowingBar(); + boxstack->update(this); + return 2; + } + case Remote::OK: + { + if (sl.getNumOptions() == 0) return 2; + + // Check to see if it is a sub directory + Directory* currentSubDir; + DirectoryList::iterator i; + DirectoryList* dirList = recman->getDirectories(); + for (i = dirList->begin(); i != dirList->end(); i++) + { + currentSubDir = *i; + if (currentSubDir->index == sl.getCurrentOption()) + { + if (recman->down(currentSubDir)) + { + slIndexStack.push(sl.getCurrentOption()); + sl.clear(); + draw(); + boxstack->update(this); + } + return 2; + } + } + + // check to see if it's a recording + Recording* current = getCurrentOptionRecording(); + if (current) + { + Log::getInstance()->log("VRecordingList", Log::DEBUG, "Found the option you pointed at. %s %s", current->getProgName(), current->getFileName()); + +/* + VRecordingMenu* v = new VRecordingMenu(recman); + v->setParent(this); + v->setRecording(current); + v->draw(); + boxstack->add(v); + boxstack->update(v); +*/ + VRecording* vr = new VRecording(recman, current); + vr->setParent(this); + vr->draw(); + boxstack->add(vr); + boxstack->update(vr); + + return 2; + } + // should not get to here + return 1; + } + case Remote::BACK: + { + if (recman->isSubDir()) + { + recman->up(); + sl.clear(); + draw(true); + boxstack->update(this); + return 2; + } + else + { + return 4; + } + } + case Remote::PLAYPAUSE: + case Remote::PLAY: + { + if (doPlay(true)) return 2; + return 1; + } + case Remote::LEFT: + case Remote::RIGHT: + case Remote::ZERO: + { + reSort(); + return 2; + } + } + // stop command getting to any more views + return 1; +} + +bool VRecordingList::load() +{ + VDR* vdr = VDR::getInstance(); + + recman = new RecMan(); + + bool success = vdr->getRecordingsList(recman); + + if (success) + { + loading = false; + char* defaultSortOrder = vdr->configLoad("General", "Recordings Sort Order"); + if (defaultSortOrder) + { + if (!STRCASECMP(defaultSortOrder, "Chronological")) recman->setSortOrderChron(); + delete[] defaultSortOrder; + } + recman->sort(); + draw(); + boxstack->update(this); + } + + return success; +} + +void VRecordingList::reSort() +{ + recman->toggleSortOrder(); + recman->sort(); + sl.clear(); + draw(); + boxstack->update(this); +} + diff --git a/vscreensaver.h b/vscreensaver.h index f64d0e4..684755e 100644 --- a/vscreensaver.h +++ b/vscreensaver.h @@ -27,18 +27,14 @@ #include "boxx.h" -#ifdef WIN32 -#include "threadwin.h" -#else -#include "threadp.h" -#endif +#include "threadsystem.h" class VScreensaver : public Boxx, public Thread_TYPE { public: VScreensaver(); - ~VScreensaver(); + virtual ~VScreensaver(); int handleCommand(int command); void draw(); diff --git a/vsleeptimer.h b/vsleeptimer.h index cafc282..d5bb518 100644 --- a/vsleeptimer.h +++ b/vsleeptimer.h @@ -26,11 +26,7 @@ #include "boxx.h" #include "timerreceiver.h" -#ifndef WIN32 -#include "threadp.h" -#else -#include "threadwin.h" -#endif +#include "threadsystem.h" class Sleeptimer : public Thread_TYPE { diff --git a/vteletextview.cc b/vteletextview.cc index 168f65d..113bf6b 100644 --- a/vteletextview.cc +++ b/vteletextview.cc @@ -1,346 +1,210 @@ -/* - Copyright 2005-2008 Chris Tallon, Marten Richter - - 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 -#include "vteletextview.h" -#include "video.h" -#include "timers.h" -#include "boxstack.h" -#include "remote.h" -#include "teletxt/txtfont.h" - -unsigned int interpol_table_fac1[16][22]; -unsigned int interpol_table_fac2[16][22]; -unsigned int interpol_table_fac3[16][22]; -unsigned int interpol_table_fac4[16][22]; -int interpol_lowbit[16]; -int interpol_upbit[16]; -int interpol_lowline[22]; -int interpol_upline[22]; - -void initpol_tables(){ - int charsizex; - int charsizey; - charsizex=16; - if (Video::getInstance()->getFormat() == Video::PAL) - { - charsizey=22; - } else { - charsizey=18; - } - int ttcharsizex=12; - int ttcharsizey=10; - for (int py=0;pygetFormat() == Video::PAL) - { - //setSize(680, 550); - setSize(680,22); //Only first line - setPosition(40, 26); - } - else - { - setPosition(40, 30); - //setSize(680, 450); - setSize(680,18);//only first line - } - createBuffer(); - keyindigit=1; - page=0x100; - -} - -VTeletextView::~VTeletextView () -{ - // Make sure the timer is deleted - pv->draw(); - BoxStack::getInstance()->update(pv); - Timers::getInstance()->cancelTimer(this, 1); - ttdecoder->unRegisterTeletextView(this); - -} - -void VTeletextView::draw(bool completedraw, bool onlyfirstline) -{ - Boxx::draw(); - int x,y; - - for (y=0;y<25;y++) { - for (x=0;x<40;x++) { - if (ttdecoder->isDirty(x,y) || completedraw) { - cTeletextChar c=ttdecoder->getChar(x,y); - c.SetDirty(false); - //Skip Blinking and conceal - drawChar(x,y,c); - ttdecoder->setChar(x,y,c); - } - } - if (onlyfirstline) break; - } - - -} - -Colour VTeletextView::enumTeletextColorToCoulour(enumTeletextColor ttcol) -{ - switch (ttcol) { - case ttcBlack: - return Colour(0,0,0); - case ttcRed: - return Colour(255,0,0); - case ttcGreen: - return Colour(0,255,0); - case ttcYellow: - return Colour(255,255,0); - case ttcBlue: - return Colour(0,0,255); - case ttcMagenta: - return Colour(255,0,255); - case ttcCyan: - return Colour(0,255,255); - case ttcWhite: - return Colour(255,255,255); - case ttcTransparent: - return Colour(0,0,0,0); - case ttcHalfRed: - return Colour(127,0,0); - case ttcHalfGreen: - return Colour(0,127,0); - case ttcHalfYellow: - return Colour(127,127,0); - case ttcHalfBlue: - return Colour(0,0,127); - case ttcHalfMagenta: - return Colour(127,0,127); - case ttcHalfCyan: - return Colour(0,127,127); - case ttcGrey: - return Colour(127,127,127); - default: - return Colour(0,0,0); - }; -} - -//Next function inspired by osdteletext plugin -void VTeletextView::drawChar(int x, int y, cTeletextChar c) -{ - unsigned int buffer [10]; - unsigned int * charmap=GetFontChar(c,buffer); - if (!charmap) { //invalid char - memset(&buffer,0,10); - charmap=buffer; - } - enumTeletextColor ttforegcolour=c.GetFGColor(); - enumTeletextColor ttbackgcolour=c.GetBGColor(); - if (c.GetBoxedOut()) { - ttforegcolour=ttcTransparent; - ttbackgcolour=ttcTransparent; - } - int charsizex; - int charsizey; - charsizex=16; - bool firstline=false; - if (y==0) firstline=true; - if (Video::getInstance()->getFormat() == Video::PAL) - { - charsizey=22; - } else { - charsizey=18; - } - int ttcharsizex=12; - int ttcharsizey=10; - int screenposx=charsizex*x; //12*40= 480 250 - int screenposy=y*charsizey; - Boxx* drawtarget=this; - if (!firstline) { - - drawtarget=pv; - screenposy+=this->getScreenY(); - screenposx+=this->getScreenX(); - - } - - Colour fgcharcl=enumTeletextColorToCoulour(ttforegcolour); - Colour bgcharcl=enumTeletextColorToCoulour(ttbackgcolour); - - drawtarget->startFastDraw(); - for (int py=0;py>upperbit)) ? fgcharcl: bgcharcl; - Colour ulc=( upperbitline &(0x8000>>lowerbit)) ? fgcharcl: bgcharcl; - Colour luc=( lowerbitline &(0x8000>>upperbit)) ? fgcharcl: bgcharcl; - Colour llc=( lowerbitline &(0x8000>>lowerbit)) ? fgcharcl: bgcharcl; - unsigned int fac1,fac2,fac3,fac4; - fac1=interpol_table_fac1[px][py]; - fac2=interpol_table_fac2[px][py]; - fac3=interpol_table_fac3[px][py]; - fac4=interpol_table_fac4[px][py]; - - Colour res((uuc.red*fac1+ulc.red*fac2+luc.red*fac3+llc.red*fac4)/256, - (uuc.green*fac1+ulc.green*fac2+luc.green*fac3+llc.green*fac4)/256, - (uuc.blue*fac1+ulc.blue*fac2+luc.blue*fac3+llc.blue*fac4)/256, - (uuc.alpha*fac1+ulc.alpha*fac2+luc.alpha*fac3+llc.alpha*fac4)/256); //if this is too slow make a table - drawtarget->drawPixelAlpha(screenposx+px,screenposy+py,res, true); - } - } - - - drawtarget->endFastDraw(); - - -} - -int VTeletextView::handleCommand(int command) { - if (subtitlemode) return 0; //Ok we are in subtitle mode, we are a slave of the player - switch (command) { - case Remote::OK: - return 2; - case Remote::BACK: - return 4; - case Remote::ZERO: - case Remote::ONE: - case Remote::TWO: - case Remote::THREE: - case Remote::FOUR: - case Remote::FIVE: - case Remote::SIX: - case Remote::SEVEN: - case Remote::EIGHT: - case Remote::NINE: - { - // key in teletext page - doKey(command); - return 2; - } - }; - - return 0; - -} - -void VTeletextView::doKey(int command) -{ - char pagenums[3]; - if (keyindigit==1){ - if (command==9) return; //not allowed - page=command<<8; - pagenums[0]=command+ 48; - pagenums[1]='-'; - pagenums[2]='-'; - keyindigit++; - } else if (keyindigit==2) { - page|=command<<4; - pagenums[0]=48+((page &0xF00)>>8); - pagenums[1]=command+ 48; - pagenums[2]='-'; - keyindigit++; - } else if (keyindigit==3) { - page|=command; - pagenums[0]=48+((page &0xF00)>>8); - pagenums[1]=48+((page &0x0F0)>>4); - pagenums[2]=48+command; - keyindigit=1; - ttdecoder->setPage(page); - } - ttdecoder->setKeyinDigits(pagenums,true); - Region toupdate; - toupdate.w=16*40; - if (Video::getInstance()->getFormat() == Video::PAL) { - toupdate.h=22; - - } else { - toupdate.h=18; - - } - toupdate.x=0; - toupdate.y=0; - - draw(false,true); - BoxStack::getInstance()->update(this,&toupdate); - -} - -void VTeletextView::timercall(int clientReference) -{ - -} - -void VTeletextView::processMessage(Message* m) -{ - if (m->message == Message::TELETEXTUPDATE) - { - draw(false,false); - BoxStack::getInstance()->update(this); - BoxStack::getInstance()->update(pv); - - } else if (m->message == Message::TELETEXTUPDATEFIRSTLINE) - { - Region toupdate; - toupdate.w=16*40; - if (Video::getInstance()->getFormat() == Video::PAL) { - toupdate.h=22; - - } else { - toupdate.h=18; - - } - toupdate.x=0; - toupdate.y=0; - - - - draw(false,true); - BoxStack::getInstance()->update(this,&toupdate); - - } -} - - +/* + Copyright 2005-2008 Chris Tallon, Marten Richter + + 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 +#include "vteletextview.h" +#include "video.h" +#include "timers.h" +#include "boxstack.h" +#include "remote.h" + + +VTeletextView::VTeletextView(TeletextDecoderVBIEBU* TTdecoder,Boxx* playerview) +{ + ttdecoder=TTdecoder; + pv=playerview; + subtitlemode=false; + + + + if (Video::getInstance()->getFormat() == Video::PAL) + { + //setSize(680, 550); + setSize(680,22); //Only first line + setPosition(40, 26); + } + else + { + setPosition(40, 30); + //setSize(680, 450); + setSize(680,18);//only first line + } + createBuffer(); + keyindigit=1; + page=0x100; + +} + +VTeletextView::~VTeletextView () +{ + // Make sure the timer is deleted + Log::getInstance()->log("VTeletextView", Log::DEBUG, "VTeletextView destruct"); + pv->draw(); + BoxStack::getInstance()->update(pv); + Timers::getInstance()->cancelTimer(this, 1); + ttdecoder->unRegisterTeletextView(this); + +} + +void VTeletextView::draw(bool completedraw, bool onlyfirstline) +{ + //Log::getInstance()->log("VTeletextView", Log::ERR, "Start draw"); + Boxx::draw(); + int x,y; + + Boxx *drawtarget=NULL; + int ox,oy; + for (y=0;y<25;y++) { + if (y==0) { + drawtarget=this; + ox=0; + oy=0; + } else { + drawtarget=pv; + ox=this->getScreenX(); + oy=this->getScreenY(); + } + + for (x=0;x<40;x++) { + if (ttdecoder->isDirty(x,y) || completedraw) { + cTeletextChar c=ttdecoder->getChar(x,y); + c.SetDirty(false); + //Skip Blinking and conceal + drawtarget->drawTTChar(ox,oy,x,y,c); + ttdecoder->setChar(x,y,c); + } + } + // Log::getInstance()->log("VTeletextView", Log::ERR, "Line %d",y); + if (onlyfirstline) break; + } + // Log::getInstance()->log("VTeletextView", Log::ERR, "Start end"); + + +} + + + + + +int VTeletextView::handleCommand(int command) { + if (subtitlemode) return 0; //Ok we are in subtitle mode, we are a slave of the player + switch (command) { + case Remote::OK: + return 2; + case Remote::BACK: + return 4; + case Remote::ZERO: + case Remote::ONE: + case Remote::TWO: + case Remote::THREE: + case Remote::FOUR: + case Remote::FIVE: + case Remote::SIX: + case Remote::SEVEN: + case Remote::EIGHT: + case Remote::NINE: + { + // key in teletext page + doKey(command); + return 2; + } + }; + + return 0; + +} + +void VTeletextView::doKey(int command) +{ + char pagenums[3]; + if (keyindigit==1){ + if (command==9) return; //not allowed + page=command<<8; + pagenums[0]=command+ 48; + pagenums[1]='-'; + pagenums[2]='-'; + keyindigit++; + } else if (keyindigit==2) { + page|=command<<4; + pagenums[0]=48+((page &0xF00)>>8); + pagenums[1]=command+ 48; + pagenums[2]='-'; + keyindigit++; + } else if (keyindigit==3) { + page|=command; + pagenums[0]=48+((page &0xF00)>>8); + pagenums[1]=48+((page &0x0F0)>>4); + pagenums[2]=48+command; + keyindigit=1; + ttdecoder->setPage(page); + } + ttdecoder->setKeyinDigits(pagenums,true); + Region toupdate; + toupdate.w=16*40; + if (Video::getInstance()->getFormat() == Video::PAL) { + toupdate.h=22; + + } else { + toupdate.h=18; + + } + toupdate.x=0; + toupdate.y=0; + + draw(false,true); + BoxStack::getInstance()->update(this,&toupdate); + +} + +void VTeletextView::timercall(int clientReference) +{ + +} + +void VTeletextView::processMessage(Message* m) +{ + if (m->message == Message::TELETEXTUPDATE) + { + draw(false,false); + BoxStack::getInstance()->update(this); + BoxStack::getInstance()->update(pv); + + } else if (m->message == Message::TELETEXTUPDATEFIRSTLINE) + { + Region toupdate; + toupdate.w=16*40; + if (Video::getInstance()->getFormat() == Video::PAL) { + toupdate.h=22; + + } else { + toupdate.h=18; + + } + toupdate.x=0; + toupdate.y=0; + + + + draw(false,true); + BoxStack::getInstance()->update(this,&toupdate); + + } +} + + diff --git a/vteletextview.h b/vteletextview.h index d69a4a6..af3b5c9 100644 --- a/vteletextview.h +++ b/vteletextview.h @@ -1,64 +1,64 @@ -/* - Copyright 2005-2008 Chris Tallon, Marten Richter - - 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 VTELETEXTVIEW_H -#define VTELETEXTVIEW_H - -#include - -#include "boxx.h" -#include "timerreceiver.h" -#include "teletextdecodervbiebu.h" - -class VTeletextView : public Boxx, public TimerReceiver -{ - public: - VTeletextView (TeletextDecoderVBIEBU* TTdecoder, Boxx* playerview); - ~VTeletextView (); - void draw(bool completedraw, bool onlyfirstline); - void draw() {draw(true,false);}; - void drawChar(int x, int y, cTeletextChar c); - - Colour enumTeletextColorToCoulour(enumTeletextColor ttcol); - - void processMessage(Message* m); - - void setSubtitleMode(bool mode) {subtitlemode=mode;}; - bool isInSubtitleMode() {return subtitlemode;}; - - int handleCommand(int command); - void timercall(int clientReference); - - - private: - void doKey(int command); - - - protected: - TeletextDecoderVBIEBU* ttdecoder; - int keyindigit; - int page; - bool subtitlemode; - Boxx* pv; - - -}; - -#endif +/* + Copyright 2005-2008 Chris Tallon, Marten Richter + + 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 VTELETEXTVIEW_H +#define VTELETEXTVIEW_H + +#include + +#include "boxx.h" +#include "timerreceiver.h" +#include "teletextdecodervbiebu.h" + +class VTeletextView : public Boxx, public TimerReceiver +{ + public: + VTeletextView (TeletextDecoderVBIEBU* TTdecoder, Boxx* playerview); + ~VTeletextView (); + void draw(bool completedraw, bool onlyfirstline); + void draw() {draw(true,false);}; + + + + + void processMessage(Message* m); + + void setSubtitleMode(bool mode) {subtitlemode=mode;}; + bool isInSubtitleMode() {return subtitlemode;}; + + int handleCommand(int command); + void timercall(int clientReference); + + + private: + void doKey(int command); + + + protected: + TeletextDecoderVBIEBU* ttdecoder; + int keyindigit; + int page; + bool subtitlemode; + Boxx* pv; + + +}; + +#endif diff --git a/vtimeredit.cc b/vtimeredit.cc index dbc042e..9b8ae1c 100644 --- a/vtimeredit.cc +++ b/vtimeredit.cc @@ -74,18 +74,19 @@ void VTimerEdit::draw() int xpos = 20; int ypos = 50; - drawText(tr("Active"), xpos, ypos, Colour::LIGHTTEXT); ypos += surface->getFontHeight(); - drawText(tr("Channel"), xpos, ypos, Colour::LIGHTTEXT); ypos += surface->getFontHeight(); - drawText(tr("Name"), xpos, ypos, Colour::LIGHTTEXT); ypos += surface->getFontHeight(); - drawText(tr("Directory"), xpos, ypos, Colour::LIGHTTEXT); ypos += surface->getFontHeight(); - ypos += surface->getFontHeight(); - drawText(tr("Start"), xpos, ypos, Colour::LIGHTTEXT); ypos += surface->getFontHeight(); - drawText(tr("Stop"), xpos, ypos, Colour::LIGHTTEXT); ypos += surface->getFontHeight(); - drawText(tr("Priority"), xpos, ypos, Colour::LIGHTTEXT); ypos += surface->getFontHeight(); - drawText(tr("Lifetime"), xpos, ypos, Colour::LIGHTTEXT); ypos += surface->getFontHeight(); - ypos += surface->getFontHeight(); - drawText(tr("Current"), xpos, ypos, Colour::LIGHTTEXT); ypos += surface->getFontHeight(); - drawText(tr("Recording"), xpos, ypos, Colour::LIGHTTEXT); ypos += surface->getFontHeight(); + int fontheight=getFontHeight(); + drawText(tr("Active"), xpos, ypos, Colour::LIGHTTEXT); ypos += fontheight; + drawText(tr("Channel"), xpos, ypos, Colour::LIGHTTEXT); ypos += fontheight; + drawText(tr("Name"), xpos, ypos, Colour::LIGHTTEXT); ypos += fontheight; + drawText(tr("Directory"), xpos, ypos, Colour::LIGHTTEXT); ypos += fontheight; + ypos += fontheight; + drawText(tr("Start"), xpos, ypos, Colour::LIGHTTEXT); ypos += fontheight; + drawText(tr("Stop"), xpos, ypos, Colour::LIGHTTEXT); ypos += fontheight; + drawText(tr("Priority"), xpos, ypos, Colour::LIGHTTEXT); ypos += fontheight; + drawText(tr("Lifetime"), xpos, ypos, Colour::LIGHTTEXT); ypos += fontheight; + ypos += fontheight; + drawText(tr("Current"), xpos, ypos, Colour::LIGHTTEXT); ypos += fontheight; + drawText(tr("Recording"), xpos, ypos, Colour::LIGHTTEXT); ypos += fontheight; // Temp @@ -97,50 +98,50 @@ void VTimerEdit::draw() // Active if (recTimer->active) strcpy(buffer, "Yes"); else strcpy(buffer, "No"); - drawText(buffer, xpos, ypos, Colour::LIGHTTEXT); ypos += surface->getFontHeight(); + drawText(buffer, xpos, ypos, Colour::LIGHTTEXT); ypos += fontheight; // Channel SNPRINTF(buffer, 999, "%lu", recTimer->channelNumber); - drawText(buffer, xpos, ypos, Colour::LIGHTTEXT); ypos += surface->getFontHeight(); + drawText(buffer, xpos, ypos, Colour::LIGHTTEXT); ypos += fontheight; // Name SNPRINTF(buffer, 999, "%s", recTimer->getName()); - drawText(buffer, xpos, ypos, Colour::LIGHTTEXT); ypos += surface->getFontHeight(); + drawText(buffer, xpos, ypos, Colour::LIGHTTEXT); ypos += fontheight; // Directory if (recTimer->getDirectory()) SNPRINTF(buffer, 999, "%s", recTimer->getDirectory()); else strcpy(buffer, ""); - drawText(buffer, xpos, ypos, Colour::LIGHTTEXT); ypos += surface->getFontHeight(); - ypos += surface->getFontHeight(); + drawText(buffer, xpos, ypos, Colour::LIGHTTEXT); ypos += fontheight; + ypos += fontheight; // Start tms = localtime((time_t*)&recTimer->startTime); strftime(buffer, 999, "%d/%m %H:%M", tms); - drawText(buffer, xpos, ypos, Colour::LIGHTTEXT); ypos += surface->getFontHeight(); + drawText(buffer, xpos, ypos, Colour::LIGHTTEXT); ypos += fontheight; // Stop tms = localtime((time_t*)&recTimer->stopTime); strftime(buffer, 999, "%d/%m %H:%M", tms); - drawText(buffer, xpos, ypos, Colour::LIGHTTEXT); ypos += surface->getFontHeight(); + drawText(buffer, xpos, ypos, Colour::LIGHTTEXT); ypos += fontheight; // Priority SNPRINTF(buffer, 999, "%lu", recTimer->priority); - drawText(buffer, xpos, ypos, Colour::LIGHTTEXT); ypos += surface->getFontHeight(); + drawText(buffer, xpos, ypos, Colour::LIGHTTEXT); ypos += fontheight; // Lifetime SNPRINTF(buffer, 999, "%lu", recTimer->lifeTime); - drawText(buffer, xpos, ypos, Colour::LIGHTTEXT); ypos += surface->getFontHeight(); - ypos += surface->getFontHeight(); + drawText(buffer, xpos, ypos, Colour::LIGHTTEXT); ypos += fontheight; + ypos += fontheight; // Current if (recTimer->pending) strcpy(buffer, "Yes"); else strcpy(buffer, "No"); - drawText(buffer, xpos, ypos, Colour::LIGHTTEXT); ypos += surface->getFontHeight(); + drawText(buffer, xpos, ypos, Colour::LIGHTTEXT); ypos += fontheight; // Recording now if (recTimer->recording) strcpy(buffer, "Yes"); else strcpy(buffer, "No"); - drawText(buffer, xpos, ypos, Colour::LIGHTTEXT); ypos += surface->getFontHeight(); + drawText(buffer, xpos, ypos, Colour::LIGHTTEXT); ypos += fontheight; } void VTimerEdit::swap() diff --git a/vtimerlist.cc b/vtimerlist.cc index aa29360..3eb5f1e 100644 --- a/vtimerlist.cc +++ b/vtimerlist.cc @@ -1,389 +1,392 @@ -/* - 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. -*/ - -#include "vtimerlist.h" - -#include "message.h" -#include "remote.h" -#include "wsymbol.h" -#include "colour.h" -#include "video.h" -#include "i18n.h" -#include "timers.h" -#include "vtimeredit.h" -#include "command.h" -#include "boxstack.h" -#include "vdr.h" -#include "vinfo.h" -#include "log.h" - -VTimerList::VTimerList() -{ - recTimerList = NULL; - - clockRegion.x = 420; - clockRegion.y = 0; - clockRegion.w = 150; - clockRegion.h = 30; - - indicatorsRegion.x = 6; - indicatorsRegion.y = 44; - indicatorsRegion.w = 18; - indicatorsRegion.h = 15 * (surface->getFontHeight() + 1); - - flipflop = true; - - setSize(570, 420); - createBuffer(); - if (Video::getInstance()->getFormat() == Video::PAL) - { - setPosition(80, 70); - } - else - { - setPosition(70, 35); - } - - setTitleBarOn(1); - setTitleText(tr("Timers")); - setTitleBarColour(Colour::TITLEBARBACKGROUND); - - sl.setPosition(30, 30 + 5); - sl.setSize(area.w - 40, area.h - 30 - 15 - 30); - add(&sl); -} - -void VTimerList::preDelete() -{ - Timers::getInstance()->cancelTimer(this, 1); -} - -VTimerList::~VTimerList() -{ - if (recTimerList) - { - for (UINT i = 0; i < recTimerList->size(); i++) - { - delete (*recTimerList)[i]; - } - - recTimerList->clear(); - delete recTimerList; - } -} - -void VTimerList::draw() -{ - // Draw statics - - TBBoxx::draw(); - - WSymbol w; - TEMPADD(&w); - - w.nextSymbol = WSymbol::UP; - w.setPosition(20, 385); - w.draw(); - - w.nextSymbol = WSymbol::DOWN; - w.setPosition(50, 385); - w.draw(); - - w.nextSymbol = WSymbol::SKIPBACK; - w.setPosition(85, 385); - w.draw(); - - w.nextSymbol = WSymbol::SKIPFORWARD; - w.setPosition(115, 385); - w.draw(); - - drawTextRJ("[ok] = edit", 560, 385, Colour::LIGHTTEXT); - - drawClock(); - drawShowing(); - drawIndicators(); -} - -bool VTimerList::load() -{ - recTimerList = VDR::getInstance()->getRecTimersList(); - - if (!recTimerList) return false; - - char strA[300]; - char strB[300]; - - struct tm* btime; - - // FIXME all drawing stuff in this class and sl.clear somewhere?! - - sl.addColumn(0); - sl.addColumn(110); - - RecTimer* recTimer; - int first = 1; - - for (UINT i = 0; i < recTimerList->size(); i++) - { - recTimer = (*recTimerList)[i]; - - btime = localtime((time_t*)&recTimer->startTime); - strftime(strA, 299, "%d/%m %H:%M ", btime); - SNPRINTF(strB, 299, "%s\t%s", strA, recTimer->getName()); - sl.addOption(strB, (ULONG)recTimer, first); - first = 0; - } - - return true; -} - -void VTimerList::drawClock() -{ - // Blank the area first - rectangle(area.w - 150, 0, 150, 30, titleBarColour); - - char timeString[20]; - time_t t; - time(&t); - struct tm* tms = localtime(&t); - strftime(timeString, 19, "%d/%m %H:%M:%S", tms); - drawTextRJ(timeString, 560, 5, Colour::LIGHTTEXT); - - Timers::getInstance()->setTimerT(this, 1, t + 1); -} - -void VTimerList::drawShowing() -{ - int topOption = sl.getTopOption() + 1; - if (sl.getNumOptions() == 0) topOption = 0; - - rectangle(220, 385, 180, 25, Colour::VIEWBACKGROUND); - char showing[200]; - sprintf(showing, tr("%i to %i of %i"), topOption, sl.getBottomOption(), sl.getNumOptions()); - drawText(showing, 220, 385, Colour::LIGHTTEXT); -} - -void VTimerList::drawIndicators() -{ - int top = sl.getTopOption(); - int bottom = sl.getBottomOption(); - int yinc = surface->getFontHeight() + 1; - RecTimer* recTimer; - - rectangle(6, 44, 18, 15*yinc, Colour::VIEWBACKGROUND); - - // The indexes recorded from the wselectlist into the index member of the RecTimer - // Is the same as the position in the vector of RecTimers - // Because they are in order, they don't change order and wselectlist starts from 0 up consecutively - - int ypos = 44; - for (int current = top; current < bottom; current++) - { - recTimer = (*recTimerList)[current]; - - if (recTimer->recording) // Flashing red square - { - if (flipflop) - { - rectangle(6, ypos, 18, 16, Colour::RED); - drawText("R", 8, ypos-3, Colour::LIGHTTEXT); - } - } - else if (recTimer->pending) - { - rectangle(6, ypos, 18, 16, Colour::RED); - drawText("X", 8, ypos-3, Colour::BLACK); - } - else if (recTimer->active == 0) - { - rectangle(6, ypos, 18, 16, Colour::SELECTHIGHLIGHT); - drawText("X", 8, ypos-3, Colour::BLACK); - } - else - { -// if (flipflop) rectangle(6, ypos, 18, 16, Colour::GREEN); - } - - ypos += yinc; - } -} - -void VTimerList::timercall(int clientReference) -{ - drawClock(); - BoxStack::getInstance()->update(this, &clockRegion); - - flipflop = !flipflop; - drawIndicators(); - BoxStack::getInstance()->update(this, &indicatorsRegion); -} - -int VTimerList::handleCommand(int command) -{ - switch(command) - { - case Remote::DF_UP: - case Remote::UP: - { - sl.up(); - sl.draw(); - drawShowing(); - drawIndicators(); - BoxStack::getInstance()->update(this); - return 2; - } - case Remote::DF_DOWN: - case Remote::DOWN: - { - sl.down(); - sl.draw(); - drawShowing(); - drawIndicators(); - BoxStack::getInstance()->update(this); - return 2; - } - case Remote::SKIPBACK: - { - sl.pageUp(); - sl.draw(); - drawShowing(); - drawIndicators(); - BoxStack::getInstance()->update(this); - return 2; - } - case Remote::SKIPFORWARD: - { - sl.pageDown(); - sl.draw(); - drawShowing(); - drawIndicators(); - BoxStack::getInstance()->update(this); - return 2; - } - case Remote::OK: - { - RecTimer* recTimer = NULL; - if (recTimerList) recTimer = (RecTimer*)sl.getCurrentOptionData(); - if (recTimer == NULL) return 2; - - VTimerEdit* v = new VTimerEdit(recTimer); - v->setParent(this); - v->draw(); - BoxStack::getInstance()->add(v); - BoxStack::getInstance()->update(v); - - return 2; - } - case Remote::BACK: - { - return 4; - } - } - // stop command getting to any more views - return 1; -} - -void VTimerList::processMessage(Message* m) -{ - if (m->message == Message::MOUSE_MOVE) - { - if (sl.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) - { - sl.draw(); - BoxStack::getInstance()->update(this); - } - } - else if (m->message == Message::MOUSE_LBDOWN) - { - if (sl.mouseLBDOWN((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) - { - BoxStack::getInstance()->handleCommand(Remote::OK); //simulate OK press - } - else - { - //check if press is outside this view! then simulate cancel - int x=(m->parameter>>16)-getScreenX(); - int y=(m->parameter&0xFFFF)-getScreenY(); - if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight()) - { - BoxStack::getInstance()->handleCommand(Remote::BACK); //simulate cancel press - } - } - } - else if (m->message == Message::DELETE_SELECTED_TIMER) - { - RecTimer* recTimer = (RecTimer*)sl.getCurrentOptionData(); - if (recTimer == NULL) return; - Log::getInstance()->log("VTimerList", Log::DEBUG, "Got timer to delete"); - - - ULONG retval = VDR::getInstance()->deleteTimer(recTimer); - if (!VDR::getInstance()->isConnected()) { Command::getInstance()->connectionLost(); return; } - Log::getInstance()->log("VTimerList", Log::DEBUG, "Got return fron delete timer: %lu", retval); - - if (retval != 10) - { - VInfo* errorBox = new VInfo(); - errorBox->setSize(360, 200); - errorBox->createBuffer(); - if (Video::getInstance()->getFormat() == Video::PAL) - errorBox->setPosition(190, 170); - else - errorBox->setPosition(180, 120); - - - if (retval == 1) errorBox->setOneLiner(tr("Timers being edited at VDR, please try later")); - else if (retval == 3) errorBox->setOneLiner(tr("Unable to delete timer - timer is running")); - else if (retval == 4) errorBox->setOneLiner(tr("Error - timer not found at VDR")); - else errorBox->setOneLiner(tr("Unknown error")); - - errorBox->setExitable(); - errorBox->setBorderOn(1); - errorBox->setTitleBarColour(Colour::DANGER); - errorBox->okButton(); - errorBox->draw(); - BoxStack::getInstance()->add(errorBox); - BoxStack::getInstance()->update(errorBox); - } - - int saveIndex = sl.getCurrentOption(); - int saveTop = sl.getTopOption(); - - if (recTimerList) - { - for (UINT i = 0; i < recTimerList->size(); i++) - { - delete (*recTimerList)[i]; - } - - recTimerList->clear(); - delete recTimerList; - } - - sl.clear(); - load(); - - sl.hintSetCurrent(saveIndex); - sl.hintSetTop(saveTop); - draw(); - BoxStack::getInstance()->update(this); - } -} - +/* + 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. +*/ + +#include "vtimerlist.h" + +#include "message.h" +#include "remote.h" +#include "wsymbol.h" +#include "colour.h" +#include "video.h" +#include "i18n.h" +#include "timers.h" +#include "vtimeredit.h" +#include "command.h" +#include "boxstack.h" +#include "vdr.h" +#include "vinfo.h" +#include "log.h" + +VTimerList::VTimerList() +{ + recTimerList = NULL; + + clockRegion.x = 420; + clockRegion.y = 0; + clockRegion.w = 150; + clockRegion.h = 30; + + + + flipflop = true; + + setSize(570, 420); + createBuffer(); + + indicatorsRegion.x = 6; + indicatorsRegion.y = 44; + indicatorsRegion.w = 18; + indicatorsRegion.h = 15 * (getFontHeight() + 1); + + if (Video::getInstance()->getFormat() == Video::PAL) + { + setPosition(80, 70); + } + else + { + setPosition(70, 35); + } + + setTitleBarOn(1); + setTitleText(tr("Timers")); + setTitleBarColour(Colour::TITLEBARBACKGROUND); + + sl.setPosition(30, 30 + 5); + sl.setSize(area.w - 40, area.h - 30 - 15 - 30); + add(&sl); +} + +void VTimerList::preDelete() +{ + Timers::getInstance()->cancelTimer(this, 1); +} + +VTimerList::~VTimerList() +{ + if (recTimerList) + { + for (UINT i = 0; i < recTimerList->size(); i++) + { + delete (*recTimerList)[i]; + } + + recTimerList->clear(); + delete recTimerList; + } +} + +void VTimerList::draw() +{ + // Draw statics + + TBBoxx::draw(); + + WSymbol w; + TEMPADD(&w); + + w.nextSymbol = WSymbol::UP; + w.setPosition(20, 385); + w.draw(); + + w.nextSymbol = WSymbol::DOWN; + w.setPosition(50, 385); + w.draw(); + + w.nextSymbol = WSymbol::SKIPBACK; + w.setPosition(85, 385); + w.draw(); + + w.nextSymbol = WSymbol::SKIPFORWARD; + w.setPosition(115, 385); + w.draw(); + + drawTextRJ("[ok] = edit", 560, 385, Colour::LIGHTTEXT); + + drawClock(); + drawShowing(); + drawIndicators(); +} + +bool VTimerList::load() +{ + recTimerList = VDR::getInstance()->getRecTimersList(); + + if (!recTimerList) return false; + + char strA[300]; + char strB[300]; + + struct tm* btime; + + // FIXME all drawing stuff in this class and sl.clear somewhere?! + + sl.addColumn(0); + sl.addColumn(110); + + RecTimer* recTimer; + int first = 1; + + for (UINT i = 0; i < recTimerList->size(); i++) + { + recTimer = (*recTimerList)[i]; + + btime = localtime((time_t*)&recTimer->startTime); + strftime(strA, 299, "%d/%m %H:%M ", btime); + SNPRINTF(strB, 299, "%s\t%s", strA, recTimer->getName()); + sl.addOption(strB, (ULONG)recTimer, first); + first = 0; + } + + return true; +} + +void VTimerList::drawClock() +{ + // Blank the area first + rectangle(area.w - 150, 0, 150, 30, titleBarColour); + + char timeString[20]; + time_t t; + time(&t); + struct tm* tms = localtime(&t); + strftime(timeString, 19, "%d/%m %H:%M:%S", tms); + drawTextRJ(timeString, 560, 5, Colour::LIGHTTEXT); + + Timers::getInstance()->setTimerT(this, 1, t + 1); +} + +void VTimerList::drawShowing() +{ + int topOption = sl.getTopOption() + 1; + if (sl.getNumOptions() == 0) topOption = 0; + + rectangle(220, 385, 180, 25, Colour::VIEWBACKGROUND); + char showing[200]; + sprintf(showing, tr("%i to %i of %i"), topOption, sl.getBottomOption(), sl.getNumOptions()); + drawText(showing, 220, 385, Colour::LIGHTTEXT); +} + +void VTimerList::drawIndicators() +{ + int top = sl.getTopOption(); + int bottom = sl.getBottomOption(); + int yinc = getFontHeight() + 1; + RecTimer* recTimer; + + rectangle(6, 44, 18, 15*yinc, Colour::VIEWBACKGROUND); + + // The indexes recorded from the wselectlist into the index member of the RecTimer + // Is the same as the position in the vector of RecTimers + // Because they are in order, they don't change order and wselectlist starts from 0 up consecutively + + int ypos = 44; + for (int current = top; current < bottom; current++) + { + recTimer = (*recTimerList)[current]; + + if (recTimer->recording) // Flashing red square + { + if (flipflop) + { + rectangle(6, ypos, 18, 16, Colour::RED); + drawText("R", 8, ypos-3, Colour::LIGHTTEXT); + } + } + else if (recTimer->pending) + { + rectangle(6, ypos, 18, 16, Colour::RED); + drawText("X", 8, ypos-3, Colour::BLACK); + } + else if (recTimer->active == 0) + { + rectangle(6, ypos, 18, 16, Colour::SELECTHIGHLIGHT); + drawText("X", 8, ypos-3, Colour::BLACK); + } + else + { +// if (flipflop) rectangle(6, ypos, 18, 16, Colour::GREEN); + } + + ypos += yinc; + } +} + +void VTimerList::timercall(int clientReference) +{ + drawClock(); + BoxStack::getInstance()->update(this, &clockRegion); + + flipflop = !flipflop; + drawIndicators(); + BoxStack::getInstance()->update(this, &indicatorsRegion); +} + +int VTimerList::handleCommand(int command) +{ + switch(command) + { + case Remote::DF_UP: + case Remote::UP: + { + sl.up(); + sl.draw(); + drawShowing(); + drawIndicators(); + BoxStack::getInstance()->update(this); + return 2; + } + case Remote::DF_DOWN: + case Remote::DOWN: + { + sl.down(); + sl.draw(); + drawShowing(); + drawIndicators(); + BoxStack::getInstance()->update(this); + return 2; + } + case Remote::SKIPBACK: + { + sl.pageUp(); + sl.draw(); + drawShowing(); + drawIndicators(); + BoxStack::getInstance()->update(this); + return 2; + } + case Remote::SKIPFORWARD: + { + sl.pageDown(); + sl.draw(); + drawShowing(); + drawIndicators(); + BoxStack::getInstance()->update(this); + return 2; + } + case Remote::OK: + { + RecTimer* recTimer = NULL; + if (recTimerList) recTimer = (RecTimer*)sl.getCurrentOptionData(); + if (recTimer == NULL) return 2; + + VTimerEdit* v = new VTimerEdit(recTimer); + v->setParent(this); + v->draw(); + BoxStack::getInstance()->add(v); + BoxStack::getInstance()->update(v); + + return 2; + } + case Remote::BACK: + { + return 4; + } + } + // stop command getting to any more views + return 1; +} + +void VTimerList::processMessage(Message* m) +{ + if (m->message == Message::MOUSE_MOVE) + { + if (sl.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) + { + sl.draw(); + BoxStack::getInstance()->update(this); + } + } + else if (m->message == Message::MOUSE_LBDOWN) + { + if (sl.mouseLBDOWN((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) + { + BoxStack::getInstance()->handleCommand(Remote::OK); //simulate OK press + } + else + { + //check if press is outside this view! then simulate cancel + int x=(m->parameter>>16)-getScreenX(); + int y=(m->parameter&0xFFFF)-getScreenY(); + if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight()) + { + BoxStack::getInstance()->handleCommand(Remote::BACK); //simulate cancel press + } + } + } + else if (m->message == Message::DELETE_SELECTED_TIMER) + { + RecTimer* recTimer = (RecTimer*)sl.getCurrentOptionData(); + if (recTimer == NULL) return; + Log::getInstance()->log("VTimerList", Log::DEBUG, "Got timer to delete"); + + + ULONG retval = VDR::getInstance()->deleteTimer(recTimer); + if (!VDR::getInstance()->isConnected()) { Command::getInstance()->connectionLost(); return; } + Log::getInstance()->log("VTimerList", Log::DEBUG, "Got return fron delete timer: %lu", retval); + + if (retval != 10) + { + VInfo* errorBox = new VInfo(); + errorBox->setSize(360, 200); + errorBox->createBuffer(); + if (Video::getInstance()->getFormat() == Video::PAL) + errorBox->setPosition(190, 170); + else + errorBox->setPosition(180, 120); + + + if (retval == 1) errorBox->setOneLiner(tr("Timers being edited at VDR, please try later")); + else if (retval == 3) errorBox->setOneLiner(tr("Unable to delete timer - timer is running")); + else if (retval == 4) errorBox->setOneLiner(tr("Error - timer not found at VDR")); + else errorBox->setOneLiner(tr("Unknown error")); + + errorBox->setExitable(); + errorBox->setBorderOn(1); + errorBox->setTitleBarColour(Colour::DANGER); + errorBox->okButton(); + errorBox->draw(); + BoxStack::getInstance()->add(errorBox); + BoxStack::getInstance()->update(errorBox); + } + + int saveIndex = sl.getCurrentOption(); + int saveTop = sl.getTopOption(); + + if (recTimerList) + { + for (UINT i = 0; i < recTimerList->size(); i++) + { + delete (*recTimerList)[i]; + } + + recTimerList->clear(); + delete recTimerList; + } + + sl.clear(); + load(); + + sl.hintSetCurrent(saveIndex); + sl.hintSetTop(saveTop); + draw(); + BoxStack::getInstance()->update(this); + } +} + diff --git a/vvideolivetv.cc b/vvideolivetv.cc index 8d50bf2..0e49830 100644 --- a/vvideolivetv.cc +++ b/vvideolivetv.cc @@ -1,1194 +1,1195 @@ -/* - Copyright 2007-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 "vvideolivetv.h" - -#include "vchannellist.h" -#include "video.h" -#include "playerlive.h" -#include "playerlivetv.h" -#include "playerliveradio.h" -#include "channel.h" -#include "boxstack.h" -#include "colour.h" -#include "osd.h" -#include "command.h" -#include "i18n.h" -#include "wtextbox.h" -#include "remote.h" -#include "vaudioselector.h" -#include "colour.h" -#include "event.h" -#include "timers.h" -#include "vepg.h" -#include "bitmap.h" -#include "log.h" -#include "vteletextview.h" - -VVideoLiveTV::VVideoLiveTV(ChannelList* tchanList, ULONG initialChannelNumber, VChannelList* tvchannelList) -{ - vdr = VDR::getInstance(); - boxstack = BoxStack::getInstance(); - video = Video::getInstance(); - - vas = NULL; - - chanList = tchanList; - vchannelList = tvchannelList; - numberWidth = (int)VDR::getInstance()->getChannelNumberWidth(); - - currentChannelIndex = 0; - previousChannelIndex = 0; - osdChannelIndex = 0; - keying = 0; - preBuffering = 0; - - playing = false; - - // Convert channel number to index - UINT i; - for(i = 0; i < chanList->size(); i++) - { - if ((*chanList)[i]->number == (UINT)initialChannelNumber) - { - currentChannelIndex = i; - osdChannelIndex = i; - break; - } - } - - eventList = NULL; - - videoMode = video->getMode(); - - if ((*chanList)[currentChannelIndex]->type == VDR::VIDEO) - { - streamType = VDR::VIDEO; - player = new PlayerLiveTV(Command::getInstance(), this, this, chanList); - } - else - { - streamType = VDR::RADIO; - player = new PlayerLiveRadio(Command::getInstance(), this, chanList); - } - player->init(); - - setSize(video->getScreenWidth(), video->getScreenHeight()); - createBuffer(); - Colour transparent(0, 0, 0, 0); - setBackgroundColour(transparent); - - dowss = false; - char* optionWSS = vdr->configLoad("General", "WSS"); - if (optionWSS) - { - if (strstr(optionWSS, "Yes")) dowss = true; - delete[] optionWSS; - } - Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Do WSS: %u", dowss); - - if (dowss) - { - wss.setFormat(video->getFormat()); - wss.setWide(true); - add(&wss); - - wssRegion.x = 0; - wssRegion.y = 6; - wssRegion.w = video->getScreenWidth(); - wssRegion.h = 2; - } - - // This variable is set to true if the user pressed OK to bring the OSD on screen - // This is only used on old remotes to stop up/down buttons being used for osd-epg scrolling - okTriggeredOSD = false; - - Colour osdBack = Colour(0, 0, 0, 128); - - osd.setBackgroundColour(osdBack); - osd.setPosition(0, video->getScreenHeight() - 150); - osd.setSize(video->getScreenWidth(), 150); - osd.setVisible(false); - add(&osd); - - clock.setBackgroundColour(osdBack); - clock.setPosition(osd.getWidth() - 100, 4); - clock.setSize(90, 30); - osd.add(&clock); - - osdChanNum.setBackgroundColour(osdBack); - osdChanNum.setPosition(50, 4); - osdChanNum.setSize((numberWidth*10) + 22, 30); // 10 px = width of number chars in font - osd.add(&osdChanNum); - - osdChanName.setBackgroundColour(osdBack); - osdChanName.setPosition(osdChanNum.getX2() + 10, 4); - osdChanName.setSize(300, 30); - osd.add(&osdChanName); - - boxRed.setBackgroundColour(Colour::RED); - boxRed.setPosition(54, 104); - boxRed.setSize(18, 16); - osd.add(&boxRed); - - boxGreen.setBackgroundColour(Colour::GREEN); - boxGreen.setPosition(220, 104); - boxGreen.setSize(18, 16); - osd.add(&boxGreen); - - boxYellow.setBackgroundColour(Colour::YELLOW); - boxYellow.setPosition(390, 104); - boxYellow.setSize(18, 16); - osd.add(&boxYellow); - - boxBlue.setBackgroundColour(Colour::BLUE); - boxBlue.setPosition(560, 104); - boxBlue.setSize(18, 16); - osd.add(&boxBlue); - - textRed.setBackgroundColour(osdBack); - textRed.setPosition(boxRed.getX2(), 98); - textRed.setSize(boxGreen.getX() - boxRed.getX2(), 30); - textRed.setText(tr("Summary")); - osd.add(&textRed); - - if (streamType == VDR::VIDEO) - { - textGreen.setBackgroundColour(osdBack); - textGreen.setPosition(boxGreen.getX2(), 98); - textGreen.setSize(boxYellow.getX() - boxGreen.getX2(), 30); - textGreen.setText(tr("Audio")); - osd.add(&textGreen); - } - - textYellow.setBackgroundColour(osdBack); - textYellow.setPosition(boxYellow.getX2(), 98); - textYellow.setSize(boxBlue.getX() - boxYellow.getX2(), 30); - textYellow.setText("Teletext"); - osd.add(&textYellow); - - textBlue.setBackgroundColour(osdBack); - textBlue.setPosition(boxBlue.getX2(), 98); - textBlue.setSize(osd.getX2() - boxBlue.getX2(), 30); - textBlue.setText(tr("EPG")); - osd.add(&textBlue); - - sl.setBackgroundColour(osdBack); - sl.setPosition(70, 36); - sl.setSize(500, 58); - sl.setNoLoop(); - osd.add(&sl); - - // Summary Box - summary.setBackgroundColour(osdBack); - summary.setPosition(0, video->getScreenHeight() - 300); - summary.setSize(video->getScreenWidth(), 150); - summary.setVisible(false); - add(&summary); - - textSummary.setBackgroundColour(osdBack); - textSummary.setPosition(40, 10); - textSummary.setSize(video->getScreenWidth() - 80, 130); - textSummary.setParaMode(true); - summary.add(&textSummary); - - summaryBlackLine.setBackgroundColour(Colour::BLACK); - summaryBlackLine.setPosition(0, summary.getHeight() - 4); - summaryBlackLine.setSize(summary.getWidth(), 4); - summary.add(&summaryBlackLine); - - sAspectRatio.setPosition(osd.getWidth() - 90, 40); - sAspectRatio.nextColour = Colour::SELECTHIGHLIGHT; - sAspectRatio.setVisible(false); - osd.add(&sAspectRatio); - - bufferBar.setPosition(osd.getWidth() - 90, 70); - bufferBar.setSize(40, 20); - bufferBar.setVisible(true); - osd.add(&bufferBar); - - sAudioChannels.setPosition(osd.getWidth() - 130, 40); - sAudioChannels.nextColour = Colour::SELECTHIGHLIGHT; - sAudioChannels.setVisible(false); - osd.add(&sAudioChannels); - - textUnavailable.setBackgroundColour(osdBack); - textUnavailable.setPosition(60, 30); - textUnavailable.setSize(osd.getWidth() - 120, 30); - textUnavailable.setText(tr("Channel Unavailable")); - textUnavailable.setVisible(false); - add(&textUnavailable); - - // FIXME painful - Region r1 = summary.getRegionR(); - Region r2 = osd.getRegionR(); - osdSummaryRegion = r1 + r2; -} - -void VVideoLiveTV::preDelete() -{ - if (playing) stop(); -} - -VVideoLiveTV::~VVideoLiveTV() -{ - delete player; - video->setDefaultAspect(); - delData(); -} - -void VVideoLiveTV::delData() -{ - if (eventList) - { - int eventListSize = eventList->size(); - for(int i = 0; i < eventListSize; i++) - { - delete (*eventList)[i]; - } - eventList->clear(); - delete eventList; - - } - sl.clear(); -} - -int VVideoLiveTV::handleCommand(int command) -{ - switch(command) - { - case Remote::BACK: - { - if (osd.getVisible() && !textUnavailable.getVisible()) - { - clearScreen(); - return 2; - } - // else drop through to stop - } - case Remote::STOP: - { - stop(); - vchannelList->highlightChannel((*chanList)[currentChannelIndex]); - return 4; - } - - // NEW REMOTE ONLY - navigate EPG, bring it onscreen if it's not there - case Remote::UP: - { - doUpDown(false); - return 2; - } - case Remote::DOWN: - { - doUpDown(true); - return 2; - } - case Remote::LEFT: - { - doLeftRight(false); - return 2; - } - case Remote::RIGHT: - { - doLeftRight(true); - return 2; - } - // Continue new remote only... - case Remote::CHANNELUP: - { - doChanUpDown(UP); - return 2; - } - case Remote::CHANNELDOWN: - { - doChanUpDown(DOWN); - return 2; - } - - // END NEW REMOTE ONLY, START OLD REMOTE ONLY - - // DF_LEFT and DF_RIGHT never get here because they are stolen - // by command as vol- and vol+ - - // Old remote. Decide what to do based on whether - // OK was pressed - osd shown manually, use up/down for epg nav - // UP/DOWN was pressed to change channel, osd was shown auto, use up/down for ch+/ch- - - case Remote::DF_UP: - { - // Old remote, decide what to do based on okTriggeredOSD - if (okTriggeredOSD) doUpDown(false); - else doChanUpDown(UP); - return 2; - } - case Remote::DF_DOWN: - { - // Old remote, decide what to do based on okTriggeredOSD - if (okTriggeredOSD) doUpDown(true); - else doChanUpDown(DOWN); - return 2; - } - - // END NEW/OLD REMOTE STUFF - - case Remote::PREVCHANNEL: - { - channelChange(PREVIOUS, 0); - osdChannelIndex = currentChannelIndex; - displayOSD(true); - return 2; - } - case Remote::OK: - { - doOK(); - return 2; - } - case Remote::RED: - case Remote::MENU: - { - doSummary(); - return 2; - } - case Remote::FULL: - case Remote::TV: - { - toggleChopSides(); - return 2; - } - - case Remote::ZERO: - case Remote::ONE: - case Remote::TWO: - case Remote::THREE: - case Remote::FOUR: - case Remote::FIVE: - case Remote::SIX: - case Remote::SEVEN: - case Remote::EIGHT: - case Remote::NINE: - { - // key in channel number - doKey(command); - return 2; - } - - case Remote::GREEN: - { - if (streamType == VDR::VIDEO) doAudioSelector(); - return 2; - } - case Remote::YELLOW: - { - if (streamType ==VDR::VIDEO) doTeletext(); //TODO: Add a selector for subtitles or teletext - return 2; - } - case Remote::GUIDE: - case Remote::BLUE: - { - doEPG(); - return 2; - } - case Remote::RECORD: - if (streamType = VDR::VIDEO) - (static_cast(player))->toggleSubtitles(); - return 2; - } - - return 1; -} - -void VVideoLiveTV::go() -{ - playing = true; - draw(); - boxstack->update(this); - - setClock(); - displayOSD(true); - - player->go(currentChannelIndex); -} - -void VVideoLiveTV::stop() -{ - Timers::getInstance()->cancelTimer(this, 1); - Timers::getInstance()->cancelTimer(this, 2); - player->stop(); - playing = false; -} - -void VVideoLiveTV::doLeftRight(bool right) -{ - if (osd.getVisible()) - { - if (right) osdChannelIndex = upChannel(osdChannelIndex); - else osdChannelIndex = downChannel(osdChannelIndex); - } - else - { - osdChannelIndex = currentChannelIndex; - } - displayOSD(true); -} - -void VVideoLiveTV::doUpDown(bool down) -{ - if (osd.getVisible()) - { - if (down) sl.down(); - else sl.up(); - sl.draw(); - - displayOSD(false); - } - else - { - displayOSD(true); - } -} - -void VVideoLiveTV::doChanUpDown(int which) -{ - channelChange(OFFSET, which); - osdChannelIndex = currentChannelIndex; - displayOSD(true); -} - -void VVideoLiveTV::doOK() -{ - if (osd.getVisible()) - { - if (keying) - { - UINT newChannel = 0; - for(int i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i); - - channelChange(NUMBER, newChannel); - osdChannelIndex = currentChannelIndex; - displayOSD(true); - } - else if (osdChannelIndex == currentChannelIndex) - { - clearScreen(); - } - else - { - channelChange(INDEX, osdChannelIndex); - displayOSD(true); - } - } - else - { - osdChannelIndex = currentChannelIndex; - displayOSD(true); - okTriggeredOSD = true; - } -} - -void VVideoLiveTV::doSummary() -{ - if (summary.getVisible()) - { - summary.setVisible(false); - draw(); - boxstack->update(this, summary.getRegion()); - Timers::getInstance()->setTimerD(this, 1, 8); // Restart a timer to get rid of osd - return; - } - - summary.setVisible(true); - - if (osd.getVisible()) - { - Timers::getInstance()->cancelTimer(this, 1); - displayOSD(false); - } - else - { - displayOSD(true); - } -} - -void VVideoLiveTV::doKey(int command) -{ - if (!osd.getVisible()) // First key. prep the data - { - setNowNextData(); - keying = 0; - } - - int i; - for (i = keying - 1; i >= 0; i--) keyingInput[i+1] = keyingInput[i]; - keyingInput[0] = command; - keying++; - - char* keyingString = new char[numberWidth + 1]; - for (i = 0; i < numberWidth; i++) keyingString[i] = '_'; - keyingString[numberWidth] = '\0'; - - for (i = 0; i < keying; i++) keyingString[i] = keyingInput[keying - 1 - i] + 48; - - if (keying == numberWidth) - { - UINT newChannel = 0; - for(i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i); - - channelChange(NUMBER, newChannel); - osdChannelIndex = currentChannelIndex; - Timers::getInstance()->cancelTimer(this, 1); // cancel the timer to react to keying input, - displayOSD(true); // this will put one back if required - } - else - { - osdChanNum.setText(keyingString); - - if (!osd.getVisible()) - { - osd.setVisible(true); - draw(); - } - else - { - osdChanNum.draw(); - } - boxstack->update(this, osd.getRegion()); - Timers::getInstance()->setTimerD(this, 1, 3); // 3s for keying input - } - delete[] keyingString; -} - -void VVideoLiveTV::doTeletext(bool subtitlemode) -{ - if (streamType !=VDR::VIDEO) return; - bool exists=true; - - // Cancel keying - if (keying) - { - keying = 0; - // and reset the display - this is a copy from setNowNextData - char formatChanNum[20]; - SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, (*chanList)[osdChannelIndex]->number); - osdChanNum.setText(formatChanNum); - osdChanName.setText((*chanList)[osdChannelIndex]->name); - } - if (osd.getVisible()) clearScreen(); - // Draw the teletxt - VTeletextView *vtxv=((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView(); - if (vtxv==NULL) { - vtxv= new VTeletextView(((PlayerLiveTV*)player)->getTeletextDecoder(),this); - ((PlayerLiveTV*)player)->getTeletextDecoder()->registerTeletextView(vtxv); - exists=false; - } - vtxv->setSubtitleMode(subtitlemode); - vtxv->draw(); - draw(); - - if (!exists) { - BoxStack::getInstance()->add(vtxv); - } - BoxStack::getInstance()->update(this); - BoxStack::getInstance()->update(vtxv); -} - -void VVideoLiveTV::doAudioSelector() -{ - // If the osd is already visisble there might be a timer for it - Timers::getInstance()->cancelTimer(this, 1); - //This causes a deadlock with the timertrhread itself is locked - - - // Cancel keying - if (keying) - { - keying = 0; - // and reset the display - this is a copy from setNowNextData - char formatChanNum[20]; - SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, (*chanList)[osdChannelIndex]->number); - osdChanNum.setText(formatChanNum); - osdChanName.setText((*chanList)[osdChannelIndex]->name); - } - int subtitleChannel=((PlayerLiveTV*)player)->getCurrentSubtitleChannel(); - int subtitleType=0x10; - if (!(static_cast(player))->isSubtitlesOn()) { - if (((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView() && - ((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView()->isInSubtitleMode() - ) { - subtitleChannel=((PlayerLiveTV*)player)->getTeletextDecoder()->getPage(); - subtitleType=0x11; - - } else { - subtitleType=0xFF; //turnedOff - subtitleChannel=0; - } - } - - // Draw the selector - vas = new VAudioSelector(this, (*chanList)[currentChannelIndex], ((PlayerLiveTV*)player)->getCurrentAudioChannel(), - subtitleType,subtitleChannel,NULL); - Colour osdBack = Colour(0, 0, 0, 128); - vas->setBackgroundColour(osdBack); - vas->setPosition(0, osd.getScreenY() - vas->getHeight()); - vas->draw(); - - // make vas != null and displayOSD will not set a timer or do any boxstack update - summary.setVisible(false); - if (osd.getVisible()) displayOSD(false); - else displayOSD(true); - draw(); - - BoxStack::getInstance()->add(vas); - BoxStack::getInstance()->update(this); - BoxStack::getInstance()->update(vas); -} - -void VVideoLiveTV::doEPG() -{ - if (osd.getVisible()) clearScreen(); - - video->setMode(Video::QUARTER); - video->setPosition(170, 5); //TODO need to deal with 4:3 switching - - VEpg* vepg = new VEpg(this, currentChannelIndex, streamType); - vepg->draw(); - boxstack->add(vepg); - boxstack->update(vepg); -} - -void VVideoLiveTV::setNowNextData() -{ - delData(); - - Channel* currentChannel = (*chanList)[osdChannelIndex]; - - char formatChanNum[20]; - SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, currentChannel->number); - osdChanNum.setText(formatChanNum); - osdChanName.setText(currentChannel->name); - - eventList = VDR::getInstance()->getChannelSchedule(currentChannel->number); - - if (!eventList) - { - sl.addOption(tr("No channel data available"), 0, 1); - } - else - { - sort(eventList->begin(), eventList->end(), EventSorter()); - - char tempString[300]; - char tempString2[300]; - struct tm* btime; - Event* event; - int eventListSize = eventList->size(); - for(int i = 0; i < eventListSize; i++) - { - event = (*eventList)[i]; - - //btime = localtime((time_t*)&event->time); - time_t etime = event->time; - btime = localtime(&etime); -#ifndef _MSC_VER - strftime(tempString2, 299, "%0H:%0M ", btime); -#else - strftime(tempString2, 299, "%H:%M ", btime); -#endif - SNPRINTF(tempString, 299, "%s %s", tempString2, event->title); - - sl.addOption(tempString, (ULONG)event, (i==0)); - } - } -} - -void VVideoLiveTV::setSummaryData() -{ - // If osd is not being displayed, sl will be filled with now, current channel - // If the display was already on, sl will have programme to show summary for, not necessarily current channel and now - Event* selectedEvent = (Event*)sl.getCurrentOptionData(); - - if (!selectedEvent) - { - Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "No summary"); - textSummary.setText(tr("No summary available")); - } - else - { - Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Summary: %s", selectedEvent->description); - textSummary.setText(selectedEvent->description); - } -} - -void VVideoLiveTV::displayOSD(bool newNowNextData) -{ - osd.setVisible(true); - if (newNowNextData) - { - setNowNextData(); - keying = 0; - } - osd.draw(); - - if (summary.getVisible()) - { - setSummaryData(); - summary.draw(); - boxstack->update(this, &osdSummaryRegion); - } - else - { - boxstack->update(this, osd.getRegion()); - } - - bool setTimer = true; - if (vas) setTimer = false; - if (summary.getVisible()) setTimer = false; - if (textUnavailable.getVisible()) setTimer = false; - - if (setTimer) Timers::getInstance()->setTimerD(this, 1, 4); -} - -void VVideoLiveTV::clearScreen() -{ - if (!summary.getVisible()) Timers::getInstance()->cancelTimer(this, 1); - - textUnavailable.setVisible(false); - osd.setVisible(false); - summary.setVisible(false); - - okTriggeredOSD = false; - - draw(); - boxstack->update(this); -} - -void VVideoLiveTV::showUnavailable() -{ - Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Show unavailable called"); - textUnavailable.setVisible(true); - textUnavailable.draw(); - - if (!osd.getVisible()) displayOSD(true); - - boxstack->update(this, textUnavailable.getRegion()); -} - -void VVideoLiveTV::setClock() -{ - char timeString[20]; - time_t t; - time(&t); - struct tm* tms = localtime(&t); - strftime(timeString, 19, "%H:%M", tms); - clock.setText(timeString); - - time_t dt = 60 - (t % 60); // seconds to the next minute - if (dt == 0) dt = 60; // advance a whole minute if necessary - dt += t; // get a time_t value for it rather than using duration - // (so it will occur at the actual second and not second and a half) - - Timers::getInstance()->setTimerT(this, 2, dt); -} - -void VVideoLiveTV::timercall(int ref) -{ - if (ref == 1) - { - if (keying) - { - Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 key start."); - UINT newChannel = 0; - for(int i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i); - - Message* m = new Message(); - m->message = Message::CHANNEL_CHANGE; - m->to = this; - m->parameter = newChannel; - m->tag = 1; // signal to call displayOSD(); - Command::getInstance()->postMessageFromOuterSpace(m); - Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 key end."); - } - else - { - Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 not key start."); - // We have received a timer, we are not keying. If still prebuffering, don't remove the bar - if (preBuffering < 100) - { - Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Still prebuffering, not removing osd..."); - Timers::getInstance()->setTimerD(this, 1, 2); // reset timer for another 2s - return; - } - Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey 1."); - osd.setVisible(false); - okTriggeredOSD = false; - Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey 2."); - draw(); - Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey 4."); - boxstack->update(this, osd.getRegion()); - - Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey 3."); - Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey end."); - } - } - else if (ref == 2) - { - Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 2 start."); - setClock(); - if (osd.getVisible()) - { - clock.draw(); - boxstack->update(this, osd.getRegion()); - } - Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 2 end."); - } -} - -bool VVideoLiveTV::channelChange(UCHAR changeType, UINT newData) -{ - UINT newChannel = 0; - if (streamType ==VDR::VIDEO) { - VTeletextView *vtxt=((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView(); - if (vtxt ) { - BoxStack::getInstance()->remove(vtxt); - - } - } - if (changeType == INDEX) - { - newChannel = newData; - } - else if (changeType == NUMBER) - { - UINT i; - for(i = 0; i < chanList->size(); i++) - { - if ((*chanList)[i]->number == (UINT)newData) - { - newChannel = i; - break; - } - } - - if (i == chanList->size()) - { - // no such channel - return false; - } - } - else if (changeType == OFFSET) - { - if (newData == UP) newChannel = upChannel(currentChannelIndex); - else newChannel = downChannel(currentChannelIndex); - } - else if (changeType == PREVIOUS) - { - newChannel = previousChannelIndex; - } - else - { - return false; // bad input - } - - if (newChannel == currentChannelIndex) return true; - - previousChannelIndex = currentChannelIndex; - currentChannelIndex = newChannel; - - preBuffering = 0; - - Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Set player to channel %u", currentChannelIndex); - player->setChannel(currentChannelIndex); - Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Done Set player to channel %u", currentChannelIndex); - - // Blank out the symbols - sAspectRatio.setVisible(false); - bufferBar.setPercent(0); - sAudioChannels.setVisible(false); - - // Remove other stuff - if (textUnavailable.getVisible()) - { - textUnavailable.setVisible(false); - - } - - draw(); - BoxStack::getInstance()->update(this); - - return true; -} - -void VVideoLiveTV::processMessage(Message* m) -{ - if (m->message == Message::MOUSE_LBDOWN) - { - //check if press is outside this view! then simulate cancel - int x=(m->parameter>>16)-osd.getScreenX(); - int y=(m->parameter&0xFFFF)-osd.getScreenY(); - if (osd.getVisible()) { - - if ((boxRed.getX()<=x) && (boxRed.getX()+(int)boxRed.getWidth()>=x ) && - (boxRed.getY()<=y) && (boxRed.getY()+(int)boxRed.getHeight()>=y )) { - BoxStack::getInstance()->handleCommand(Remote::RED); - } else if ((boxGreen.getX()<=x) && (boxGreen.getX()+(int)boxGreen.getWidth()>=x ) && - (boxGreen.getY()<=y) && (boxGreen.getY()+(int)boxGreen.getHeight()>=y)){ - BoxStack::getInstance()->handleCommand(Remote::GREEN); - } else if ((boxYellow.getX()<=x) && (boxYellow.getX()+(int)boxYellow.getWidth()>=x ) && - (boxYellow.getY()<=y) && (boxYellow.getY()+(int)boxYellow.getHeight()>=y )){ - BoxStack::getInstance()->handleCommand(Remote::YELLOW); - } else if ((boxBlue.getX()<=x) && (boxBlue.getX()+(int)boxBlue.getWidth()>=x ) && - (boxBlue.getY()<=y) && (boxBlue.getY()+(int)boxBlue.getHeight()>=y )){ - BoxStack::getInstance()->handleCommand(Remote::BLUE); - } else { - BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press - } - - } else { - BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press - } - } - else if (m->message == Message::CHANNEL_CHANGE) - { - channelChange(NUMBER, m->parameter); - osdChannelIndex = currentChannelIndex; - if (m->tag == 1) displayOSD(true); - } - else if (m->message == Message::EPG_CLOSE) - { - video->setMode(videoMode); - } - else if (m->message == Message::CHILD_CLOSE) - { - if (m->from == vas) - { - vas = NULL; - displayOSD(false); - } - } - else if (m->message == Message::AUDIO_CHANGE_CHANNEL) - { - Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received change audio channel to %i", m->parameter); - player->setAudioChannel((m->parameter & 0xFFFF),(m->parameter & 0xFF0000)>>16); - } - else if (m->message == Message::SUBTITLE_CHANGE_CHANNEL) - { - if (streamType !=VDR::VIDEO) return; - Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received change subtitle channel to %i", m->parameter); - int type=((m->parameter & 0xFF0000)>>16); - switch (type) { - case 0x10: { //dvbsubtitle - if (streamType = VDR::VIDEO){ - player->setSubtitleChannel((m->parameter & 0xFFFF)); - (static_cast(player))->turnSubtitlesOn(true); - VTeletextView *vtxt=((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView(); - if (vtxt && vtxt->isInSubtitleMode()) { - BoxStack::getInstance()->remove(vtxt); - } - } - } break; - case 0xFF: { //nosubtitles - if (streamType = VDR::VIDEO){ - (static_cast(player))->turnSubtitlesOn(false); - VTeletextView *vtxt=((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView(); - if (vtxt && vtxt->isInSubtitleMode()) { - BoxStack::getInstance()->remove(vtxt); - } - } - } break; - case 0x11: { //videotext - (static_cast(player))->turnSubtitlesOn(false); - doTeletext(true); - ((PlayerLiveTV*)player)->getTeletextDecoder()->setPage((m->parameter & 0xFFFF)); - } break; - }; - if (vas) { - BoxStack::getInstance()->update(vas); - //BoxStack::getInstance()->update(&osd); //eveil error - } - BoxStack::getInstance()->update(this, osd.getRegion()); - - - } - else if (m->message == Message::PLAYER_EVENT) - { - switch(m->parameter) - { - case PlayerLiveTV::CONNECTION_LOST: // connection lost detected - { - Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received connection lost from player"); - Command::getInstance()->connectionLost(); - break; - } - - case PlayerLiveTV::STREAM_END: - { - // Message comes from playerlivetv through master mutex, so can do anything here - showUnavailable(); - break; - } - - case PlayerLiveTV::ASPECT43: - { - if ((video->getTVsize() == Video::ASPECT16X9) && dowss) - { - Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 43"); - wss.setWide(false); - wss.draw(); - BoxStack::getInstance()->update(this, &wssRegion); - } - - sAspectRatio.nextSymbol = WSymbol::VIDEOASPECT43; - sAspectRatio.setVisible(true); - - if (osd.getVisible()) // don't wake up the whole osd just for a aspect change - { - osd.draw(); - BoxStack::getInstance()->update(this, osd.getRegion()); - } - - break; - } - case PlayerLiveTV::ASPECT169: - { - if ((video->getTVsize() == Video::ASPECT16X9) && dowss) - { - Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 169"); - wss.setWide(true); - wss.draw(); - BoxStack::getInstance()->update(this, &wssRegion); - } - - sAspectRatio.nextSymbol = WSymbol::VIDEOASPECT169; - sAspectRatio.setVisible(true); - - if (osd.getVisible()) // don't wake up the whole osd just for a aspect change - { - osd.draw(); - BoxStack::getInstance()->update(this, osd.getRegion()); - } - - break; - } - case PlayerLiveTV::PREBUFFERING: - { - preBuffering = m->tag; - Log::getInstance()->log("VVideoRec", Log::DEBUG, "Prebuffering - %u", preBuffering); - bufferBar.setPercent(preBuffering); - - if (osd.getVisible()) - { - bufferBar.setVisible(true); - bufferBar.draw(); - Region r; - bufferBar.getRootBoxRegion(&r); ///////// FIXME !!! - BoxStack::getInstance()->update(this, &r); - - if (preBuffering == 100) - { - doAudioChannelSymbol(); - } - } - } - } - } -} - -void VVideoLiveTV::doAudioChannelSymbol() -{ - // get the doobery - Channel* currentChannel = (*chanList)[osdChannelIndex]; - - bool multiAudio = false; - #if WIN32 - if (currentChannel->numDPids > 1) multiAudio = true; - #endif - if (currentChannel->numAPids > 1) multiAudio = true; - - // draw the doobery - - if (multiAudio) sAudioChannels.nextSymbol = WSymbol::MULTIAUDIO; - else sAudioChannels.nextSymbol = WSymbol::SINGLEAUDIO; - sAudioChannels.setVisible(true); - - if (osd.getVisible()) - { - sAudioChannels.draw(); - Region r; - sAudioChannels.getRootBoxRegion(&r); ///////// FIXME !!! - // Fix this n'all. - r.w = 32; - r.h = 16; - BoxStack::getInstance()->update(this, &r); - } -} - -UINT VVideoLiveTV::upChannel(UINT index) -{ - if (index == (chanList->size() - 1)) // at the end - return 0; // so go to start - else - return index + 1; -} - -UINT VVideoLiveTV::downChannel(UINT index) -{ - if (index == 0) // at the start - return chanList->size() - 1; // so go to end - else - return index - 1; -} - -void VVideoLiveTV::toggleChopSides() -{ - if (video->getTVsize() == Video::ASPECT16X9) return; // Means nothing for 16:9 TVs - - if (videoMode == Video::NORMAL) - { - videoMode = Video::LETTERBOX; - video->setMode(Video::LETTERBOX); - } - else - { - videoMode = Video::NORMAL; - video->setMode(Video::NORMAL); - } -} - -void VVideoLiveTV::drawOSDBitmap(UINT posX, UINT posY, const Bitmap& bm) -{ - drawBitmap(posX, posY, bm); - Region r; - r.x = posX; r.y = posY; r.w = bm.getWidth(); r.h = bm.getHeight(); - boxstack->update(this, &r); -} - -void VVideoLiveTV::clearOSD() -{ - rectangle(area, Colour(0,0,0,0)); - boxstack->update(this, &area); -} - -void VVideoLiveTV::clearOSDArea(UINT posX, UINT posY, UINT width, UINT height) -{ - Region r; - r.x = posX; r.y = posY; r.w = width; r.h = height; - rectangle(r, Colour(0,0,0,0)); - boxstack->update(this, &r); -} +/* + Copyright 2007-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 "vvideolivetv.h" + +#include "vchannellist.h" +#include "video.h" +#include "audio.h" +#include "playerlive.h" +#include "playerlivetv.h" +#include "playerliveradio.h" +#include "channel.h" +#include "boxstack.h" +#include "colour.h" +#include "osd.h" +#include "command.h" +#include "i18n.h" +#include "wtextbox.h" +#include "remote.h" +#include "vaudioselector.h" +#include "colour.h" +#include "event.h" +#include "timers.h" +#include "vepg.h" +#include "bitmap.h" +#include "log.h" +#include "vteletextview.h" + +VVideoLiveTV::VVideoLiveTV(ChannelList* tchanList, ULONG initialChannelNumber, VChannelList* tvchannelList) +{ + vdr = VDR::getInstance(); + boxstack = BoxStack::getInstance(); + video = Video::getInstance(); + + vas = NULL; + + chanList = tchanList; + vchannelList = tvchannelList; + numberWidth = (int)VDR::getInstance()->getChannelNumberWidth(); + + currentChannelIndex = 0; + previousChannelIndex = 0; + osdChannelIndex = 0; + keying = 0; + preBuffering = 0; + + playing = false; + + // Convert channel number to index + UINT i; + for(i = 0; i < chanList->size(); i++) + { + if ((*chanList)[i]->number == (UINT)initialChannelNumber) + { + currentChannelIndex = i; + osdChannelIndex = i; + break; + } + } + + eventList = NULL; + + videoMode = video->getMode(); + + if ((*chanList)[currentChannelIndex]->type == VDR::VIDEO) + { + streamType = VDR::VIDEO; + player = new PlayerLiveTV(Command::getInstance(), this, this, chanList); + } + else + { + streamType = VDR::RADIO; + player = new PlayerLiveRadio(Command::getInstance(), this, chanList); + } + player->init(); + + setSize(video->getScreenWidth(), video->getScreenHeight()); + createBuffer(); + Colour transparent(0, 0, 0, 0); + setBackgroundColour(transparent); + + dowss = false; + char* optionWSS = vdr->configLoad("General", "WSS"); + if (optionWSS) + { + if (strstr(optionWSS, "Yes")) dowss = true; + delete[] optionWSS; + } + Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Do WSS: %u", dowss); + + if (dowss) + { + wss.setFormat(video->getFormat()); + wss.setWide(true); + add(&wss); + + wssRegion.x = 0; + wssRegion.y = 6; + wssRegion.w = video->getScreenWidth(); + wssRegion.h = 2; + } + + // This variable is set to true if the user pressed OK to bring the OSD on screen + // This is only used on old remotes to stop up/down buttons being used for osd-epg scrolling + okTriggeredOSD = false; + + Colour osdBack = Colour(0, 0, 0, 128); + + osd.setBackgroundColour(osdBack); + osd.setPosition(0, video->getScreenHeight() - 150); + osd.setSize(video->getScreenWidth(), 150); + osd.setVisible(false); + add(&osd); + + clock.setBackgroundColour(osdBack); + clock.setPosition(osd.getWidth() - 100, 4); + clock.setSize(90, 30); + osd.add(&clock); + + osdChanNum.setBackgroundColour(osdBack); + osdChanNum.setPosition(50, 4); + osdChanNum.setSize((numberWidth*10) + 22, 30); // 10 px = width of number chars in font + osd.add(&osdChanNum); + + osdChanName.setBackgroundColour(osdBack); + osdChanName.setPosition(osdChanNum.getX2() + 10, 4); + osdChanName.setSize(300, 30); + osd.add(&osdChanName); + + boxRed.setBackgroundColour(Colour::RED); + boxRed.setPosition(54, 104); + boxRed.setSize(18, 16); + osd.add(&boxRed); + + boxGreen.setBackgroundColour(Colour::GREEN); + boxGreen.setPosition(220, 104); + boxGreen.setSize(18, 16); + osd.add(&boxGreen); + + boxYellow.setBackgroundColour(Colour::YELLOW); + boxYellow.setPosition(390, 104); + boxYellow.setSize(18, 16); + osd.add(&boxYellow); + + boxBlue.setBackgroundColour(Colour::BLUE); + boxBlue.setPosition(560, 104); + boxBlue.setSize(18, 16); + osd.add(&boxBlue); + + textRed.setBackgroundColour(osdBack); + textRed.setPosition(boxRed.getX2(), 98); + textRed.setSize(boxGreen.getX() - boxRed.getX2(), 30); + textRed.setText(tr("Summary")); + osd.add(&textRed); + + if (streamType == VDR::VIDEO) + { + textGreen.setBackgroundColour(osdBack); + textGreen.setPosition(boxGreen.getX2(), 98); + textGreen.setSize(boxYellow.getX() - boxGreen.getX2(), 30); + textGreen.setText(tr("Audio")); + osd.add(&textGreen); + } + + textYellow.setBackgroundColour(osdBack); + textYellow.setPosition(boxYellow.getX2(), 98); + textYellow.setSize(boxBlue.getX() - boxYellow.getX2(), 30); + textYellow.setText(tr("Teletext")); + osd.add(&textYellow); + + textBlue.setBackgroundColour(osdBack); + textBlue.setPosition(boxBlue.getX2(), 98); + textBlue.setSize(osd.getX2() - boxBlue.getX2(), 30); + textBlue.setText(tr("EPG")); + osd.add(&textBlue); + + sl.setBackgroundColour(osdBack); + sl.setPosition(70, 36); + sl.setSize(500, 58); + sl.setNoLoop(); + osd.add(&sl); + + // Summary Box + summary.setBackgroundColour(osdBack); + summary.setPosition(0, video->getScreenHeight() - 300); + summary.setSize(video->getScreenWidth(), 150); + summary.setVisible(false); + add(&summary); + + textSummary.setBackgroundColour(osdBack); + textSummary.setPosition(40, 10); + textSummary.setSize(video->getScreenWidth() - 80, 130); + textSummary.setParaMode(true); + summary.add(&textSummary); + + summaryBlackLine.setBackgroundColour(Colour::BLACK); + summaryBlackLine.setPosition(0, summary.getHeight() - 4); + summaryBlackLine.setSize(summary.getWidth(), 4); + summary.add(&summaryBlackLine); + + sAspectRatio.setPosition(osd.getWidth() - 90, 40); + sAspectRatio.nextColour = Colour::SELECTHIGHLIGHT; + sAspectRatio.setVisible(false); + osd.add(&sAspectRatio); + + bufferBar.setPosition(osd.getWidth() - 90, 70); + bufferBar.setSize(40, 20); + bufferBar.setVisible(true); + osd.add(&bufferBar); + + sAudioChannels.setPosition(osd.getWidth() - 130, 40); + sAudioChannels.nextColour = Colour::SELECTHIGHLIGHT; + sAudioChannels.setVisible(false); + osd.add(&sAudioChannels); + + textUnavailable.setBackgroundColour(osdBack); + textUnavailable.setPosition(60, 30); + textUnavailable.setSize(osd.getWidth() - 120, 30); + textUnavailable.setText(tr("Channel Unavailable")); + textUnavailable.setVisible(false); + add(&textUnavailable); + + // FIXME painful + Region r1 = summary.getRegionR(); + Region r2 = osd.getRegionR(); + osdSummaryRegion = r1 + r2; +} + +void VVideoLiveTV::preDelete() +{ + if (playing) stop(); +} + +VVideoLiveTV::~VVideoLiveTV() +{ + delete player; + video->setDefaultAspect(); + delData(); +} + +void VVideoLiveTV::delData() +{ + if (eventList) + { + int eventListSize = eventList->size(); + for(int i = 0; i < eventListSize; i++) + { + delete (*eventList)[i]; + } + eventList->clear(); + delete eventList; + + } + sl.clear(); +} + +int VVideoLiveTV::handleCommand(int command) +{ + switch(command) + { + case Remote::BACK: + { + if (osd.getVisible() && !textUnavailable.getVisible()) + { + clearScreen(); + return 2; + } + // else drop through to stop + } + case Remote::STOP: + { + stop(); + vchannelList->highlightChannel((*chanList)[currentChannelIndex]); + return 4; + } + + // NEW REMOTE ONLY - navigate EPG, bring it onscreen if it's not there + case Remote::UP: + { + doUpDown(false); + return 2; + } + case Remote::DOWN: + { + doUpDown(true); + return 2; + } + case Remote::LEFT: + { + doLeftRight(false); + return 2; + } + case Remote::RIGHT: + { + doLeftRight(true); + return 2; + } + // Continue new remote only... + case Remote::CHANNELUP: + { + doChanUpDown(UP); + return 2; + } + case Remote::CHANNELDOWN: + { + doChanUpDown(DOWN); + return 2; + } + + // END NEW REMOTE ONLY, START OLD REMOTE ONLY + + // DF_LEFT and DF_RIGHT never get here because they are stolen + // by command as vol- and vol+ + + // Old remote. Decide what to do based on whether + // OK was pressed - osd shown manually, use up/down for epg nav + // UP/DOWN was pressed to change channel, osd was shown auto, use up/down for ch+/ch- + + case Remote::DF_UP: + { + // Old remote, decide what to do based on okTriggeredOSD + if (okTriggeredOSD) doUpDown(false); + else doChanUpDown(UP); + return 2; + } + case Remote::DF_DOWN: + { + // Old remote, decide what to do based on okTriggeredOSD + if (okTriggeredOSD) doUpDown(true); + else doChanUpDown(DOWN); + return 2; + } + + // END NEW/OLD REMOTE STUFF + + case Remote::PREVCHANNEL: + { + channelChange(PREVIOUS, 0); + osdChannelIndex = currentChannelIndex; + displayOSD(true); + return 2; + } + case Remote::OK: + { + doOK(); + return 2; + } + case Remote::RED: + case Remote::MENU: + { + doSummary(); + return 2; + } + case Remote::FULL: + case Remote::TV: + { + toggleChopSides(); + return 2; + } + + case Remote::ZERO: + case Remote::ONE: + case Remote::TWO: + case Remote::THREE: + case Remote::FOUR: + case Remote::FIVE: + case Remote::SIX: + case Remote::SEVEN: + case Remote::EIGHT: + case Remote::NINE: + { + // key in channel number + doKey(command); + return 2; + } + + case Remote::GREEN: + { + if (streamType == VDR::VIDEO) doAudioSelector(); + return 2; + } + case Remote::YELLOW: + { + if (streamType ==VDR::VIDEO) doTeletext(); //TODO: Add a selector for subtitles or teletext + return 2; + } + case Remote::GUIDE: + case Remote::BLUE: + { + doEPG(); + return 2; + } + case Remote::RECORD: + if (streamType == VDR::VIDEO) + (static_cast(player))->toggleSubtitles(); + return 2; + } + + return 1; +} + +void VVideoLiveTV::go() +{ + playing = true; + draw(); + boxstack->update(this); + + setClock(); + displayOSD(true); + + player->go(currentChannelIndex); +} + +void VVideoLiveTV::stop() +{ + Timers::getInstance()->cancelTimer(this, 1); + Timers::getInstance()->cancelTimer(this, 2); + player->stop(); + playing = false; +} + +void VVideoLiveTV::doLeftRight(bool right) +{ + if (osd.getVisible()) + { + if (right) osdChannelIndex = upChannel(osdChannelIndex); + else osdChannelIndex = downChannel(osdChannelIndex); + } + else + { + osdChannelIndex = currentChannelIndex; + } + displayOSD(true); +} + +void VVideoLiveTV::doUpDown(bool down) +{ + if (osd.getVisible()) + { + if (down) sl.down(); + else sl.up(); + sl.draw(); + + displayOSD(false); + } + else + { + displayOSD(true); + } +} + +void VVideoLiveTV::doChanUpDown(int which) +{ + channelChange(OFFSET, which); + osdChannelIndex = currentChannelIndex; + displayOSD(true); +} + +void VVideoLiveTV::doOK() +{ + if (osd.getVisible()) + { + if (keying) + { + UINT newChannel = 0; + for(int i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i); + + channelChange(NUMBER, newChannel); + osdChannelIndex = currentChannelIndex; + displayOSD(true); + } + else if (osdChannelIndex == currentChannelIndex) + { + clearScreen(); + } + else + { + channelChange(INDEX, osdChannelIndex); + displayOSD(true); + } + } + else + { + osdChannelIndex = currentChannelIndex; + displayOSD(true); + okTriggeredOSD = true; + } +} + +void VVideoLiveTV::doSummary() +{ + if (summary.getVisible()) + { + summary.setVisible(false); + draw(); + boxstack->update(this, summary.getRegion()); + Timers::getInstance()->setTimerD(this, 1, 8); // Restart a timer to get rid of osd + return; + } + + summary.setVisible(true); + + if (osd.getVisible()) + { + Timers::getInstance()->cancelTimer(this, 1); + displayOSD(false); + } + else + { + displayOSD(true); + } +} + +void VVideoLiveTV::doKey(int command) +{ + if (!osd.getVisible()) // First key. prep the data + { + setNowNextData(); + keying = 0; + } + + int i; + for (i = keying - 1; i >= 0; i--) keyingInput[i+1] = keyingInput[i]; + keyingInput[0] = command; + keying++; + + char* keyingString = new char[numberWidth + 1]; + for (i = 0; i < numberWidth; i++) keyingString[i] = '_'; + keyingString[numberWidth] = '\0'; + + for (i = 0; i < keying; i++) keyingString[i] = keyingInput[keying - 1 - i] + 48; + + if (keying == numberWidth) + { + UINT newChannel = 0; + for(i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i); + + channelChange(NUMBER, newChannel); + osdChannelIndex = currentChannelIndex; + Timers::getInstance()->cancelTimer(this, 1); // cancel the timer to react to keying input, + displayOSD(true); // this will put one back if required + } + else + { + osdChanNum.setText(keyingString); + + if (!osd.getVisible()) + { + osd.setVisible(true); + draw(); + } + else + { + osdChanNum.draw(); + } + boxstack->update(this, osd.getRegion()); + Timers::getInstance()->setTimerD(this, 1, 3); // 3s for keying input + } + delete[] keyingString; +} + +void VVideoLiveTV::doTeletext(bool subtitlemode) +{ + if (streamType !=VDR::VIDEO) return; + bool exists=true; + + // Cancel keying + if (keying) + { + keying = 0; + // and reset the display - this is a copy from setNowNextData + char formatChanNum[20]; + SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, (*chanList)[osdChannelIndex]->number); + osdChanNum.setText(formatChanNum); + osdChanName.setText((*chanList)[osdChannelIndex]->name); + } + if (osd.getVisible()) clearScreen(); + // Draw the teletxt + VTeletextView *vtxv=((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView(); + if (vtxv==NULL) { + vtxv= new VTeletextView(((PlayerLiveTV*)player)->getTeletextDecoder(),this); + ((PlayerLiveTV*)player)->getTeletextDecoder()->registerTeletextView(vtxv); + exists=false; + } + vtxv->setSubtitleMode(subtitlemode); + vtxv->draw(); + draw(); + + if (!exists) { + BoxStack::getInstance()->add(vtxv); + } + BoxStack::getInstance()->update(this); + BoxStack::getInstance()->update(vtxv); +} + +void VVideoLiveTV::doAudioSelector() +{ + // If the osd is already visisble there might be a timer for it + Timers::getInstance()->cancelTimer(this, 1); + //This causes a deadlock with the timertrhread itself is locked + + + // Cancel keying + if (keying) + { + keying = 0; + // and reset the display - this is a copy from setNowNextData + char formatChanNum[20]; + SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, (*chanList)[osdChannelIndex]->number); + osdChanNum.setText(formatChanNum); + osdChanName.setText((*chanList)[osdChannelIndex]->name); + } + int subtitleChannel=((PlayerLiveTV*)player)->getCurrentSubtitleChannel(); + int subtitleType=0x10; + if (!(static_cast(player))->isSubtitlesOn()) { + if (((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView() && + ((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView()->isInSubtitleMode() + ) { + subtitleChannel=((PlayerLiveTV*)player)->getTeletextDecoder()->getPage(); + subtitleType=0x11; + + } else { + subtitleType=0xFF; //turnedOff + subtitleChannel=0; + } + } + + // Draw the selector + vas = new VAudioSelector(this, (*chanList)[currentChannelIndex], ((PlayerLiveTV*)player)->getCurrentAudioChannel(), + subtitleType,subtitleChannel,NULL); + Colour osdBack = Colour(0, 0, 0, 128); + vas->setBackgroundColour(osdBack); + vas->setPosition(0, osd.getScreenY() - vas->getHeight()); + vas->draw(); + + // make vas != null and displayOSD will not set a timer or do any boxstack update + summary.setVisible(false); + if (osd.getVisible()) displayOSD(false); + else displayOSD(true); + draw(); + + BoxStack::getInstance()->add(vas); + BoxStack::getInstance()->update(this); + BoxStack::getInstance()->update(vas); +} + +void VVideoLiveTV::doEPG() +{ + if (osd.getVisible()) clearScreen(); + + video->setMode(Video::QUARTER); + video->setPosition(170, 5); //TODO need to deal with 4:3 switching + + VEpg* vepg = new VEpg(this, currentChannelIndex, streamType); + vepg->draw(); + boxstack->add(vepg); + boxstack->update(vepg); +} + +void VVideoLiveTV::setNowNextData() +{ + delData(); + + Channel* currentChannel = (*chanList)[osdChannelIndex]; + + char formatChanNum[20]; + SNPRINTF(formatChanNum, 19, "%0*lu", numberWidth, currentChannel->number); + osdChanNum.setText(formatChanNum); + osdChanName.setText(currentChannel->name); + + eventList = VDR::getInstance()->getChannelSchedule(currentChannel->number); + + if (!eventList) + { + sl.addOption(tr("No channel data available"), 0, 1); + } + else + { + sort(eventList->begin(), eventList->end(), EventSorter()); + + char tempString[300]; + char tempString2[300]; + struct tm* btime; + Event* event; + int eventListSize = eventList->size(); + for(int i = 0; i < eventListSize; i++) + { + event = (*eventList)[i]; + + //btime = localtime((time_t*)&event->time); + time_t etime = event->time; + btime = localtime(&etime); +#ifndef _MSC_VER + strftime(tempString2, 299, "%0H:%0M ", btime); +#else + strftime(tempString2, 299, "%H:%M ", btime); +#endif + SNPRINTF(tempString, 299, "%s %s", tempString2, event->title); + + sl.addOption(tempString, (ULONG)event, (i==0)); + } + } +} + +void VVideoLiveTV::setSummaryData() +{ + // If osd is not being displayed, sl will be filled with now, current channel + // If the display was already on, sl will have programme to show summary for, not necessarily current channel and now + Event* selectedEvent = (Event*)sl.getCurrentOptionData(); + + if (!selectedEvent) + { + Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "No summary"); + textSummary.setText(tr("No summary available")); + } + else + { + Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Summary: %s", selectedEvent->description); + textSummary.setText(selectedEvent->description); + } +} + +void VVideoLiveTV::displayOSD(bool newNowNextData) +{ + osd.setVisible(true); + if (newNowNextData) + { + setNowNextData(); + keying = 0; + } + osd.draw(); + + if (summary.getVisible()) + { + setSummaryData(); + summary.draw(); + boxstack->update(this, &osdSummaryRegion); + } + else + { + boxstack->update(this, osd.getRegion()); + } + + bool setTimer = true; + if (vas) setTimer = false; + if (summary.getVisible()) setTimer = false; + if (textUnavailable.getVisible()) setTimer = false; + + if (setTimer) Timers::getInstance()->setTimerD(this, 1, 4); +} + +void VVideoLiveTV::clearScreen() +{ + if (!summary.getVisible()) Timers::getInstance()->cancelTimer(this, 1); + + textUnavailable.setVisible(false); + osd.setVisible(false); + summary.setVisible(false); + + okTriggeredOSD = false; + + draw(); + boxstack->update(this); +} + +void VVideoLiveTV::showUnavailable() +{ + Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Show unavailable called"); + textUnavailable.setVisible(true); + textUnavailable.draw(); + + if (!osd.getVisible()) displayOSD(true); + + boxstack->update(this, textUnavailable.getRegion()); +} + +void VVideoLiveTV::setClock() +{ + char timeString[20]; + time_t t; + time(&t); + struct tm* tms = localtime(&t); + strftime(timeString, 19, "%H:%M", tms); + clock.setText(timeString); + + time_t dt = 60 - (t % 60); // seconds to the next minute + if (dt == 0) dt = 60; // advance a whole minute if necessary + dt += t; // get a time_t value for it rather than using duration + // (so it will occur at the actual second and not second and a half) + + Timers::getInstance()->setTimerT(this, 2, dt); +} + +void VVideoLiveTV::timercall(int ref) +{ + if (ref == 1) + { + if (keying) + { + Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 key start."); + UINT newChannel = 0; + for(int i = keying - 1; i >= 0; i--) newChannel += keyingInput[i] * (ULONG)pow(10., i); + + Message* m = new Message(); + m->message = Message::CHANNEL_CHANGE; + m->to = this; + m->parameter = newChannel; + m->tag = 1; // signal to call displayOSD(); + Command::getInstance()->postMessageFromOuterSpace(m); + Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 key end."); + } + else + { + Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 not key start."); + // We have received a timer, we are not keying. If still prebuffering, don't remove the bar + if (preBuffering < 100) + { + Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Still prebuffering, not removing osd..."); + Timers::getInstance()->setTimerD(this, 1, 2); // reset timer for another 2s + return; + } + Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey 1."); + osd.setVisible(false); + okTriggeredOSD = false; + Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey 2."); + draw(); + Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey 4."); + boxstack->update(this, osd.getRegion()); + + Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey 3."); + Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 1 notkey end."); + } + } + else if (ref == 2) + { + Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 2 start."); + setClock(); + if (osd.getVisible()) + { + clock.draw(); + boxstack->update(this, osd.getRegion()); + } + Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Timer Call 2 end."); + } +} + +bool VVideoLiveTV::channelChange(UCHAR changeType, UINT newData) +{ + UINT newChannel = 0; + if (streamType ==VDR::VIDEO) { + VTeletextView *vtxt=((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView(); + if (vtxt ) { + BoxStack::getInstance()->remove(vtxt); + + } + } + if (changeType == INDEX) + { + newChannel = newData; + } + else if (changeType == NUMBER) + { + UINT i; + for(i = 0; i < chanList->size(); i++) + { + if ((*chanList)[i]->number == (UINT)newData) + { + newChannel = i; + break; + } + } + + if (i == chanList->size()) + { + // no such channel + return false; + } + } + else if (changeType == OFFSET) + { + if (newData == UP) newChannel = upChannel(currentChannelIndex); + else newChannel = downChannel(currentChannelIndex); + } + else if (changeType == PREVIOUS) + { + newChannel = previousChannelIndex; + } + else + { + return false; // bad input + } + + if (newChannel == currentChannelIndex) return true; + + previousChannelIndex = currentChannelIndex; + currentChannelIndex = newChannel; + + preBuffering = 0; + + Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Set player to channel %u", currentChannelIndex); + player->setChannel(currentChannelIndex); + Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Done Set player to channel %u", currentChannelIndex); + + // Blank out the symbols + sAspectRatio.setVisible(false); + bufferBar.setPercent(0); + sAudioChannels.setVisible(false); + + // Remove other stuff + if (textUnavailable.getVisible()) + { + textUnavailable.setVisible(false); + + } + + draw(); + BoxStack::getInstance()->update(this); + + return true; +} + +void VVideoLiveTV::processMessage(Message* m) +{ + if (m->message == Message::MOUSE_LBDOWN) + { + //check if press is outside this view! then simulate cancel + int x=(m->parameter>>16)-osd.getScreenX(); + int y=(m->parameter&0xFFFF)-osd.getScreenY(); + if (osd.getVisible()) { + + if ((boxRed.getX()<=x) && (boxRed.getX()+(int)boxRed.getWidth()>=x ) && + (boxRed.getY()<=y) && (boxRed.getY()+(int)boxRed.getHeight()>=y )) { + BoxStack::getInstance()->handleCommand(Remote::RED); + } else if ((boxGreen.getX()<=x) && (boxGreen.getX()+(int)boxGreen.getWidth()>=x ) && + (boxGreen.getY()<=y) && (boxGreen.getY()+(int)boxGreen.getHeight()>=y)){ + BoxStack::getInstance()->handleCommand(Remote::GREEN); + } else if ((boxYellow.getX()<=x) && (boxYellow.getX()+(int)boxYellow.getWidth()>=x ) && + (boxYellow.getY()<=y) && (boxYellow.getY()+(int)boxYellow.getHeight()>=y )){ + BoxStack::getInstance()->handleCommand(Remote::YELLOW); + } else if ((boxBlue.getX()<=x) && (boxBlue.getX()+(int)boxBlue.getWidth()>=x ) && + (boxBlue.getY()<=y) && (boxBlue.getY()+(int)boxBlue.getHeight()>=y )){ + BoxStack::getInstance()->handleCommand(Remote::BLUE); + } else { + BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press + } + + } else { + BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press + } + } + else if (m->message == Message::CHANNEL_CHANGE) + { + channelChange(NUMBER, m->parameter); + osdChannelIndex = currentChannelIndex; + if (m->tag == 1) displayOSD(true); + } + else if (m->message == Message::EPG_CLOSE) + { + video->setMode(videoMode); + } + else if (m->message == Message::CHILD_CLOSE) + { + if (m->from == vas) + { + vas = NULL; + displayOSD(false); + } + } + else if (m->message == Message::AUDIO_CHANGE_CHANNEL) + { + Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received change audio channel to %i", m->parameter); + player->setAudioChannel((m->parameter & 0xFFFF),(m->parameter & 0xFF0000)>>16); + } + else if (m->message == Message::SUBTITLE_CHANGE_CHANNEL) + { + if (streamType !=VDR::VIDEO) return; + Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received change subtitle channel to %i", m->parameter); + int type=((m->parameter & 0xFF0000)>>16); + switch (type) { + case 0x10: { //dvbsubtitle + if (streamType == VDR::VIDEO){ + player->setSubtitleChannel((m->parameter & 0xFFFF)); + (static_cast(player))->turnSubtitlesOn(true); + VTeletextView *vtxt=((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView(); + if (vtxt && vtxt->isInSubtitleMode()) { + BoxStack::getInstance()->remove(vtxt); + } + } + } break; + case 0xFF: { //nosubtitles + if (streamType == VDR::VIDEO){ + (static_cast(player))->turnSubtitlesOn(false); + VTeletextView *vtxt=((PlayerLiveTV*)player)->getTeletextDecoder()->getTeletxtView(); + if (vtxt && vtxt->isInSubtitleMode()) { + BoxStack::getInstance()->remove(vtxt); + } + } + } break; + case 0x11: { //videotext + (static_cast(player))->turnSubtitlesOn(false); + doTeletext(true); + ((PlayerLiveTV*)player)->getTeletextDecoder()->setPage((m->parameter & 0xFFFF)); + } break; + }; + if (vas) { + BoxStack::getInstance()->update(vas); + //BoxStack::getInstance()->update(&osd); //eveil error + } + BoxStack::getInstance()->update(this, osd.getRegion()); + + + } + else if (m->message == Message::PLAYER_EVENT) + { + switch(m->parameter) + { + case PlayerLiveTV::CONNECTION_LOST: // connection lost detected + { + Log::getInstance()->log("VVideoLiveTV", Log::DEBUG, "Received connection lost from player"); + Command::getInstance()->connectionLost(); + break; + } + + case PlayerLiveTV::STREAM_END: + { + // Message comes from playerlivetv through master mutex, so can do anything here + showUnavailable(); + break; + } + + case PlayerLiveTV::ASPECT43: + { + if ((video->getTVsize() == Video::ASPECT16X9) && dowss) + { + Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 43"); + wss.setWide(false); + wss.draw(); + BoxStack::getInstance()->update(this, &wssRegion); + } + + sAspectRatio.nextSymbol = WSymbol::VIDEOASPECT43; + sAspectRatio.setVisible(true); + + if (osd.getVisible()) // don't wake up the whole osd just for a aspect change + { + osd.draw(); + BoxStack::getInstance()->update(this, osd.getRegion()); + } + + break; + } + case PlayerLiveTV::ASPECT169: + { + if ((video->getTVsize() == Video::ASPECT16X9) && dowss) + { + Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 169"); + wss.setWide(true); + wss.draw(); + BoxStack::getInstance()->update(this, &wssRegion); + } + + sAspectRatio.nextSymbol = WSymbol::VIDEOASPECT169; + sAspectRatio.setVisible(true); + + if (osd.getVisible()) // don't wake up the whole osd just for a aspect change + { + osd.draw(); + BoxStack::getInstance()->update(this, osd.getRegion()); + } + + break; + } + case PlayerLiveTV::PREBUFFERING: + { + preBuffering = m->tag; + Log::getInstance()->log("VVideoRec", Log::DEBUG, "Prebuffering - %u", preBuffering); + bufferBar.setPercent(preBuffering); + + if (osd.getVisible()) + { + bufferBar.setVisible(true); + bufferBar.draw(); + Region r; + bufferBar.getRootBoxRegion(&r); ///////// FIXME !!! + BoxStack::getInstance()->update(this, &r); + + if (preBuffering == 100) + { + doAudioChannelSymbol(); + } + } + } + } + } +} + +void VVideoLiveTV::doAudioChannelSymbol() +{ + // get the doobery + Channel* currentChannel = (*chanList)[osdChannelIndex]; + + bool multiAudio = false; + if (Audio::getInstance()->supportsAc3()) { + if ((currentChannel->numDPids+currentChannel->numAPids) > 1) multiAudio = true; + } + if (currentChannel->numAPids > 1) multiAudio = true; + + // draw the doobery + + if (multiAudio) sAudioChannels.nextSymbol = WSymbol::MULTIAUDIO; + else sAudioChannels.nextSymbol = WSymbol::SINGLEAUDIO; + sAudioChannels.setVisible(true); + + if (osd.getVisible()) + { + sAudioChannels.draw(); + Region r; + sAudioChannels.getRootBoxRegion(&r); ///////// FIXME !!! + // Fix this n'all. + r.w = 32; + r.h = 16; + BoxStack::getInstance()->update(this, &r); + } +} + +UINT VVideoLiveTV::upChannel(UINT index) +{ + if (index == (chanList->size() - 1)) // at the end + return 0; // so go to start + else + return index + 1; +} + +UINT VVideoLiveTV::downChannel(UINT index) +{ + if (index == 0) // at the start + return chanList->size() - 1; // so go to end + else + return index - 1; +} + +void VVideoLiveTV::toggleChopSides() +{ + if (video->getTVsize() == Video::ASPECT16X9) return; // Means nothing for 16:9 TVs + + if (videoMode == Video::NORMAL) + { + videoMode = Video::LETTERBOX; + video->setMode(Video::LETTERBOX); + } + else + { + videoMode = Video::NORMAL; + video->setMode(Video::NORMAL); + } +} + +void VVideoLiveTV::drawOSDBitmap(UINT posX, UINT posY, const Bitmap& bm) +{ + drawBitmap(posX, posY, bm); + Region r; + r.x = posX; r.y = posY; r.w = bm.getWidth(); r.h = bm.getHeight(); + boxstack->update(this, &r); +} + +void VVideoLiveTV::clearOSD() +{ + rectangle(area, Colour(0,0,0,0)); + boxstack->update(this, &area); +} + +void VVideoLiveTV::clearOSDArea(UINT posX, UINT posY, UINT width, UINT height) +{ + Region r; + r.x = posX; r.y = posY; r.w = width; r.h = height; + rectangle(r, Colour(0,0,0,0)); + boxstack->update(this, &r); +} diff --git a/vvideomedia.cc b/vvideomedia.cc index daca8b1..9365a7c 100644 --- a/vvideomedia.cc +++ b/vvideomedia.cc @@ -1,784 +1,784 @@ -/* - 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. -*/ - -#include "vvideomedia.h" -#include "vmedialist.h" -#include "media.h" -#include "mediaplayer.h" - -#include "command.h" -#include "osd.h" -#include "wsymbol.h" -#include "audio.h" -#include "video.h" -#include "timers.h" -#include "playermedia.h" -#include "recording.h" -#include "vaudioselector.h" -#include "message.h" -#include "remote.h" -#include "boxstack.h" -#include "vinfo.h" -#include "i18n.h" -#include "log.h" -#include "recinfo.h" - -//use the picture channel -#define MEDIACHANNEL 1 - -//we misuse PLAYER_EVENTS for timer messages -//this should be larger then any player message -#define PLAYER_TIMER_BASE 100 - -VVideoMedia::VVideoMedia(Media* media, VMediaList *p) -{ - lparent=p; - boxstack = BoxStack::getInstance(); - video = Video::getInstance(); - timers = Timers::getInstance(); - vas = NULL; - vsummary = NULL; - lengthBytes=0; - myMedia = new Media(media); - - player = new PlayerMedia(this); - player->run(); - - videoMode = video->getMode(); - - playing = false; - - - setSize(video->getScreenWidth(), video->getScreenHeight()); - createBuffer(); - transparent.set(0, 0, 0, 0); - setBackgroundColour(transparent); - - barRegion.x = 0; - barRegion.y = video->getScreenHeight() - 58; // FIXME, need to be - 1? and below? - barRegion.w = video->getScreenWidth(); - barRegion.h = 58; - - clocksRegion.x = barRegion.x + 140; - clocksRegion.y = barRegion.y + 12; - clocksRegion.w = 170; - clocksRegion.h = surface->getFontHeight(); - - - barBlue.set(0, 0, 150, 150); - - barShowing = false; - barGenHold = false; - barScanHold = false; - barVasHold = false; - - dowss = false; - char* optionWSS = VDR::getInstance()->configLoad("General", "WSS"); - if (optionWSS) - { - if (strstr(optionWSS, "Yes")) dowss = true; - delete[] optionWSS; - } - Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Do WSS: %u", dowss); - - if (dowss) - { - wss.setFormat(video->getFormat()); - wss.setWide(true); - add(&wss); - - wssRegion.x = 0; - wssRegion.y = 0; - wssRegion.w = video->getScreenWidth(); - wssRegion.h = 300; - } -} - -VVideoMedia::~VVideoMedia() -{ - Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Entering destructor"); - - if (vas) - { - boxstack->remove(vas); - vas = NULL; - } - - if (vsummary) { - remove(vsummary); - delete vsummary; - } - - if (playing) stopPlay(); - video->setDefaultAspect(); - - timers->cancelTimer(this, 1); - timers->cancelTimer(this, 2); - - delete myMedia; - Log::getInstance()->log("VVideoMedia", Log::DEBUG, "shutting down player"); - player->shutdown(); - delete player; - Log::getInstance()->log("VVideoMedia", Log::DEBUG, "deleted"); -} - -void VVideoMedia::go(bool resume) -{ - ULONG startFrameNum=0; - - Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Starting stream: %s at frame: %lu", myMedia->getFileName(), startFrameNum); - - lengthBytes = 0; - - int rt=0; - const MediaURI *u=myMedia->getURI(); - if (!u) { - Log::getInstance()->log("VVideoMedia", Log::ERR, "stream: %s has no URI", myMedia->getFileName()); - rt=-1; - } - else { - rt=MediaPlayer::getInstance()->openMedium(MEDIACHANNEL,u,&lengthBytes,area.w,area.h); - } - if (rt==0) - { - //TODO: figure out len in frames - int seq=player->playNew(MEDIACHANNEL,lengthBytes,0); - int ok=player->waitForSequence(2,seq); - if (ok < 0) rt=-1; - else { - playing = true; - doBar(0); - } - } - if (rt != 0) - { - stopPlay(); // clean up - - Message* m = new Message(); - m->message = Message::CLOSE_ME; - m->from = this; - m->to = boxstack; - Command::getInstance()->postMessageNoLock(m); - - VInfo* vi = new VInfo(); - vi->setSize(400, 150); - vi->createBuffer(); - if (video->getFormat() == Video::PAL) - vi->setPosition(170, 200); - else - vi->setPosition(160, 150); - vi->setExitable(); - vi->setBorderOn(1); - vi->setTitleBarOn(0); - vi->setOneLiner(tr("Error playing media")); - vi->draw(); - - m = new Message(); - m->message = Message::ADD_VIEW; - m->to = boxstack; - m->parameter = (ULONG)vi; - Command::getInstance()->postMessageNoLock(m); - } -} - -int VVideoMedia::handleCommand(int command) -{ - switch(command) - { - case Remote::PLAY: - { - player->play(); - doBar(0); - return 2; - } - - case Remote::BACK: - { - if (vsummary) - { - removeSummary(); - return 2; - } - } // DROP THROUGH - case Remote::STOP: - case Remote::MENU: - { - if (playing) stopPlay(); - - return 4; - } - case Remote::PAUSE: - { - player->pause(); - doBar(0); - return 2; - } - case Remote::SKIPFORWARD: - { - doBar(3); - player->skipForward(60); - return 2; - } - case Remote::SKIPBACK: - { - doBar(4); - player->skipBackward(60); - return 2; - } - case Remote::FORWARD: - { - player->fastForward(); - doBar(0); - return 2; - } - case Remote::REVERSE: - { - player->fastBackward(); - doBar(0); - return 2; - } - case Remote::RED: - { - if (vsummary) removeSummary(); - else doSummary(); - return 2; - } - case Remote::GREEN: - { - doAudioSelector(); - return 2; - } - case Remote::YELLOW: - { - doBar(2); - player->skipBackward(10); - return 2; - } - case Remote::BLUE: - { - doBar(1); - player->skipForward(10); - return 2; - } - case Remote::STAR: - { - doBar(2); - player->skipBackward(10); - return 2; - } - case Remote::HASH: - { - doBar(1); - player->skipForward(10); - return 2; - } - case Remote::FULL: - case Remote::TV: - { - toggleChopSides(); - return 2; - } - - case Remote::OK: - { - if (vsummary) - { - removeSummary(); - return 2; - } - - if (barShowing) removeBar(); - else { - doBar(0); - barGenHold=true; - } - return 2; - } - - case Remote::ZERO: player->jumpToPercent(0); doBar(0); return 2; - case Remote::ONE: player->jumpToPercent(10); doBar(0); return 2; - case Remote::TWO: player->jumpToPercent(20); doBar(0); return 2; - case Remote::THREE: player->jumpToPercent(30); doBar(0); return 2; - case Remote::FOUR: player->jumpToPercent(40); doBar(0); return 2; - case Remote::FIVE: player->jumpToPercent(50); doBar(0); return 2; - case Remote::SIX: player->jumpToPercent(60); doBar(0); return 2; - case Remote::SEVEN: player->jumpToPercent(70); doBar(0); return 2; - case Remote::EIGHT: player->jumpToPercent(80); doBar(0); return 2; - case Remote::NINE: player->jumpToPercent(90); doBar(0); return 2; - - - } - - return 1; -} - -void VVideoMedia::processMessage(Message* m) -{ - Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Message received"); - - if (m->message == Message::MOUSE_LBDOWN) - { - UINT x = (m->parameter>>16) - getScreenX(); - UINT y = (m->parameter&0xFFFF) - getScreenY(); - - if (!barShowing) - { - BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press - } - else if (barRegion.x<=x && barRegion.y<=y && (barRegion.x+barRegion.w)>=x && (barRegion.y+barRegion.h)>=y) - { - int progBarXbase = barRegion.x + 300; - if (x>=barRegion.x + progBarXbase + 24 - && x<=barRegion.x + progBarXbase + 4 + 302 - && y>=barRegion.y + 12 - 2 - && y<=barRegion.y + 12 - 2+28) - { - int cx=x-(barRegion.x + progBarXbase + 4); - double percent=((double)cx)/302.*100.; - player->jumpToPercent(percent); - doBar(3); - return; - // int progressWidth = 302 * currentFrameNum / lengthFrames; - // rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 16, progressWidth, 16, Colour::SELECTHIGHLIGHT); - } - } - else - { - BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press - } - } - else if (m->message == Message::PLAYER_EVENT) - { - switch(m->parameter) - { - case PlayerMedia::CONNECTION_LOST: // connection lost detected - { - // I can't handle this, send it to command - Message* m2 = new Message(); - m2->to = Command::getInstance(); - m2->message = Message::CONNECTION_LOST; - Command::getInstance()->postMessageNoLock(m2); - break; - } - case PlayerMedia::STREAM_END: - { - Message* m2 = new Message(); // Must be done after this thread finishes, and must break into master mutex - m2->to = BoxStack::getInstance(); - m2->message = Message::CLOSE_ME; - Command::getInstance()->postMessageNoLock(m2); - break; - } - case PlayerMedia::STATUS_CHANGE: - doBar(0); - break; - case PlayerMedia::ASPECT43: - { - if (dowss) - { - Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Received do WSS 43"); - wss.setWide(false); - wss.draw(); - boxstack->update(this, &wssRegion); - } - break; - } - case PlayerMedia::ASPECT169: - { - if (dowss) - { - Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Received do WSS 169"); - wss.setWide(true); - wss.draw(); - boxstack->update(this, &wssRegion); - } - break; - } - case (PLAYER_TIMER_BASE+1) : - //timer1: - // Remove bar - removeBar(); - break; - case (PLAYER_TIMER_BASE+2) : - //timer2: - // Update clock - if (!barShowing) break; - drawBarClocks(); - BoxStack::getInstance()->update(this,&barRegion); - if (player->getLengthFrames() != 0) timers->setTimerD(this, 2, 0, 200000000); - else timers->setTimerD(this, 2, 1); - break; - } - } - else if (m->message == Message::AUDIO_CHANGE_CHANNEL) - { - Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Received change audio channel to %i", m->parameter); - player->setAudioChannel(m->parameter); - } - else if (m->message == Message::CHILD_CLOSE) - { - if (m->from == vas) - { - vas = NULL; - barVasHold = false; - if (!barGenHold && !barScanHold && !barVasHold) removeBar(); - } - } -} - -void VVideoMedia::stopPlay() -{ - Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Pre stopPlay"); - - removeBar(); - - player->stop(); - - playing = false; - MediaPlayer::getInstance()->closeMediaChannel(MEDIACHANNEL); - - Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Post stopPlay"); -} - -void VVideoMedia::toggleChopSides() -{ - if (video->getTVsize() == Video::ASPECT16X9) return; // Means nothing for 16:9 TVs - - if (videoMode == Video::NORMAL) - { - videoMode = Video::LETTERBOX; - video->setMode(Video::LETTERBOX); - } - else - { - videoMode = Video::NORMAL; - video->setMode(Video::NORMAL); - } -} - -void VVideoMedia::doAudioSelector() -{ - bool* availableMpegAudioChannels = player->getDemuxerMpegAudioChannels(); - bool* availableAc3AudioChannels = 0; - int currentAudioChannel = player->getCurrentAudioChannel(); - if (Audio::getInstance()->supportsAc3()) - { - availableAc3AudioChannels = player->getDemuxerAc3AudioChannels(); - } - - - RecInfo ri; - ri.summary=new char[strlen(myMedia->getDisplayName())+1]; - strcpy(ri.summary,myMedia->getDisplayName()); - vas = new VAudioSelector(this, availableMpegAudioChannels, availableAc3AudioChannels, currentAudioChannel, NULL,NULL,0,0, &ri); - - vas->setBackgroundColour(barBlue); - vas->setPosition(0, barRegion.y - 120); - -// pal 62, ntsc 57 - - barVasHold = true; - doBar(0); - - vas->draw(); - boxstack->add(vas); - boxstack->update(vas); -} - -void VVideoMedia::doBar(int action) -{ - Log::getInstance()->log("VVideoMedia",Log::DEBUG,"doBar %d",action); - barShowing = true; - - rectangle(barRegion, barBlue); - - /* Work out what to display - choices: - - Playing > - Paused || - FFwd >> - FBwd << - - Specials, informed by parameter - - Skip forward 10s >| - Skip backward 10s |< - Skip forward 1m >>| - Skip backward 1m |<< - - */ - - WSymbol w; - TEMPADD(&w); - w.nextSymbol = 0; - w.setPosition(barRegion.x + 66, barRegion.y + 16); - - UCHAR playerState = 0; - - if (action) - { - if (action == 1) w.nextSymbol = WSymbol::SKIPFORWARD; - else if (action == 2) w.nextSymbol = WSymbol::SKIPBACK; - else if (action == 3) w.nextSymbol = WSymbol::SKIPFORWARD2; - else if (action == 4) w.nextSymbol = WSymbol::SKIPBACK2; - } - else - { - playerState = player->getState(); - if (playerState == PlayerMedia::S_PLAY) w.nextSymbol = WSymbol::PLAY; - else if (playerState == PlayerMedia::S_FF) w.nextSymbol = WSymbol::FFWD; - else if (playerState == PlayerMedia::S_BACK) w.nextSymbol = WSymbol::FBWD; - else if (playerState == PlayerMedia::S_SEEK) w.nextSymbol = WSymbol::RIGHTARROW; - else if (playerState == PlayerMedia::S_STOP) w.nextSymbol = WSymbol::PAUSE; - else w.nextSymbol = WSymbol::PAUSE; - } - - w.draw(); - - if ((playerState == PlayerMedia::S_FF) || (playerState == PlayerMedia::S_BACK)) - { - // draw blips to show how fast the scan is - UCHAR scanrate = 2;//player->getIScanRate(); - if (scanrate >= 2) - { - char text[5]; - SNPRINTF(text, 5, "%ux", scanrate); - drawText(text, barRegion.x + 102, barRegion.y + 12, Colour::LIGHTTEXT); - } - } - - drawBarClocks(); - boxstack->update(this, &barRegion); - - timers->cancelTimer(this, 1); - - - if ((playerState == PlayerMedia::S_FF) || (playerState == PlayerMedia::S_BACK)) barScanHold = true; - else barScanHold = false; - - if (!barGenHold && !barScanHold && !barVasHold) timers->setTimerD(this, 1, 4); - - if (player->getLengthFrames() != 0) timers->setTimerD(this, 2, 0, 200000000); - else timers->setTimerD(this, 2, 1); -} - -void VVideoMedia::timercall(int clientReference) -{ - Message *m=new Message(); - m->message=Message::PLAYER_EVENT; - m->to=this; - m->from=this; - m->parameter=PLAYER_TIMER_BASE+clientReference; - Command::getInstance()->postMessageFromOuterSpace(m); -} - -void VVideoMedia::drawBarClocks() -{ - if (barScanHold) - { - UCHAR playerState = player->getState(); - // sticky bar is set if we are in ffwd/fbwd mode - // if player has gone to S_PLAY then kill stickyBar, and run doBar(0) which - // will repaint all the bar (it will call this function again, but - // this section won't run because stickyBarF will then == false) - - if ((playerState != PlayerMedia::S_FF) && (playerState != PlayerMedia::S_BACK)) - { - barScanHold = false; - doBar(0); - return; - } - } - - Log* logger = Log::getInstance(); - logger->log("VVideoMedia", Log::DEBUG, "Draw bar clocks"); - - // Draw RTC - // Blank the area first - rectangle(barRegion.x + 624, barRegion.y + 12, 60, 30, barBlue); - char timeString[20]; - time_t t; - time(&t); - struct tm* tms = localtime(&t); - strftime(timeString, 19, "%H:%M", tms); - drawText(timeString, barRegion.x + 624, barRegion.y + 12, Colour::LIGHTTEXT); - - ULLONG lenPTS=player->getLenPTS(); - // Draw clocks - - rectangle(clocksRegion, barBlue); - - ULLONG currentPTS = player->getCurrentPTS(); - - hmsf currentFrameHMSF = ptsToHMS(currentPTS); - hmsf lengthHMSF = ptsToHMS(lenPTS); - - char buffer[100]; - if (currentPTS > lenPTS && lenPTS != 0) - { - strcpy(buffer, "-:--:-- / -:--:--"); - } - else - { - SNPRINTF(buffer, 99, "%01i:%02i:%02i / %01i:%02i:%02i", currentFrameHMSF.hours, currentFrameHMSF.minutes, currentFrameHMSF.seconds, lengthHMSF.hours, lengthHMSF.minutes, lengthHMSF.seconds); - } - logger->log("VVideoMedia", Log::DEBUG, "cur %llu,len %llu, txt %s",currentPTS,lenPTS,buffer); - - drawText(buffer, clocksRegion.x, clocksRegion.y, Colour::LIGHTTEXT); - - - - - - - - // Draw progress bar - int progBarXbase = barRegion.x + 300; - - if (lenPTS == 0) return; - rectangle(barRegion.x + progBarXbase, barRegion.y + 12, 310, 24, Colour::LIGHTTEXT); - rectangle(barRegion.x + progBarXbase + 2, barRegion.y + 14, 306, 20, barBlue); - - if (currentPTS > lenPTS) return; - - // Draw yellow portion - int progressWidth = 302 * currentPTS / lenPTS; - rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 16, progressWidth, 16, Colour::SELECTHIGHLIGHT); - -} - -void VVideoMedia::removeBar() -{ - if (!barShowing) return; - timers->cancelTimer(this, 2); - barShowing = false; - barGenHold = false; - barScanHold = false; - barVasHold = false; - rectangle(barRegion, transparent); - BoxStack::getInstance()->update(this, &barRegion); -} - -void VVideoMedia::doSummary() -{ - vsummary = new VInfo(); - vsummary->setTitleText(myMedia->getDisplayName()); - vsummary->setBorderOn(1); - vsummary->setExitable(); - const MediaURI *u=myMedia->getURI(); - int stlen=0; - if (u) { - stlen+=strlen(myMedia->getFileName()); - stlen+=strlen(tr("FileName"))+10; - } - stlen+=strlen(tr("Size"))+50; - stlen+=strlen(tr("Directory"))+500; - stlen+=strlen(tr("Time"))+50; - char *pinfo=player->getInfo(); - stlen+=strlen(pinfo)+10; - char *buf=new char [stlen]; - char *tsbuf=new char [stlen]; - char *tsbuf2=new char [stlen]; - char *tbuf=new char[Media::TIMEBUFLEN]; - SNPRINTF(buf,stlen,"%s\n" - "%s: %llu Bytes\n" - "%s\n" - "%s: %s\n" - "%s", - shortendedText(tr("FileName"),": ",myMedia->getFileName(),tsbuf,vsummary->getWidth()), - tr("Size"), - lengthBytes, - shortendedText(tr("Directory"),": ",lparent->getDirname(MEDIA_TYPE_VIDEO),tsbuf2,vsummary->getWidth()), - tr("Time"), - myMedia->getTimeString(tbuf), - pinfo - ); - //TODO more info - if (u) { - Log::getInstance()->log("VVideoMedia",Log::DEBUG,"info %s",buf); - vsummary->setMainText(buf); - } - else vsummary->setMainText(tr("Info unavailable")); - delete pinfo; - if (Video::getInstance()->getFormat() == Video::PAL) - { - vsummary->setPosition(70, 100); - } - else - { - vsummary->setPosition(40, 70); - } - vsummary->setSize(580, 350); - add(vsummary); - vsummary->draw(); - - BoxStack::getInstance()->update(this); - delete [] buf; - delete [] tsbuf; - delete [] tsbuf2; - delete [] tbuf; -} - -void VVideoMedia::removeSummary() -{ - if (vsummary) - { - remove(vsummary); - delete vsummary; - vsummary = NULL; - draw(); - BoxStack::getInstance()->update(this); - } -} - - -hmsf VVideoMedia::ptsToHMS(ULLONG pts) { - ULLONG secs=pts/90000; - hmsf rt; - rt.frames=0; - rt.seconds=secs%60; - secs=secs/60; - rt.minutes=secs%60; - secs=secs/60; - rt.hours=secs; - return rt; -} - -char * VVideoMedia::shortendedText(const char * title, const char * title2,const char * intext,char *buffer,UINT width) { - if (! intext) { - intext=""; - } - UINT twidth=0; - for (const char *p=title;*p!=0;p++) twidth+=charWidth(*p); - for (const char *p=title2;*p!=0;p++) twidth+=charWidth(*p); - const char *prfx="..."; - UINT prfwidth=3*charWidth('.'); - const char *istart=intext+strlen(intext); - UINT iwidth=0; - while (twidth+iwidth+prfwidth < width-2*paraMargin && istart> intext) { - istart--; - iwidth+=charWidth(*istart); - } - if (twidth+iwidth+prfwidth >= width-2*paraMargin && istart < intext+strlen(intext)) istart++; - if (istart == intext) prfx=""; - sprintf(buffer,"%s%s%s%s",title,title2,prfx,intext); - return buffer; -} - - +/* + 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. +*/ + +#include "vvideomedia.h" +#include "vmedialist.h" +#include "media.h" +#include "mediaplayer.h" + +#include "command.h" +#include "osd.h" +#include "wsymbol.h" +#include "audio.h" +#include "video.h" +#include "timers.h" +#include "playermedia.h" +#include "recording.h" +#include "vaudioselector.h" +#include "message.h" +#include "remote.h" +#include "boxstack.h" +#include "vinfo.h" +#include "i18n.h" +#include "log.h" +#include "recinfo.h" + +//use the picture channel +#define MEDIACHANNEL 1 + +//we misuse PLAYER_EVENTS for timer messages +//this should be larger then any player message +#define PLAYER_TIMER_BASE 100 + +VVideoMedia::VVideoMedia(Media* media, VMediaList *p) +{ + lparent=p; + boxstack = BoxStack::getInstance(); + video = Video::getInstance(); + timers = Timers::getInstance(); + vas = NULL; + vsummary = NULL; + lengthBytes=0; + myMedia = new Media(media); + + player = new PlayerMedia(this); + player->run(); + + videoMode = video->getMode(); + + playing = false; + + + setSize(video->getScreenWidth(), video->getScreenHeight()); + createBuffer(); + transparent.set(0, 0, 0, 0); + setBackgroundColour(transparent); + + barRegion.x = 0; + barRegion.y = video->getScreenHeight() - 58; // FIXME, need to be - 1? and below? + barRegion.w = video->getScreenWidth(); + barRegion.h = 58; + + clocksRegion.x = barRegion.x + 140; + clocksRegion.y = barRegion.y + 12; + clocksRegion.w = 170; + clocksRegion.h = getFontHeight(); + + + barBlue.set(0, 0, 150, 150); + + barShowing = false; + barGenHold = false; + barScanHold = false; + barVasHold = false; + + dowss = false; + char* optionWSS = VDR::getInstance()->configLoad("General", "WSS"); + if (optionWSS) + { + if (strstr(optionWSS, "Yes")) dowss = true; + delete[] optionWSS; + } + Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Do WSS: %u", dowss); + + if (dowss) + { + wss.setFormat(video->getFormat()); + wss.setWide(true); + add(&wss); + + wssRegion.x = 0; + wssRegion.y = 0; + wssRegion.w = video->getScreenWidth(); + wssRegion.h = 300; + } +} + +VVideoMedia::~VVideoMedia() +{ + Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Entering destructor"); + + if (vas) + { + boxstack->remove(vas); + vas = NULL; + } + + if (vsummary) { + remove(vsummary); + delete vsummary; + } + + if (playing) stopPlay(); + video->setDefaultAspect(); + + timers->cancelTimer(this, 1); + timers->cancelTimer(this, 2); + + delete myMedia; + Log::getInstance()->log("VVideoMedia", Log::DEBUG, "shutting down player"); + player->shutdown(); + delete player; + Log::getInstance()->log("VVideoMedia", Log::DEBUG, "deleted"); +} + +void VVideoMedia::go(bool resume) +{ + ULONG startFrameNum=0; + + Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Starting stream: %s at frame: %lu", myMedia->getFileName(), startFrameNum); + + lengthBytes = 0; + + int rt=0; + const MediaURI *u=myMedia->getURI(); + if (!u) { + Log::getInstance()->log("VVideoMedia", Log::ERR, "stream: %s has no URI", myMedia->getFileName()); + rt=-1; + } + else { + rt=MediaPlayer::getInstance()->openMedium(MEDIACHANNEL,u,&lengthBytes,area.w,area.h); + } + if (rt==0) + { + //TODO: figure out len in frames + int seq=player->playNew(MEDIACHANNEL,lengthBytes,0); + int ok=player->waitForSequence(2,seq); + if (ok < 0) rt=-1; + else { + playing = true; + doBar(0); + } + } + if (rt != 0) + { + stopPlay(); // clean up + + Message* m = new Message(); + m->message = Message::CLOSE_ME; + m->from = this; + m->to = boxstack; + Command::getInstance()->postMessageNoLock(m); + + VInfo* vi = new VInfo(); + vi->setSize(400, 150); + vi->createBuffer(); + if (video->getFormat() == Video::PAL) + vi->setPosition(170, 200); + else + vi->setPosition(160, 150); + vi->setExitable(); + vi->setBorderOn(1); + vi->setTitleBarOn(0); + vi->setOneLiner(tr("Error playing media")); + vi->draw(); + + m = new Message(); + m->message = Message::ADD_VIEW; + m->to = boxstack; + m->parameter = (ULONG)vi; + Command::getInstance()->postMessageNoLock(m); + } +} + +int VVideoMedia::handleCommand(int command) +{ + switch(command) + { + case Remote::PLAY: + { + player->play(); + doBar(0); + return 2; + } + + case Remote::BACK: + { + if (vsummary) + { + removeSummary(); + return 2; + } + } // DROP THROUGH + case Remote::STOP: + case Remote::MENU: + { + if (playing) stopPlay(); + + return 4; + } + case Remote::PAUSE: + { + player->pause(); + doBar(0); + return 2; + } + case Remote::SKIPFORWARD: + { + doBar(3); + player->skipForward(60); + return 2; + } + case Remote::SKIPBACK: + { + doBar(4); + player->skipBackward(60); + return 2; + } + case Remote::FORWARD: + { + player->fastForward(); + doBar(0); + return 2; + } + case Remote::REVERSE: + { + player->fastBackward(); + doBar(0); + return 2; + } + case Remote::RED: + { + if (vsummary) removeSummary(); + else doSummary(); + return 2; + } + case Remote::GREEN: + { + doAudioSelector(); + return 2; + } + case Remote::YELLOW: + { + doBar(2); + player->skipBackward(10); + return 2; + } + case Remote::BLUE: + { + doBar(1); + player->skipForward(10); + return 2; + } + case Remote::STAR: + { + doBar(2); + player->skipBackward(10); + return 2; + } + case Remote::HASH: + { + doBar(1); + player->skipForward(10); + return 2; + } + case Remote::FULL: + case Remote::TV: + { + toggleChopSides(); + return 2; + } + + case Remote::OK: + { + if (vsummary) + { + removeSummary(); + return 2; + } + + if (barShowing) removeBar(); + else { + doBar(0); + barGenHold=true; + } + return 2; + } + + case Remote::ZERO: player->jumpToPercent(0); doBar(0); return 2; + case Remote::ONE: player->jumpToPercent(10); doBar(0); return 2; + case Remote::TWO: player->jumpToPercent(20); doBar(0); return 2; + case Remote::THREE: player->jumpToPercent(30); doBar(0); return 2; + case Remote::FOUR: player->jumpToPercent(40); doBar(0); return 2; + case Remote::FIVE: player->jumpToPercent(50); doBar(0); return 2; + case Remote::SIX: player->jumpToPercent(60); doBar(0); return 2; + case Remote::SEVEN: player->jumpToPercent(70); doBar(0); return 2; + case Remote::EIGHT: player->jumpToPercent(80); doBar(0); return 2; + case Remote::NINE: player->jumpToPercent(90); doBar(0); return 2; + + + } + + return 1; +} + +void VVideoMedia::processMessage(Message* m) +{ + Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Message received"); + + if (m->message == Message::MOUSE_LBDOWN) + { + UINT x = (m->parameter>>16) - getScreenX(); + UINT y = (m->parameter&0xFFFF) - getScreenY(); + + if (!barShowing) + { + BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press + } + else if (barRegion.x<=x && barRegion.y<=y && (barRegion.x+barRegion.w)>=x && (barRegion.y+barRegion.h)>=y) + { + int progBarXbase = barRegion.x + 300; + if (x>=barRegion.x + progBarXbase + 24 + && x<=barRegion.x + progBarXbase + 4 + 302 + && y>=barRegion.y + 12 - 2 + && y<=barRegion.y + 12 - 2+28) + { + int cx=x-(barRegion.x + progBarXbase + 4); + double percent=((double)cx)/302.*100.; + player->jumpToPercent(percent); + doBar(3); + return; + // int progressWidth = 302 * currentFrameNum / lengthFrames; + // rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 16, progressWidth, 16, Colour::SELECTHIGHLIGHT); + } + } + else + { + BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press + } + } + else if (m->message == Message::PLAYER_EVENT) + { + switch(m->parameter) + { + case PlayerMedia::CONNECTION_LOST: // connection lost detected + { + // I can't handle this, send it to command + Message* m2 = new Message(); + m2->to = Command::getInstance(); + m2->message = Message::CONNECTION_LOST; + Command::getInstance()->postMessageNoLock(m2); + break; + } + case PlayerMedia::STREAM_END: + { + Message* m2 = new Message(); // Must be done after this thread finishes, and must break into master mutex + m2->to = BoxStack::getInstance(); + m2->message = Message::CLOSE_ME; + Command::getInstance()->postMessageNoLock(m2); + break; + } + case PlayerMedia::STATUS_CHANGE: + doBar(0); + break; + case PlayerMedia::ASPECT43: + { + if (dowss) + { + Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Received do WSS 43"); + wss.setWide(false); + wss.draw(); + boxstack->update(this, &wssRegion); + } + break; + } + case PlayerMedia::ASPECT169: + { + if (dowss) + { + Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Received do WSS 169"); + wss.setWide(true); + wss.draw(); + boxstack->update(this, &wssRegion); + } + break; + } + case (PLAYER_TIMER_BASE+1) : + //timer1: + // Remove bar + removeBar(); + break; + case (PLAYER_TIMER_BASE+2) : + //timer2: + // Update clock + if (!barShowing) break; + drawBarClocks(); + BoxStack::getInstance()->update(this,&barRegion); + if (player->getLengthFrames() != 0) timers->setTimerD(this, 2, 0, 200000000); + else timers->setTimerD(this, 2, 1); + break; + } + } + else if (m->message == Message::AUDIO_CHANGE_CHANNEL) + { + Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Received change audio channel to %i", m->parameter); + player->setAudioChannel(m->parameter); + } + else if (m->message == Message::CHILD_CLOSE) + { + if (m->from == vas) + { + vas = NULL; + barVasHold = false; + if (!barGenHold && !barScanHold && !barVasHold) removeBar(); + } + } +} + +void VVideoMedia::stopPlay() +{ + Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Pre stopPlay"); + + removeBar(); + + player->stop(); + + playing = false; + MediaPlayer::getInstance()->closeMediaChannel(MEDIACHANNEL); + + Log::getInstance()->log("VVideoMedia", Log::DEBUG, "Post stopPlay"); +} + +void VVideoMedia::toggleChopSides() +{ + if (video->getTVsize() == Video::ASPECT16X9) return; // Means nothing for 16:9 TVs + + if (videoMode == Video::NORMAL) + { + videoMode = Video::LETTERBOX; + video->setMode(Video::LETTERBOX); + } + else + { + videoMode = Video::NORMAL; + video->setMode(Video::NORMAL); + } +} + +void VVideoMedia::doAudioSelector() +{ + bool* availableMpegAudioChannels = player->getDemuxerMpegAudioChannels(); + bool* availableAc3AudioChannels = 0; + int currentAudioChannel = player->getCurrentAudioChannel(); + if (Audio::getInstance()->supportsAc3()) + { + availableAc3AudioChannels = player->getDemuxerAc3AudioChannels(); + } + + + RecInfo ri; + ri.summary=new char[strlen(myMedia->getDisplayName())+1]; + strcpy(ri.summary,myMedia->getDisplayName()); + vas = new VAudioSelector(this, availableMpegAudioChannels, availableAc3AudioChannels, currentAudioChannel, NULL,NULL,0,0, &ri); + + vas->setBackgroundColour(barBlue); + vas->setPosition(0, barRegion.y - 120); + +// pal 62, ntsc 57 + + barVasHold = true; + doBar(0); + + vas->draw(); + boxstack->add(vas); + boxstack->update(vas); +} + +void VVideoMedia::doBar(int action) +{ + Log::getInstance()->log("VVideoMedia",Log::DEBUG,"doBar %d",action); + barShowing = true; + + rectangle(barRegion, barBlue); + + /* Work out what to display - choices: + + Playing > + Paused || + FFwd >> + FBwd << + + Specials, informed by parameter + + Skip forward 10s >| + Skip backward 10s |< + Skip forward 1m >>| + Skip backward 1m |<< + + */ + + WSymbol w; + TEMPADD(&w); + w.nextSymbol = 0; + w.setPosition(barRegion.x + 66, barRegion.y + 16); + + UCHAR playerState = 0; + + if (action) + { + if (action == 1) w.nextSymbol = WSymbol::SKIPFORWARD; + else if (action == 2) w.nextSymbol = WSymbol::SKIPBACK; + else if (action == 3) w.nextSymbol = WSymbol::SKIPFORWARD2; + else if (action == 4) w.nextSymbol = WSymbol::SKIPBACK2; + } + else + { + playerState = player->getState(); + if (playerState == PlayerMedia::S_PLAY) w.nextSymbol = WSymbol::PLAY; + else if (playerState == PlayerMedia::S_FF) w.nextSymbol = WSymbol::FFWD; + else if (playerState == PlayerMedia::S_BACK) w.nextSymbol = WSymbol::FBWD; + else if (playerState == PlayerMedia::S_SEEK) w.nextSymbol = WSymbol::RIGHTARROW; + else if (playerState == PlayerMedia::S_STOP) w.nextSymbol = WSymbol::PAUSE; + else w.nextSymbol = WSymbol::PAUSE; + } + + w.draw(); + + if ((playerState == PlayerMedia::S_FF) || (playerState == PlayerMedia::S_BACK)) + { + // draw blips to show how fast the scan is + UCHAR scanrate = 2;//player->getIScanRate(); + if (scanrate >= 2) + { + char text[5]; + SNPRINTF(text, 5, "%ux", scanrate); + drawText(text, barRegion.x + 102, barRegion.y + 12, Colour::LIGHTTEXT); + } + } + + drawBarClocks(); + boxstack->update(this, &barRegion); + + timers->cancelTimer(this, 1); + + + if ((playerState == PlayerMedia::S_FF) || (playerState == PlayerMedia::S_BACK)) barScanHold = true; + else barScanHold = false; + + if (!barGenHold && !barScanHold && !barVasHold) timers->setTimerD(this, 1, 4); + + if (player->getLengthFrames() != 0) timers->setTimerD(this, 2, 0, 200000000); + else timers->setTimerD(this, 2, 1); +} + +void VVideoMedia::timercall(int clientReference) +{ + Message *m=new Message(); + m->message=Message::PLAYER_EVENT; + m->to=this; + m->from=this; + m->parameter=PLAYER_TIMER_BASE+clientReference; + Command::getInstance()->postMessageFromOuterSpace(m); +} + +void VVideoMedia::drawBarClocks() +{ + if (barScanHold) + { + UCHAR playerState = player->getState(); + // sticky bar is set if we are in ffwd/fbwd mode + // if player has gone to S_PLAY then kill stickyBar, and run doBar(0) which + // will repaint all the bar (it will call this function again, but + // this section won't run because stickyBarF will then == false) + + if ((playerState != PlayerMedia::S_FF) && (playerState != PlayerMedia::S_BACK)) + { + barScanHold = false; + doBar(0); + return; + } + } + + Log* logger = Log::getInstance(); + logger->log("VVideoMedia", Log::DEBUG, "Draw bar clocks"); + + // Draw RTC + // Blank the area first + rectangle(barRegion.x + 624, barRegion.y + 12, 60, 30, barBlue); + char timeString[20]; + time_t t; + time(&t); + struct tm* tms = localtime(&t); + strftime(timeString, 19, "%H:%M", tms); + drawText(timeString, barRegion.x + 624, barRegion.y + 12, Colour::LIGHTTEXT); + + ULLONG lenPTS=player->getLenPTS(); + // Draw clocks + + rectangle(clocksRegion, barBlue); + + ULLONG currentPTS = player->getCurrentPTS(); + + hmsf currentFrameHMSF = ptsToHMS(currentPTS); + hmsf lengthHMSF = ptsToHMS(lenPTS); + + char buffer[100]; + if (currentPTS > lenPTS && lenPTS != 0) + { + strcpy(buffer, "-:--:-- / -:--:--"); + } + else + { + SNPRINTF(buffer, 99, "%01i:%02i:%02i / %01i:%02i:%02i", currentFrameHMSF.hours, currentFrameHMSF.minutes, currentFrameHMSF.seconds, lengthHMSF.hours, lengthHMSF.minutes, lengthHMSF.seconds); + } + logger->log("VVideoMedia", Log::DEBUG, "cur %llu,len %llu, txt %s",currentPTS,lenPTS,buffer); + + drawText(buffer, clocksRegion.x, clocksRegion.y, Colour::LIGHTTEXT); + + + + + + + + // Draw progress bar + int progBarXbase = barRegion.x + 300; + + if (lenPTS == 0) return; + rectangle(barRegion.x + progBarXbase, barRegion.y + 12, 310, 24, Colour::LIGHTTEXT); + rectangle(barRegion.x + progBarXbase + 2, barRegion.y + 14, 306, 20, barBlue); + + if (currentPTS > lenPTS) return; + + // Draw yellow portion + int progressWidth = 302 * currentPTS / lenPTS; + rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 16, progressWidth, 16, Colour::SELECTHIGHLIGHT); + +} + +void VVideoMedia::removeBar() +{ + if (!barShowing) return; + timers->cancelTimer(this, 2); + barShowing = false; + barGenHold = false; + barScanHold = false; + barVasHold = false; + rectangle(barRegion, transparent); + BoxStack::getInstance()->update(this, &barRegion); +} + +void VVideoMedia::doSummary() +{ + vsummary = new VInfo(); + vsummary->setTitleText(myMedia->getDisplayName()); + vsummary->setBorderOn(1); + vsummary->setExitable(); + const MediaURI *u=myMedia->getURI(); + int stlen=0; + if (u) { + stlen+=strlen(myMedia->getFileName()); + stlen+=strlen(tr("FileName"))+10; + } + stlen+=strlen(tr("Size"))+50; + stlen+=strlen(tr("Directory"))+500; + stlen+=strlen(tr("Time"))+50; + char *pinfo=player->getInfo(); + stlen+=strlen(pinfo)+10; + char *buf=new char [stlen]; + char *tsbuf=new char [stlen]; + char *tsbuf2=new char [stlen]; + char *tbuf=new char[Media::TIMEBUFLEN]; + SNPRINTF(buf,stlen,"%s\n" + "%s: %llu Bytes\n" + "%s\n" + "%s: %s\n" + "%s", + shortendedText(tr("FileName"),": ",myMedia->getFileName(),tsbuf,vsummary->getWidth()), + tr("Size"), + lengthBytes, + shortendedText(tr("Directory"),": ",lparent->getDirname(MEDIA_TYPE_VIDEO),tsbuf2,vsummary->getWidth()), + tr("Time"), + myMedia->getTimeString(tbuf), + pinfo + ); + //TODO more info + if (u) { + Log::getInstance()->log("VVideoMedia",Log::DEBUG,"info %s",buf); + vsummary->setMainText(buf); + } + else vsummary->setMainText(tr("Info unavailable")); + delete pinfo; + if (Video::getInstance()->getFormat() == Video::PAL) + { + vsummary->setPosition(70, 100); + } + else + { + vsummary->setPosition(40, 70); + } + vsummary->setSize(580, 350); + add(vsummary); + vsummary->draw(); + + BoxStack::getInstance()->update(this); + delete [] buf; + delete [] tsbuf; + delete [] tsbuf2; + delete [] tbuf; +} + +void VVideoMedia::removeSummary() +{ + if (vsummary) + { + remove(vsummary); + delete vsummary; + vsummary = NULL; + draw(); + BoxStack::getInstance()->update(this); + } +} + + +hmsf VVideoMedia::ptsToHMS(ULLONG pts) { + ULLONG secs=pts/90000; + hmsf rt; + rt.frames=0; + rt.seconds=secs%60; + secs=secs/60; + rt.minutes=secs%60; + secs=secs/60; + rt.hours=secs; + return rt; +} + +char * VVideoMedia::shortendedText(const char * title, const char * title2,const char * intext,char *buffer,UINT width) { + if (! intext) { + intext=""; + } + UINT twidth=0; + for (const char *p=title;*p!=0;p++) twidth+=charWidth(*p); + for (const char *p=title2;*p!=0;p++) twidth+=charWidth(*p); + const char *prfx="..."; + UINT prfwidth=3*charWidth('.'); + const char *istart=intext+strlen(intext); + UINT iwidth=0; + while (twidth+iwidth+prfwidth < width-2*paraMargin && istart> intext) { + istart--; + iwidth+=charWidth(*istart); + } + if (twidth+iwidth+prfwidth >= width-2*paraMargin && istart < intext+strlen(intext)) istart++; + if (istart == intext) prfx=""; + sprintf(buffer,"%s%s%s%s",title,title2,prfx,intext); + return buffer; +} + + diff --git a/vvideorec.cc b/vvideorec.cc index c84c9a6..e651dd0 100644 --- a/vvideorec.cc +++ b/vvideorec.cc @@ -1,1086 +1,1093 @@ -/* - 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. -*/ - -#include - -#include "vvideorec.h" -#include "vteletextview.h" - -#include "command.h" -#include "osd.h" -#include "wsymbol.h" -#include "audio.h" -#include "vdr.h" -#include "video.h" -#include "timers.h" -#include "player.h" -#include "recording.h" -#include "vaudioselector.h" -#include "message.h" -#include "remote.h" -#include "boxstack.h" -#include "vinfo.h" -#include "i18n.h" -#include "bitmap.h" -#include "recinfo.h" -#include "log.h" -#include "channel.h" - -VVideoRec::VVideoRec(Recording* rec, bool ish264) -{ - boxstack = BoxStack::getInstance(); - vdr = VDR::getInstance(); - video = Video::getInstance(); - timers = Timers::getInstance(); - vas = NULL; - vsummary = NULL; - - videoMode = video->getMode(); - myRec = rec; - - video->seth264mode(ish264); - - player = new Player(Command::getInstance(), this, this); - player->init(myRec->IsPesRecording,myRec->recInfo->fps); - - playing = false; - - startMargin = 0; - endMargin = 0; - char* cstartMargin = vdr->configLoad("Timers", "Start margin"); - char* cendMargin = vdr->configLoad("Timers", "End margin"); - if (!cstartMargin) - { - startMargin = 300; // 5 mins default - } - else - { - startMargin = atoi(cstartMargin) * 60; - delete[] cstartMargin; - } - - if (!cendMargin) - { - endMargin = 300; // 5 mins default - } - else - { - endMargin = atoi(cendMargin) * 60; - delete[] cendMargin; - } - - Log::getInstance()->log("VVideoRec", Log::DEBUG, "SM: %u EM: %u", startMargin, endMargin); - - setSize(video->getScreenWidth(), video->getScreenHeight()); - createBuffer(); - transparent.set(0, 0, 0, 0); - setBackgroundColour(transparent); - - barRegion.x = 0; - barRegion.y = video->getScreenHeight() - 58; // FIXME, need to be - 1? and below? - barRegion.w = video->getScreenWidth(); - barRegion.h = 58; - - clocksRegion.x = barRegion.x + 140; - clocksRegion.y = barRegion.y + 12; - clocksRegion.w = 170; - clocksRegion.h = surface->getFontHeight(); -// barBlue.set(0, 0, 150, 150); - barBlue.set(0, 0, 0, 128); - - barShowing = false; - barGenHold = false; - barScanHold = false; - barVasHold = false; - - dowss = false; - char* optionWSS = vdr->configLoad("General", "WSS"); - if (optionWSS) - { - if (strstr(optionWSS, "Yes")) dowss = true; - delete[] optionWSS; - } - Log::getInstance()->log("VVideoRec", Log::DEBUG, "Do WSS: %u", dowss); - - if (dowss) - { - wss.setFormat(video->getFormat()); - wss.setWide(true); - add(&wss); - - wssRegion.x = 0; - wssRegion.y = 0; - wssRegion.w = video->getScreenWidth(); - wssRegion.h = 300; - } -} - -void VVideoRec::preDelete() -{ - timers->cancelTimer(this, 1); - timers->cancelTimer(this, 2); - - if (vas) - { - boxstack->remove(vas); - vas = NULL; - } - - if (vsummary) delete vsummary; - - if (playing) stopPlay(); -} - -VVideoRec::~VVideoRec() -{ - Log::getInstance()->log("VVideoRec", Log::DEBUG, "Entering vvideorec destructor"); - - video->setDefaultAspect(); - - // kill recInfo in case resumePoint has changed (likely) - myRec->dropRecInfo(); - // FIXME - do this properly - save the resume point back to the server manually and update - // rec->recInfo->resumePoint - this will fix the ~10s offset problem as well -} - -void VVideoRec::go(bool resume) -{ - ULONG startFrameNum; - if (resume) - startFrameNum = myRec->recInfo->resumePoint; - else - startFrameNum = 0; - - Log::getInstance()->log("VVideoRec", Log::DEBUG, "Starting stream: %s at frame: %lu", myRec->getFileName(), startFrameNum); - ULONG lengthFrames = 0; - bool isPesRecording; - ULLONG lengthBytes = vdr->streamRecording(myRec->getFileName(), &lengthFrames, &isPesRecording); - myRec->IsPesRecording = isPesRecording; - if (lengthBytes) - { - player->setLengthBytes(lengthBytes); - player->setLengthFrames(lengthFrames); - player->setStartFrame(startFrameNum); - player->play(); - playing = true; - doBar(0); - } - else - { - stopPlay(); // clean up - - if (!vdr->isConnected()) - { - Command::getInstance()->connectionLost(); - return; - } - - Message* m = new Message(); - m->message = Message::CLOSE_ME; - m->from = this; - m->to = boxstack; - Command::getInstance()->postMessageNoLock(m); - - VInfo* vi = new VInfo(); - vi->setSize(400, 150); - vi->createBuffer(); - if (video->getFormat() == Video::PAL) - vi->setPosition(170, 200); - else - vi->setPosition(160, 150); - vi->setExitable(); - vi->setBorderOn(1); - vi->setTitleBarOn(0); - vi->setOneLiner(tr("Error playing recording")); - vi->draw(); - - m = new Message(); - m->message = Message::ADD_VIEW; - m->to = boxstack; - m->parameter = (ULONG)vi; - Command::getInstance()->postMessageNoLock(m); - } -} - -int VVideoRec::handleCommand(int command) -{ - switch(command) - { - case Remote::PLAY: - { - player->play(); - doBar(0); - return 2; - } - - case Remote::BACK: - { - if (vsummary) - { - removeSummary(); - return 2; - } - } // DROP THROUGH - case Remote::STOP: - case Remote::MENU: - { - if (playing) stopPlay(); - - return 4; - } - case Remote::PAUSE: - { - player->pause(); - doBar(0); - return 2; - } - case Remote::SKIPFORWARD: - { - doBar(3); - player->skipForward(60); - return 2; - } - case Remote::SKIPBACK: - { - doBar(4); - player->skipBackward(60); - return 2; - } - case Remote::FORWARD: - { - player->fastForward(); - doBar(0); - return 2; - } - case Remote::REVERSE: - { - player->fastBackward(); - doBar(0); - return 2; - } - case Remote::RED: - { - if (vsummary) removeSummary(); - else doSummary(); - return 2; - } - case Remote::GREEN: - { - doAudioSelector(); - return 2; - } - case Remote::YELLOW: - { - if (myRec->hasMarks()) - { - // skip to previous mark - Log* logger = Log::getInstance(); - int currentFrame = (player->getCurrentFrameNum()); // get current Frame - currentFrame -= 5. * myRec->recInfo->fps; // subtrack 5 seconds, else you cannot skip more than once back .. - - int prevMark = myRec->getPrevMark(currentFrame); // find previous Frame - if (prevMark) - { - logger->log("VVideoRec", Log::NOTICE, "jump back from pos %i to mark at %i",currentFrame,prevMark); - player->jumpToMark(prevMark); - } - doBar(4); - } - else - { - doBar(2); - player->skipBackward(10); - } - return 2; - } - case Remote::BLUE: - { - if (myRec->hasMarks()) - { - // skip to next mark - Log* logger = Log::getInstance(); - int currentFrame = (player->getCurrentFrameNum()); - - int nextMark = myRec->getNextMark(currentFrame); - - if (nextMark) - { - logger->log("VVideoRec", Log::NOTICE, "jump forward from pos %i to mark at %i",currentFrame,nextMark); - player->jumpToMark(nextMark); - } - doBar(3); - } - else - { - doBar(1); - player->skipForward(10); - } - return 2; - } - case Remote::STAR: - { - doBar(2); - player->skipBackward(10); - return 2; - } - case Remote::HASH: - { - doBar(1); - player->skipForward(10); - return 2; - } - case Remote::FULL: - case Remote::TV: - { - toggleChopSides(); - return 2; - } - - case Remote::OK: - { - if (vsummary) - { - removeSummary(); - return 2; - } - - if (barShowing) removeBar(); - else doBar(0); - return 2; - } - - case Remote::ZERO: player->jumpToPercent(0); doBar(0); return 2; - case Remote::ONE: player->jumpToPercent(10); doBar(0); return 2; - case Remote::TWO: player->jumpToPercent(20); doBar(0); return 2; - case Remote::THREE: player->jumpToPercent(30); doBar(0); return 2; - case Remote::FOUR: player->jumpToPercent(40); doBar(0); return 2; - case Remote::FIVE: player->jumpToPercent(50); doBar(0); return 2; - case Remote::SIX: player->jumpToPercent(60); doBar(0); return 2; - case Remote::SEVEN: player->jumpToPercent(70); doBar(0); return 2; - case Remote::EIGHT: player->jumpToPercent(80); doBar(0); return 2; - case Remote::NINE: player->jumpToPercent(90); doBar(0); return 2; - - case Remote::RECORD: player->toggleSubtitles(); return 2; -#ifdef DEV -// case Remote::RED: -// { - //Don't use RED for anything. It will eventually be recording summary - - //player->test1(); - - - /* - // for testing EPG in NTSC with a NTSC test video - Video::getInstance()->setMode(Video::QUARTER); - Video::getInstance()->setPosition(170, 5); - VEpg* vepg = new VEpg(NULL, 0); - vepg->draw(); - BoxStack::getInstance()->add(vepg); - BoxStack::getInstance()->update(vepg); - */ - -// return 2; -// } - -#endif - - } - - return 1; -} - -void VVideoRec::doTeletext() -{ - - bool exists=true; - - - // Draw the teletxt - VTeletextView *vtxv=player->getTeletextDecoder()->getTeletxtView(); - if (vtxv==NULL) { - vtxv= new VTeletextView((player)->getTeletextDecoder(),this); - (player)->getTeletextDecoder()->registerTeletextView(vtxv); - exists=false; - } - vtxv->setSubtitleMode(true); - vtxv->draw(); - draw(); - - if (!exists) { - BoxStack::getInstance()->add(vtxv); - } - BoxStack::getInstance()->update(this); - BoxStack::getInstance()->update(vtxv); -} - -void VVideoRec::processMessage(Message* m) -{ - Log::getInstance()->log("VVideoRec", Log::DEBUG, "Message received"); - - if (m->message == Message::MOUSE_LBDOWN) - { - UINT x = (m->parameter>>16) - getScreenX(); - UINT y = (m->parameter&0xFFFF) - getScreenY(); - - if (!barShowing) - { - BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press - } - else if (barRegion.x<=x && barRegion.y<=y && (barRegion.x+barRegion.w)>=x && (barRegion.y+barRegion.h)>=y) - { - int progBarXbase = barRegion.x + 300; - if (myRec->hasMarks()) - { - MarkList* markList = myRec->getMarkList(); - MarkList::iterator i; - Mark* loopMark = NULL; - int posPix; - ULONG lengthFrames; - if (myRec->recInfo->timerEnd > time(NULL)) - { - // chasing playback - // Work out an approximate length in frames (good to 1s...) - lengthFrames = (ULONG)((double)(myRec->recInfo->timerEnd - myRec->recInfo->timerStart) * myRec->recInfo->fps); - } - else - { - lengthFrames = player->getLengthFrames(); - } - for(i = markList->begin(); i != markList->end(); i++) - { - loopMark = *i; - if (loopMark->pos) - { - posPix = 302 * loopMark->pos / lengthFrames; - rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 - 2, 3, 28, Colour::DANGER); - if (x>=barRegion.x + progBarXbase + 2 + posPix - && x<=barRegion.x + progBarXbase + 2 + posPix+3 - && y>=barRegion.y + 12 - 2 - && y<=barRegion.y + 12 - 2+28) - { - player->jumpToMark(loopMark->pos); - doBar(3); - return; - } - } - } - } - - if (x>=barRegion.x + progBarXbase + 24 - && x<=barRegion.x + progBarXbase + 4 + 302 - && y>=barRegion.y + 12 - 2 - && y<=barRegion.y + 12 - 2+28) - { - int cx=x-(barRegion.x + progBarXbase + 4); - double percent=((double)cx)/302.*100.; - player->jumpToPercent(percent); - doBar(3); - return; - // int progressWidth = 302 * currentFrameNum / lengthFrames; - // rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 16, progressWidth, 16, Colour::SELECTHIGHLIGHT); - } - } - else - { - BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press - } - } - else if (m->from == player) - { - if (m->message != Message::PLAYER_EVENT) return; - switch(m->parameter) - { - case Player::CONNECTION_LOST: // connection lost detected - { - // I can't handle this, send it to command - Message* m2 = new Message(); - m2->to = Command::getInstance(); - m2->message = Message::CONNECTION_LOST; - Command::getInstance()->postMessageNoLock(m2); - break; - } - case Player::STOP_PLAYBACK: - { - // FIXME Obselete ish - improve this - Message* m2 = new Message(); // Must be done after this thread finishes, and must break into master mutex - m2->to = Command::getInstance(); - m2->message = Message::STOP_PLAYBACK; - Command::getInstance()->postMessageNoLock(m2); - break; - } - case Player::ASPECT43: - { - if (dowss) - { - Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 43"); - wss.setWide(false); - wss.draw(); - boxstack->update(this, &wssRegion); - } - break; - } - case Player::ASPECT169: - { - if (dowss) - { - Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 169"); - wss.setWide(true); - wss.draw(); - boxstack->update(this, &wssRegion); - } - break; - } - } - } - else if (m->message == Message::AUDIO_CHANGE_CHANNEL) - { - Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received change audio channel to %i", m->parameter); - player->setAudioChannel(m->parameter&0xFFFF,(m->parameter&0xFF0000)>> 16 ); - } - else if (m->message == Message::SUBTITLE_CHANGE_CHANNEL) - { - Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received change subtitle channel to %i", m->parameter); - int type=((m->parameter & 0xFF0000)>>16); - switch (type) { - case 0x10: { //dvbsubtitle - player->setSubtitleChannel((m->parameter & 0xFFFF)); - player->turnSubtitlesOn(true); - VTeletextView *vtxt=((Player*)player)->getTeletextDecoder()->getTeletxtView(); - if (vtxt && vtxt->isInSubtitleMode()) { - BoxStack::getInstance()->remove(vtxt); - } - } break; - case 0xFF: { //nosubtitles - - player->turnSubtitlesOn(false); - VTeletextView *vtxt=((Player*)player)->getTeletextDecoder()->getTeletxtView(); - if (vtxt && vtxt->isInSubtitleMode()) { - BoxStack::getInstance()->remove(vtxt); - } - - } break; - case 0x11: { //videotext - player->turnSubtitlesOn(false); - doTeletext(); - ((Player*)player)->getTeletextDecoder()->setPage((m->parameter & 0xFFFF)); - } break; - }; - if (vas) { - BoxStack::getInstance()->update(vas); - } - BoxStack::getInstance()->update(this); - - - } - else if (m->message == Message::CHILD_CLOSE) - { - if (m->from == vas) - { - vas = NULL; - barVasHold = false; - if (!barGenHold && !barScanHold && !barVasHold) removeBar(); - } - } -} - -void VVideoRec::stopPlay() -{ - Log::getInstance()->log("VVideoRec", Log::DEBUG, "Pre stopPlay"); - - removeBar(); - Log::getInstance()->log("VVideoRec", Log::DEBUG, "1"); - player->stop(); - Log::getInstance()->log("VVideoRec", Log::DEBUG, "2"); - vdr->stopStreaming(); - Log::getInstance()->log("VVideoRec", Log::DEBUG, "3"); - delete player; - - playing = false; - - if (!vdr->isConnected()) { Command::getInstance()->connectionLost(); return; } - Log::getInstance()->log("VVideoRec", Log::DEBUG, "Post stopPlay"); -} - -void VVideoRec::toggleChopSides() -{ - if (video->getTVsize() == Video::ASPECT16X9) return; // Means nothing for 16:9 TVs - - if (videoMode == Video::NORMAL) - { - videoMode = Video::LETTERBOX; - video->setMode(Video::LETTERBOX); - } - else - { - videoMode = Video::NORMAL; - video->setMode(Video::NORMAL); - } -} - -void VVideoRec::doAudioSelector() -{ - int subtitleChannel=player->getCurrentSubtitleChannel(); - int subtitleType=0x10; - if (!(player)->isSubtitlesOn()) { - if ((player)->getTeletextDecoder()->getTeletxtView() && - (player)->getTeletextDecoder()->getTeletxtView()->isInSubtitleMode() - ) { - subtitleChannel=(player)->getTeletextDecoder()->getPage(); - subtitleType=0x11; - - } else { - subtitleType=0xFF; //turnedOff - subtitleChannel=0; - } - } - if (player->isPesRecording()) { - bool* availableMpegAudioChannels = player->getDemuxerMpegAudioChannels(); - bool* availableAc3AudioChannels = NULL; - bool* availableSubtitleChannels = player->getDemuxerSubtitleChannels(); - int *availableTTxtpages = player->getTeletxtSubtitlePages(); - int currentAudioChannel = player->getCurrentAudioChannel(); - if (Audio::getInstance()->supportsAc3()) - { - availableAc3AudioChannels = player->getDemuxerAc3AudioChannels(); - } - - vas = new VAudioSelector(this, availableMpegAudioChannels, availableAc3AudioChannels, currentAudioChannel,availableSubtitleChannels, availableTTxtpages, - subtitleChannel, subtitleType, myRec->recInfo); - } else { - // Draw the selector - Channel temp_channel=player->getDemuxerChannel(); - RecInfo *cur_info= myRec->recInfo; - unsigned char numchan_recinfo = cur_info->numComponents; - unsigned char numchan_subtitles_siz = temp_channel.numSPids; - ULONG mp_audcounter = 0; - ULONG ac3_counter = 0; - int dvb_subcounter = 1; - - unsigned char type; - char* lang; - char* description; - int i; - for (i = 0; i < numchan_recinfo; i++) - { - apid* ac = NULL; - type = cur_info->types[i]; - lang = cur_info->languages[i]; - description = cur_info->descriptions[i]; - - - if (cur_info->streams[i] == 2) { - switch (type) - { - case 1: //mpaudio mono - case 3: //mpaudio stereo - if (mp_audcounter < temp_channel.numAPids) ac = &temp_channel.apids[mp_audcounter]; - - mp_audcounter++; - break; - case 5: //ac3 - if (ac3_counter < temp_channel.numDPids) ac = &temp_channel.dpids[ac3_counter]; - ac3_counter++; - break; - } - } else if (cur_info->streams[i] == 3){ - if (dvb_subcounter < numchan_subtitles_siz) ac = &temp_channel.spids[dvb_subcounter]; - } else continue; //neither audio nor subtitle - if (ac) - { - if (description && (strlen(description) > 0)) - { - ac->name = new char[strlen(description) + 1]; - strcpy(ac->name, description); - - } else if (lang && strlen(lang) > 0) - { - ac->name = new char[strlen(lang) + 1]; - strcpy(ac->name, lang); - - } - } - } - for (i=0;iname==NULL) { - ac->name = new char[strlen(tr("unknown")) + 1]; - strcpy(ac->name, tr("unknown")); - } - } - for (i=0;iname==NULL) { - ac->name = new char[strlen(tr("unknown")) + 1]; - strcpy(ac->name, tr("unknown")); - } - } - for (i=0;iname==NULL) { - ac->name = new char[strlen(tr("unknown")) + 1]; - strcpy(ac->name, tr("unknown")); - } - } - - vas = new VAudioSelector(this,&temp_channel , (player)->getCurrentAudioChannel(), - subtitleType,subtitleChannel,player->getTeletxtSubtitlePages()); - for (i=0;iname; - ac->name=NULL; - } - for (i=0;iname; - ac->name=NULL; - } - for (i=0;iname; - ac->name=NULL; - } - } - - - vas->setBackgroundColour(barBlue); - vas->setPosition(0, barRegion.y - 120); - -// pal 62, ntsc 57 - - barVasHold = true; - doBar(0); - - vas->draw(); - boxstack->add(vas); - boxstack->update(vas); -} - -void VVideoRec::doBar(int action) -{ - barShowing = true; - - rectangle(barRegion, barBlue); - - /* Work out what to display - choices: - - Playing > - Paused || - FFwd >> - FBwd << - - Specials, informed by parameter - - Skip forward 10s >| - Skip backward 10s |< - Skip forward 1m >>| - Skip backward 1m |<< - - */ - - WSymbol w; - TEMPADD(&w); - w.nextSymbol = 0; - w.setPosition(barRegion.x + 66, barRegion.y + 16); - - UCHAR playerState = 0; - - if (action) - { - if (action == 1) w.nextSymbol = WSymbol::SKIPFORWARD; - else if (action == 2) w.nextSymbol = WSymbol::SKIPBACK; - else if (action == 3) w.nextSymbol = WSymbol::SKIPFORWARD2; - else if (action == 4) w.nextSymbol = WSymbol::SKIPBACK2; - } - else - { - playerState = player->getState(); - if (playerState == Player::S_PAUSE_P) w.nextSymbol = WSymbol::PAUSE; - else if (playerState == Player::S_PAUSE_I) w.nextSymbol = WSymbol::PAUSE; - else if (playerState == Player::S_FFWD) w.nextSymbol = WSymbol::FFWD; - else if (playerState == Player::S_FBWD) w.nextSymbol = WSymbol::FBWD; - else w.nextSymbol = WSymbol::PLAY; - } - - w.draw(); - - if ((playerState == Player::S_FFWD) || (playerState == Player::S_FBWD)) - { - // draw blips to show how fast the scan is - UCHAR scanrate = player->getIScanRate(); - if (scanrate >= 2) - { - char text[5]; - SNPRINTF(text, 5, "%ux", scanrate); - drawText(text, barRegion.x + 102, barRegion.y + 12, Colour::LIGHTTEXT); - } - } - - drawBarClocks(); - - boxstack->update(this, &barRegion); - - timers->cancelTimer(this, 1); - - - if ((playerState == Player::S_FFWD) || (playerState == Player::S_FBWD)) barScanHold = true; - else barScanHold = false; - - if (!barGenHold && !barScanHold && !barVasHold) timers->setTimerD(this, 1, 4); - - timers->setTimerD(this, 2, 0, 200000000); -} - -void VVideoRec::timercall(int clientReference) -{ - switch(clientReference) - { - case 1: - { - // Remove bar - removeBar(); - break; - } - case 2: - { - // Update clock - if (!barShowing) break; - drawBarClocks(); - boxstack->update(this, &barRegion); - - timers->setTimerD(this, 2, 0, 200000000); - break; - } - } -} - -hmsf VVideoRec::framesToHMSF(ULONG frames) -{ - hmsf ret; - /* from vdr */ - double Seconds; - double fps=myRec->recInfo->fps; - ret.frames= int(modf((frames + 0.5) / fps, &Seconds) * fps + 1); - int s = int(Seconds); - ret.seconds=s % 60; - ret.minutes = s / 60 % 60; - ret.hours = s / 3600; - - - return ret; -} - -void VVideoRec::drawBarClocks() -{ - if (barScanHold) - { - UCHAR playerState = player->getState(); - // sticky bar is set if we are in ffwd/fbwd mode - // if player has gone to S_PLAY then kill stickyBar, and run doBar(0) which - // will repaint all the bar (it will call this function again, but - // this section won't run because stickyBarF will then == false) - - if ((playerState != Player::S_FFWD) && (playerState != Player::S_FBWD)) - { - barScanHold = false; - doBar(0); - return; // doBar will call this function and do the rest - } - } - - Log* logger = Log::getInstance(); - logger->log("VVideoRec", Log::DEBUG, "Draw bar clocks"); - - // Draw RTC - // Blank the area first - rectangle(barRegion.x + 624, barRegion.y + 12, 60, 30, barBlue); - char timeString[20]; - time_t t; - time(&t); - struct tm* tms = localtime(&t); - strftime(timeString, 19, "%H:%M", tms); - drawText(timeString, barRegion.x + 624, barRegion.y + 12, Colour::LIGHTTEXT); - - // Draw clocks - - rectangle(clocksRegion, barBlue); - - ULONG currentFrameNum = player->getCurrentFrameNum(); - ULONG lengthFrames; - if (myRec->recInfo->timerEnd > time(NULL)) - { - // chasing playback - // Work out an approximate length in frames (good to 1s...) - lengthFrames =(ULONG) ((double)(myRec->recInfo->timerEnd - myRec->recInfo->timerStart) * myRec->recInfo->fps); - } - else - { - lengthFrames = player->getLengthFrames(); - } - - hmsf currentFrameHMSF = framesToHMSF(currentFrameNum); - hmsf lengthHMSF = framesToHMSF(lengthFrames); - - char buffer[100]; - if (currentFrameNum >= lengthFrames) - { - strcpy(buffer, "-:--:-- / -:--:--"); - } - else - { - SNPRINTF(buffer, 99, "%01i:%02i:%02i / %01i:%02i:%02i", currentFrameHMSF.hours, currentFrameHMSF.minutes, currentFrameHMSF.seconds, lengthHMSF.hours, lengthHMSF.minutes, lengthHMSF.seconds); - logger->log("VVideoRec", Log::DEBUG, buffer); - } - - drawText(buffer, clocksRegion.x, clocksRegion.y, Colour::LIGHTTEXT); - - - - - - - - // Draw progress bar - int progBarXbase = barRegion.x + 300; - - rectangle(barRegion.x + progBarXbase, barRegion.y + 12, 310, 24, Colour::LIGHTTEXT); - rectangle(barRegion.x + progBarXbase + 2, barRegion.y + 14, 306, 20, barBlue); - - if (currentFrameNum > lengthFrames) return; - if (lengthFrames == 0) return; - - // Draw yellow portion - int progressWidth = 302 * currentFrameNum / lengthFrames; - rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 16, progressWidth, 16, Colour::SELECTHIGHLIGHT); - - if (myRec->recInfo->timerEnd > time(NULL)) // if chasing - { - int nrWidth = (int)(302 * ((double)(lengthFrames - player->getLengthFrames()) / lengthFrames)); - - Log::getInstance()->log("GVASDF", Log::DEBUG, "Length Frames: %lu", lengthFrames); - Log::getInstance()->log("GVASDF", Log::DEBUG, "Player lf: %lu", player->getLengthFrames()); - Log::getInstance()->log("GVASDF", Log::DEBUG, "NR WDITH: %i", nrWidth); - rectangle(barRegion.x + progBarXbase + 4 + 302 - nrWidth, barRegion.y + 16, nrWidth, 16, Colour::RED); - } - - int posPix; - // Now calc position for blips - - if (myRec->hasMarks()) - { - // Draw blips where there are cut marks - MarkList* markList = myRec->getMarkList(); - MarkList::iterator i; - Mark* loopMark = NULL; - - for(i = markList->begin(); i != markList->end(); i++) - { - loopMark = *i; - if (loopMark->pos) - { - logger->log("VVideoRec", Log::DEBUG, "Drawing mark at frame %i", loopMark->pos); - posPix = 302 * loopMark->pos / lengthFrames; - rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 - 2, 3, 28, Colour::DANGER); - } - } - } - else - { - // Draw blips where start and end margins probably are - - posPix =(int) (302. * myRec->recInfo->fps * ((double)startMargin) /((double) lengthFrames)); - - rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 - 2, 2, 2, Colour::LIGHTTEXT); - rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 + 24, 2, 2, Colour::LIGHTTEXT); - - posPix = (int)(302. * ((double)lengthFrames - ((double)endMargin) * myRec->recInfo->fps) / ((double)lengthFrames)); - - rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 - 2, 2, 2, Colour::LIGHTTEXT); - rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 + 24, 2, 2, Colour::LIGHTTEXT); - } -} - -void VVideoRec::removeBar() -{ - if (!barShowing) return; - timers->cancelTimer(this, 2); - barShowing = false; - barGenHold = false; - barScanHold = false; - barVasHold = false; - rectangle(barRegion, transparent); - boxstack->update(this, &barRegion); -} - -void VVideoRec::doSummary() -{ - vsummary = new VInfo(); - vsummary->setTitleText(myRec->getProgName()); - vsummary->setBorderOn(1); - vsummary->setExitable(); - if (myRec->recInfo->summary) vsummary->setMainText(myRec->recInfo->summary); - else vsummary->setMainText(tr("Summary unavailable")); - if (Video::getInstance()->getFormat() == Video::PAL) - { - vsummary->setPosition(120, 130); - } - else - { - vsummary->setPosition(110, 90); - } - vsummary->setSize(510, 270); - add(vsummary); - vsummary->draw(); - - BoxStack::getInstance()->update(this); -} - -void VVideoRec::removeSummary() -{ - if (vsummary) - { - remove(vsummary); - delete vsummary; - vsummary = NULL; - draw(); - BoxStack::getInstance()->update(this); - } -} - -void VVideoRec::drawOSDBitmap(UINT posX, UINT posY, const Bitmap& bm) -{ - drawBitmap(posX, posY, bm); - Region r; - r.x = posX; r.y = posY; r.w = bm.getWidth(); r.h = bm.getHeight(); - boxstack->update(this, &r); -} - -void VVideoRec::clearOSD() -{ - rectangle(area, transparent); - boxstack->update(this, &area); -} - -void VVideoRec::clearOSDArea(UINT posX, UINT posY, UINT width, UINT height) -{ - Region r; - r.x = posX; r.y = posY; r.w = width; r.h = height; - rectangle(r, transparent); - boxstack->update(this, &r); -} +/* + 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. +*/ + +#include + +#include "vvideorec.h" +#include "vteletextview.h" + +#include "command.h" +#include "osd.h" +#include "wsymbol.h" +#include "audio.h" +#include "vdr.h" +#include "video.h" +#include "timers.h" +#include "player.h" +#include "recording.h" +#include "vaudioselector.h" +#include "message.h" +#include "remote.h" +#include "boxstack.h" +#include "vinfo.h" +#include "i18n.h" +#include "bitmap.h" +#include "recinfo.h" +#include "log.h" +#include "channel.h" + +VVideoRec::VVideoRec(Recording* rec, bool ish264) +{ + boxstack = BoxStack::getInstance(); + vdr = VDR::getInstance(); + video = Video::getInstance(); + timers = Timers::getInstance(); + vas = NULL; + vsummary = NULL; + + videoMode = video->getMode(); + myRec = rec; + + video->seth264mode(ish264); + + player = new Player(Command::getInstance(), this, this); + player->init(myRec->IsPesRecording,myRec->recInfo->fps); + + playing = false; + + startMargin = 0; + endMargin = 0; + char* cstartMargin = vdr->configLoad("Timers", "Start margin"); + char* cendMargin = vdr->configLoad("Timers", "End margin"); + if (!cstartMargin) + { + startMargin = 300; // 5 mins default + } + else + { + startMargin = atoi(cstartMargin) * 60; + delete[] cstartMargin; + } + + if (!cendMargin) + { + endMargin = 300; // 5 mins default + } + else + { + endMargin = atoi(cendMargin) * 60; + delete[] cendMargin; + } + + Log::getInstance()->log("VVideoRec", Log::DEBUG, "SM: %u EM: %u", startMargin, endMargin); + + setSize(video->getScreenWidth(), video->getScreenHeight()); + createBuffer(); + transparent.set(0, 0, 0, 0); + setBackgroundColour(transparent); + + barRegion.x = 0; + barRegion.y = video->getScreenHeight() - 58; // FIXME, need to be - 1? and below? + barRegion.w = video->getScreenWidth(); + barRegion.h = 58; + + clocksRegion.x = barRegion.x + 140; + clocksRegion.y = barRegion.y + 12; + clocksRegion.w = 170; + clocksRegion.h = getFontHeight(); +// barBlue.set(0, 0, 150, 150); + barBlue.set(0, 0, 0, 128); + + barShowing = false; + barGenHold = false; + barScanHold = false; + barVasHold = false; + + dowss = false; + char* optionWSS = vdr->configLoad("General", "WSS"); + if (optionWSS) + { + if (strstr(optionWSS, "Yes")) dowss = true; + delete[] optionWSS; + } + Log::getInstance()->log("VVideoRec", Log::DEBUG, "Do WSS: %u", dowss); + + if (dowss) + { + wss.setFormat(video->getFormat()); + wss.setWide(true); + add(&wss); + + wssRegion.x = 0; + wssRegion.y = 0; + wssRegion.w = video->getScreenWidth(); + wssRegion.h = 300; + } +} + +void VVideoRec::preDelete() +{ + timers->cancelTimer(this, 1); + timers->cancelTimer(this, 2); + + if (vas) + { + boxstack->remove(vas); + vas = NULL; + } + + if (vsummary) delete vsummary; + + if (playing) stopPlay(); +} + +VVideoRec::~VVideoRec() +{ + Log::getInstance()->log("VVideoRec", Log::DEBUG, "Entering vvideorec destructor"); + + video->setDefaultAspect(); + + // kill recInfo in case resumePoint has changed (likely) + myRec->dropRecInfo(); + // FIXME - do this properly - save the resume point back to the server manually and update + // rec->recInfo->resumePoint - this will fix the ~10s offset problem as well +} + +void VVideoRec::go(bool resume) +{ + ULONG startFrameNum; + if (resume) + startFrameNum = myRec->recInfo->resumePoint; + else + startFrameNum = 0; + + Log::getInstance()->log("VVideoRec", Log::DEBUG, "Starting stream: %s at frame: %lu", myRec->getFileName(), startFrameNum); + ULONG lengthFrames = 0; + bool isPesRecording; + ULLONG lengthBytes = vdr->streamRecording(myRec->getFileName(), &lengthFrames, &isPesRecording); + myRec->IsPesRecording = isPesRecording; + if (lengthBytes) + { + player->setLengthBytes(lengthBytes); + player->setLengthFrames(lengthFrames); + player->setStartFrame(startFrameNum); + player->play(); + playing = true; + doBar(0); + } + else + { + stopPlay(); // clean up + + if (!vdr->isConnected()) + { + Command::getInstance()->connectionLost(); + return; + } + + Message* m = new Message(); + m->message = Message::CLOSE_ME; + m->from = this; + m->to = boxstack; + Command::getInstance()->postMessageNoLock(m); + + VInfo* vi = new VInfo(); + vi->setSize(400, 150); + vi->createBuffer(); + if (video->getFormat() == Video::PAL) + vi->setPosition(170, 200); + else + vi->setPosition(160, 150); + vi->setExitable(); + vi->setBorderOn(1); + vi->setTitleBarOn(0); + vi->setOneLiner(tr("Error playing recording")); + vi->draw(); + + m = new Message(); + m->message = Message::ADD_VIEW; + m->to = boxstack; + m->parameter = (ULONG)vi; + Command::getInstance()->postMessageNoLock(m); + } +} + +int VVideoRec::handleCommand(int command) +{ + switch(command) + { + case Remote::PLAY: + { + player->play(); + doBar(0); + return 2; + } + + case Remote::PLAYPAUSE: + { + player->playpause(); + doBar(0); + return 2; + } + + case Remote::BACK: + { + if (vsummary) + { + removeSummary(); + return 2; + } + } // DROP THROUGH + case Remote::STOP: + case Remote::MENU: + { + if (playing) stopPlay(); + + return 4; + } + case Remote::PAUSE: + { + player->pause(); + doBar(0); + return 2; + } + case Remote::SKIPFORWARD: + { + doBar(3); + player->skipForward(60); + return 2; + } + case Remote::SKIPBACK: + { + doBar(4); + player->skipBackward(60); + return 2; + } + case Remote::FORWARD: + { + player->fastForward(); + doBar(0); + return 2; + } + case Remote::REVERSE: + { + player->fastBackward(); + doBar(0); + return 2; + } + case Remote::RED: + { + if (vsummary) removeSummary(); + else doSummary(); + return 2; + } + case Remote::GREEN: + { + doAudioSelector(); + return 2; + } + case Remote::YELLOW: + { + if (myRec->hasMarks()) + { + // skip to previous mark + Log* logger = Log::getInstance(); + int currentFrame = (player->getCurrentFrameNum()); // get current Frame + currentFrame -= 5. * myRec->recInfo->fps; // subtrack 5 seconds, else you cannot skip more than once back .. + + int prevMark = myRec->getPrevMark(currentFrame); // find previous Frame + if (prevMark) + { + logger->log("VVideoRec", Log::NOTICE, "jump back from pos %i to mark at %i",currentFrame,prevMark); + player->jumpToMark(prevMark); + } + doBar(4); + } + else + { + doBar(2); + player->skipBackward(10); + } + return 2; + } + case Remote::BLUE: + { + if (myRec->hasMarks()) + { + // skip to next mark + Log* logger = Log::getInstance(); + int currentFrame = (player->getCurrentFrameNum()); + + int nextMark = myRec->getNextMark(currentFrame); + + if (nextMark) + { + logger->log("VVideoRec", Log::NOTICE, "jump forward from pos %i to mark at %i",currentFrame,nextMark); + player->jumpToMark(nextMark); + } + doBar(3); + } + else + { + doBar(1); + player->skipForward(10); + } + return 2; + } + case Remote::STAR: + { + doBar(2); + player->skipBackward(10); + return 2; + } + case Remote::HASH: + { + doBar(1); + player->skipForward(10); + return 2; + } + case Remote::FULL: + case Remote::TV: + { + toggleChopSides(); + return 2; + } + + case Remote::OK: + { + if (vsummary) + { + removeSummary(); + return 2; + } + + if (barShowing) removeBar(); + else doBar(0); + return 2; + } + + case Remote::ZERO: player->jumpToPercent(0); doBar(0); return 2; + case Remote::ONE: player->jumpToPercent(10); doBar(0); return 2; + case Remote::TWO: player->jumpToPercent(20); doBar(0); return 2; + case Remote::THREE: player->jumpToPercent(30); doBar(0); return 2; + case Remote::FOUR: player->jumpToPercent(40); doBar(0); return 2; + case Remote::FIVE: player->jumpToPercent(50); doBar(0); return 2; + case Remote::SIX: player->jumpToPercent(60); doBar(0); return 2; + case Remote::SEVEN: player->jumpToPercent(70); doBar(0); return 2; + case Remote::EIGHT: player->jumpToPercent(80); doBar(0); return 2; + case Remote::NINE: player->jumpToPercent(90); doBar(0); return 2; + + case Remote::RECORD: player->toggleSubtitles(); return 2; +#ifdef DEV +// case Remote::RED: +// { + //Don't use RED for anything. It will eventually be recording summary + + //player->test1(); + + + /* + // for testing EPG in NTSC with a NTSC test video + Video::getInstance()->setMode(Video::QUARTER); + Video::getInstance()->setPosition(170, 5); + VEpg* vepg = new VEpg(NULL, 0); + vepg->draw(); + BoxStack::getInstance()->add(vepg); + BoxStack::getInstance()->update(vepg); + */ + +// return 2; +// } + +#endif + + } + + return 1; +} + +void VVideoRec::doTeletext() +{ + + bool exists=true; + + + // Draw the teletxt + VTeletextView *vtxv=player->getTeletextDecoder()->getTeletxtView(); + if (vtxv==NULL) { + vtxv= new VTeletextView((player)->getTeletextDecoder(),this); + (player)->getTeletextDecoder()->registerTeletextView(vtxv); + exists=false; + } + vtxv->setSubtitleMode(true); + vtxv->draw(); + draw(); + + if (!exists) { + BoxStack::getInstance()->add(vtxv); + } + BoxStack::getInstance()->update(this); + BoxStack::getInstance()->update(vtxv); +} + +void VVideoRec::processMessage(Message* m) +{ + Log::getInstance()->log("VVideoRec", Log::DEBUG, "Message received"); + + if (m->message == Message::MOUSE_LBDOWN) + { + UINT x = (m->parameter>>16) - getScreenX(); + UINT y = (m->parameter&0xFFFF) - getScreenY(); + + if (!barShowing) + { + BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press + } + else if (barRegion.x<=x && barRegion.y<=y && (barRegion.x+barRegion.w)>=x && (barRegion.y+barRegion.h)>=y) + { + int progBarXbase = barRegion.x + 300; + if (myRec->hasMarks()) + { + MarkList* markList = myRec->getMarkList(); + MarkList::iterator i; + Mark* loopMark = NULL; + int posPix; + ULONG lengthFrames; + if (myRec->recInfo->timerEnd > time(NULL)) + { + // chasing playback + // Work out an approximate length in frames (good to 1s...) + lengthFrames = (ULONG)((double)(myRec->recInfo->timerEnd - myRec->recInfo->timerStart) * myRec->recInfo->fps); + } + else + { + lengthFrames = player->getLengthFrames(); + } + for(i = markList->begin(); i != markList->end(); i++) + { + loopMark = *i; + if (loopMark->pos) + { + posPix = 302 * loopMark->pos / lengthFrames; + rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 - 2, 3, 28, Colour::DANGER); + if (x>=barRegion.x + progBarXbase + 2 + posPix + && x<=barRegion.x + progBarXbase + 2 + posPix+3 + && y>=barRegion.y + 12 - 2 + && y<=barRegion.y + 12 - 2+28) + { + player->jumpToMark(loopMark->pos); + doBar(3); + return; + } + } + } + } + + if (x>=barRegion.x + progBarXbase + 24 + && x<=barRegion.x + progBarXbase + 4 + 302 + && y>=barRegion.y + 12 - 2 + && y<=barRegion.y + 12 - 2+28) + { + int cx=x-(barRegion.x + progBarXbase + 4); + double percent=((double)cx)/302.*100.; + player->jumpToPercent(percent); + doBar(3); + return; + // int progressWidth = 302 * currentFrameNum / lengthFrames; + // rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 16, progressWidth, 16, Colour::SELECTHIGHLIGHT); + } + } + else + { + BoxStack::getInstance()->handleCommand(Remote::OK); //simulate rok press + } + } + else if (m->from == player) + { + if (m->message != Message::PLAYER_EVENT) return; + switch(m->parameter) + { + case Player::CONNECTION_LOST: // connection lost detected + { + // I can't handle this, send it to command + Message* m2 = new Message(); + m2->to = Command::getInstance(); + m2->message = Message::CONNECTION_LOST; + Command::getInstance()->postMessageNoLock(m2); + break; + } + case Player::STOP_PLAYBACK: + { + // FIXME Obselete ish - improve this + Message* m2 = new Message(); // Must be done after this thread finishes, and must break into master mutex + m2->to = Command::getInstance(); + m2->message = Message::STOP_PLAYBACK; + Command::getInstance()->postMessageNoLock(m2); + break; + } + case Player::ASPECT43: + { + if (dowss) + { + Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 43"); + wss.setWide(false); + wss.draw(); + boxstack->update(this, &wssRegion); + } + break; + } + case Player::ASPECT169: + { + if (dowss) + { + Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received do WSS 169"); + wss.setWide(true); + wss.draw(); + boxstack->update(this, &wssRegion); + } + break; + } + } + } + else if (m->message == Message::AUDIO_CHANGE_CHANNEL) + { + Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received change audio channel to %i", m->parameter); + player->setAudioChannel(m->parameter&0xFFFF,(m->parameter&0xFF0000)>> 16 ); + } + else if (m->message == Message::SUBTITLE_CHANGE_CHANNEL) + { + Log::getInstance()->log("VVideoRec", Log::DEBUG, "Received change subtitle channel to %i", m->parameter); + int type=((m->parameter & 0xFF0000)>>16); + switch (type) { + case 0x10: { //dvbsubtitle + player->setSubtitleChannel((m->parameter & 0xFFFF)); + player->turnSubtitlesOn(true); + VTeletextView *vtxt=((Player*)player)->getTeletextDecoder()->getTeletxtView(); + if (vtxt && vtxt->isInSubtitleMode()) { + BoxStack::getInstance()->remove(vtxt); + } + } break; + case 0xFF: { //nosubtitles + + player->turnSubtitlesOn(false); + VTeletextView *vtxt=((Player*)player)->getTeletextDecoder()->getTeletxtView(); + if (vtxt && vtxt->isInSubtitleMode()) { + BoxStack::getInstance()->remove(vtxt); + } + + } break; + case 0x11: { //videotext + player->turnSubtitlesOn(false); + doTeletext(); + ((Player*)player)->getTeletextDecoder()->setPage((m->parameter & 0xFFFF)); + } break; + }; + if (vas) { + BoxStack::getInstance()->update(vas); + } + BoxStack::getInstance()->update(this); + + + } + else if (m->message == Message::CHILD_CLOSE) + { + if (m->from == vas) + { + vas = NULL; + barVasHold = false; + if (!barGenHold && !barScanHold && !barVasHold) removeBar(); + } + } +} + +void VVideoRec::stopPlay() +{ + Log::getInstance()->log("VVideoRec", Log::DEBUG, "Pre stopPlay"); + + removeBar(); + Log::getInstance()->log("VVideoRec", Log::DEBUG, "1"); + player->stop(); + Log::getInstance()->log("VVideoRec", Log::DEBUG, "2"); + vdr->stopStreaming(); + Log::getInstance()->log("VVideoRec", Log::DEBUG, "3"); + delete player; + + playing = false; + + if (!vdr->isConnected()) { Command::getInstance()->connectionLost(); return; } + Log::getInstance()->log("VVideoRec", Log::DEBUG, "Post stopPlay"); +} + +void VVideoRec::toggleChopSides() +{ + if (video->getTVsize() == Video::ASPECT16X9) return; // Means nothing for 16:9 TVs + + if (videoMode == Video::NORMAL) + { + videoMode = Video::LETTERBOX; + video->setMode(Video::LETTERBOX); + } + else + { + videoMode = Video::NORMAL; + video->setMode(Video::NORMAL); + } +} + +void VVideoRec::doAudioSelector() +{ + int subtitleChannel=player->getCurrentSubtitleChannel(); + int subtitleType=0x10; + if (!(player)->isSubtitlesOn()) { + if ((player)->getTeletextDecoder()->getTeletxtView() && + (player)->getTeletextDecoder()->getTeletxtView()->isInSubtitleMode() + ) { + subtitleChannel=(player)->getTeletextDecoder()->getPage(); + subtitleType=0x11; + + } else { + subtitleType=0xFF; //turnedOff + subtitleChannel=0; + } + } + if (player->isPesRecording()) { + bool* availableMpegAudioChannels = player->getDemuxerMpegAudioChannels(); + bool* availableAc3AudioChannels = NULL; + bool* availableSubtitleChannels = player->getDemuxerSubtitleChannels(); + int *availableTTxtpages = player->getTeletxtSubtitlePages(); + int currentAudioChannel = player->getCurrentAudioChannel(); + if (Audio::getInstance()->supportsAc3()) + { + availableAc3AudioChannels = player->getDemuxerAc3AudioChannels(); + } + + vas = new VAudioSelector(this, availableMpegAudioChannels, availableAc3AudioChannels, currentAudioChannel,availableSubtitleChannels, availableTTxtpages, + subtitleChannel, subtitleType, myRec->recInfo); + } else { + // Draw the selector + Channel temp_channel=player->getDemuxerChannel(); + RecInfo *cur_info= myRec->recInfo; + unsigned char numchan_recinfo = cur_info->numComponents; + unsigned char numchan_subtitles_siz = temp_channel.numSPids; + ULONG mp_audcounter = 0; + ULONG ac3_counter = 0; + int dvb_subcounter = 1; + + unsigned char type; + char* lang; + char* description; + int i; + for (i = 0; i < numchan_recinfo; i++) + { + apid* ac = NULL; + type = cur_info->types[i]; + lang = cur_info->languages[i]; + description = cur_info->descriptions[i]; + + + if (cur_info->streams[i] == 2) { + switch (type) + { + case 1: //mpaudio mono + case 3: //mpaudio stereo + if (mp_audcounter < temp_channel.numAPids) ac = &temp_channel.apids[mp_audcounter]; + + mp_audcounter++; + break; + case 5: //ac3 + if (ac3_counter < temp_channel.numDPids) ac = &temp_channel.dpids[ac3_counter]; + ac3_counter++; + break; + } + } else if (cur_info->streams[i] == 3){ + if (dvb_subcounter < numchan_subtitles_siz) ac = &temp_channel.spids[dvb_subcounter]; + } else continue; //neither audio nor subtitle + if (ac) + { + if (description && (strlen(description) > 0)) + { + ac->name = new char[strlen(description) + 1]; + strcpy(ac->name, description); + + } else if (lang && strlen(lang) > 0) + { + ac->name = new char[strlen(lang) + 1]; + strcpy(ac->name, lang); + + } + } + } + for (i=0;iname==NULL) { + ac->name = new char[strlen(tr("unknown")) + 1]; + strcpy(ac->name, tr("unknown")); + } + } + for (i=0;iname==NULL) { + ac->name = new char[strlen(tr("unknown")) + 1]; + strcpy(ac->name, tr("unknown")); + } + } + for (i=0;iname==NULL) { + ac->name = new char[strlen(tr("unknown")) + 1]; + strcpy(ac->name, tr("unknown")); + } + } + + vas = new VAudioSelector(this,&temp_channel , (player)->getCurrentAudioChannel(), + subtitleType,subtitleChannel,player->getTeletxtSubtitlePages()); + for (i=0;iname; + ac->name=NULL; + } + for (i=0;iname; + ac->name=NULL; + } + for (i=0;iname; + ac->name=NULL; + } + } + + + vas->setBackgroundColour(barBlue); + vas->setPosition(0, barRegion.y - 120); + +// pal 62, ntsc 57 + + barVasHold = true; + doBar(0); + + vas->draw(); + boxstack->add(vas); + boxstack->update(vas); +} + +void VVideoRec::doBar(int action) +{ + barShowing = true; + + rectangle(barRegion, barBlue); + + /* Work out what to display - choices: + + Playing > + Paused || + FFwd >> + FBwd << + + Specials, informed by parameter + + Skip forward 10s >| + Skip backward 10s |< + Skip forward 1m >>| + Skip backward 1m |<< + + */ + + WSymbol w; + TEMPADD(&w); + w.nextSymbol = 0; + w.setPosition(barRegion.x + 66, barRegion.y + 16); + + UCHAR playerState = 0; + + if (action) + { + if (action == 1) w.nextSymbol = WSymbol::SKIPFORWARD; + else if (action == 2) w.nextSymbol = WSymbol::SKIPBACK; + else if (action == 3) w.nextSymbol = WSymbol::SKIPFORWARD2; + else if (action == 4) w.nextSymbol = WSymbol::SKIPBACK2; + } + else + { + playerState = player->getState(); + if (playerState == Player::S_PAUSE_P) w.nextSymbol = WSymbol::PAUSE; + else if (playerState == Player::S_PAUSE_I) w.nextSymbol = WSymbol::PAUSE; + else if (playerState == Player::S_FFWD) w.nextSymbol = WSymbol::FFWD; + else if (playerState == Player::S_FBWD) w.nextSymbol = WSymbol::FBWD; + else w.nextSymbol = WSymbol::PLAY; + } + + w.draw(); + + if ((playerState == Player::S_FFWD) || (playerState == Player::S_FBWD)) + { + // draw blips to show how fast the scan is + UCHAR scanrate = player->getIScanRate(); + if (scanrate >= 2) + { + char text[5]; + SNPRINTF(text, 5, "%ux", scanrate); + drawText(text, barRegion.x + 102, barRegion.y + 12, Colour::LIGHTTEXT); + } + } + + drawBarClocks(); + + boxstack->update(this, &barRegion); + + timers->cancelTimer(this, 1); + + + if ((playerState == Player::S_FFWD) || (playerState == Player::S_FBWD)) barScanHold = true; + else barScanHold = false; + + if (!barGenHold && !barScanHold && !barVasHold) timers->setTimerD(this, 1, 4); + + timers->setTimerD(this, 2, 0, 200000000); +} + +void VVideoRec::timercall(int clientReference) +{ + switch(clientReference) + { + case 1: + { + // Remove bar + removeBar(); + break; + } + case 2: + { + // Update clock + if (!barShowing) break; + drawBarClocks(); + boxstack->update(this, &barRegion); + + timers->setTimerD(this, 2, 0, 200000000); + break; + } + } +} + +hmsf VVideoRec::framesToHMSF(ULONG frames) +{ + hmsf ret; + /* from vdr */ + double Seconds; + double fps=myRec->recInfo->fps; + ret.frames= int(modf((frames + 0.5) / fps, &Seconds) * fps + 1); + int s = int(Seconds); + ret.seconds=s % 60; + ret.minutes = s / 60 % 60; + ret.hours = s / 3600; + + + return ret; +} + +void VVideoRec::drawBarClocks() +{ + if (barScanHold) + { + UCHAR playerState = player->getState(); + // sticky bar is set if we are in ffwd/fbwd mode + // if player has gone to S_PLAY then kill stickyBar, and run doBar(0) which + // will repaint all the bar (it will call this function again, but + // this section won't run because stickyBarF will then == false) + + if ((playerState != Player::S_FFWD) && (playerState != Player::S_FBWD)) + { + barScanHold = false; + doBar(0); + return; // doBar will call this function and do the rest + } + } + + Log* logger = Log::getInstance(); + logger->log("VVideoRec", Log::DEBUG, "Draw bar clocks"); + + // Draw RTC + // Blank the area first + rectangle(barRegion.x + 624, barRegion.y + 12, 60, 30, barBlue); + char timeString[20]; + time_t t; + time(&t); + struct tm* tms = localtime(&t); + strftime(timeString, 19, "%H:%M", tms); + drawText(timeString, barRegion.x + 624, barRegion.y + 12, Colour::LIGHTTEXT); + + // Draw clocks + + rectangle(clocksRegion, barBlue); + + ULONG currentFrameNum = player->getCurrentFrameNum(); + ULONG lengthFrames; + if (myRec->recInfo->timerEnd > time(NULL)) + { + // chasing playback + // Work out an approximate length in frames (good to 1s...) + lengthFrames =(ULONG) ((double)(myRec->recInfo->timerEnd - myRec->recInfo->timerStart) * myRec->recInfo->fps); + } + else + { + lengthFrames = player->getLengthFrames(); + } + + hmsf currentFrameHMSF = framesToHMSF(currentFrameNum); + hmsf lengthHMSF = framesToHMSF(lengthFrames); + + char buffer[100]; + if (currentFrameNum >= lengthFrames) + { + strcpy(buffer, "-:--:-- / -:--:--"); + } + else + { + SNPRINTF(buffer, 99, "%01i:%02i:%02i / %01i:%02i:%02i", currentFrameHMSF.hours, currentFrameHMSF.minutes, currentFrameHMSF.seconds, lengthHMSF.hours, lengthHMSF.minutes, lengthHMSF.seconds); + logger->log("VVideoRec", Log::DEBUG, buffer); + } + + drawText(buffer, clocksRegion.x, clocksRegion.y, Colour::LIGHTTEXT); + + + + + + + + // Draw progress bar + int progBarXbase = barRegion.x + 300; + + rectangle(barRegion.x + progBarXbase, barRegion.y + 12, 310, 24, Colour::LIGHTTEXT); + rectangle(barRegion.x + progBarXbase + 2, barRegion.y + 14, 306, 20, barBlue); + + if (currentFrameNum > lengthFrames) return; + if (lengthFrames == 0) return; + + // Draw yellow portion + int progressWidth = 302 * currentFrameNum / lengthFrames; + rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 16, progressWidth, 16, Colour::SELECTHIGHLIGHT); + + if (myRec->recInfo->timerEnd > time(NULL)) // if chasing + { + int nrWidth = (int)(302 * ((double)(lengthFrames - player->getLengthFrames()) / lengthFrames)); + + Log::getInstance()->log("GVASDF", Log::DEBUG, "Length Frames: %lu", lengthFrames); + Log::getInstance()->log("GVASDF", Log::DEBUG, "Player lf: %lu", player->getLengthFrames()); + Log::getInstance()->log("GVASDF", Log::DEBUG, "NR WDITH: %i", nrWidth); + rectangle(barRegion.x + progBarXbase + 4 + 302 - nrWidth, barRegion.y + 16, nrWidth, 16, Colour::RED); + } + + int posPix; + // Now calc position for blips + + if (myRec->hasMarks()) + { + // Draw blips where there are cut marks + MarkList* markList = myRec->getMarkList(); + MarkList::iterator i; + Mark* loopMark = NULL; + + for(i = markList->begin(); i != markList->end(); i++) + { + loopMark = *i; + if (loopMark->pos) + { + logger->log("VVideoRec", Log::DEBUG, "Drawing mark at frame %i", loopMark->pos); + posPix = 302 * loopMark->pos / lengthFrames; + rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 - 2, 3, 28, Colour::DANGER); + } + } + } + else + { + // Draw blips where start and end margins probably are + + posPix =(int) (302. * myRec->recInfo->fps * ((double)startMargin) /((double) lengthFrames)); + + rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 - 2, 2, 2, Colour::LIGHTTEXT); + rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 + 24, 2, 2, Colour::LIGHTTEXT); + + posPix = (int)(302. * ((double)lengthFrames - ((double)endMargin) * myRec->recInfo->fps) / ((double)lengthFrames)); + + rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 - 2, 2, 2, Colour::LIGHTTEXT); + rectangle(barRegion.x + progBarXbase + 2 + posPix, barRegion.y + 12 + 24, 2, 2, Colour::LIGHTTEXT); + } +} + +void VVideoRec::removeBar() +{ + if (!barShowing) return; + timers->cancelTimer(this, 2); + barShowing = false; + barGenHold = false; + barScanHold = false; + barVasHold = false; + rectangle(barRegion, transparent); + boxstack->update(this, &barRegion); +} + +void VVideoRec::doSummary() +{ + vsummary = new VInfo(); + vsummary->setTitleText(myRec->getProgName()); + vsummary->setBorderOn(1); + vsummary->setExitable(); + if (myRec->recInfo->summary) vsummary->setMainText(myRec->recInfo->summary); + else vsummary->setMainText(tr("Summary unavailable")); + if (Video::getInstance()->getFormat() == Video::PAL) + { + vsummary->setPosition(120, 130); + } + else + { + vsummary->setPosition(110, 90); + } + vsummary->setSize(510, 270); + add(vsummary); + vsummary->draw(); + + BoxStack::getInstance()->update(this); +} + +void VVideoRec::removeSummary() +{ + if (vsummary) + { + remove(vsummary); + delete vsummary; + vsummary = NULL; + draw(); + BoxStack::getInstance()->update(this); + } +} + +void VVideoRec::drawOSDBitmap(UINT posX, UINT posY, const Bitmap& bm) +{ + drawBitmap(posX, posY, bm); + Region r; + r.x = posX; r.y = posY; r.w = bm.getWidth(); r.h = bm.getHeight(); + boxstack->update(this, &r); +} + +void VVideoRec::clearOSD() +{ + rectangle(area, transparent); + boxstack->update(this, &area); +} + +void VVideoRec::clearOSDArea(UINT posX, UINT posY, UINT width, UINT height) +{ + Region r; + r.x = posX; r.y = posY; r.w = width; r.h = height; + rectangle(r, transparent); + boxstack->update(this, &r); +} diff --git a/vwelcome.cc b/vwelcome.cc index 3f92793..cb88da6 100644 --- a/vwelcome.cc +++ b/vwelcome.cc @@ -1,350 +1,358 @@ -/* - 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. -*/ - -#include "vwelcome.h" - -#include "remote.h" -#include "vdr.h" -#include "vchannellist.h" -#include "vrecordinglist.h" -#include "vtimerlist.h" -#include "command.h" -#include "message.h" -#include "colour.h" -#include "video.h" -#include "i18n.h" -#include "timers.h" -#include "vscreensaver.h" -#include "vmedialist.h" -#include "boxstack.h" -#include "vopts.h" - -VWelcome::VWelcome() -{ - boxstack = BoxStack::getInstance(); - - clockRegion.x = 400; - clockRegion.y = 0; - clockRegion.w = 60; - clockRegion.h = 30; - - setSize(460, 220); - createBuffer(); - if (Video::getInstance()->getFormat() == Video::PAL) - { - setPosition(140, 170); - } - else - { - setPosition(130, 140); - } - - setTitleBarOn(1); - setTitleBarColour(Colour::TITLEBARBACKGROUND); - - sl.setPosition(20, 40); - sl.setSize(200, 160); - add(&sl); - - setTitleText(tr("Welcome")); - sl.addOption(tr("1. Live TV"), 1, 1); - sl.addOption(tr("2. Radio"), 2, 0); - sl.addOption(tr("3. Recordings"), 3, 0); - sl.addOption(tr("4. Timers"), 4, 0); -#ifndef _MIPS_ARCH -#ifndef WIN32 - sl.addOption(tr("5. MediaPlayer"), 5, 0); -#endif -#endif - sl.addOption(tr("6. Options"), 6, 0); -#ifndef _MIPS_ARCH - sl.addOption(tr("7. Reboot"), 7, 0); -#else - sl.addOption(tr("7. Exit to Gaya"), 7, 0); -#endif - - jpeg.setPosition(240, 60); -#ifndef _MIPS_ARCH - jpeg.init("/vdr.jpg"); -#else - jpeg.init("vdr.jpg"); -#endif - add(&jpeg); -} - -void VWelcome::preDelete() -{ - Timers::getInstance()->cancelTimer(this, 1); -} - -VWelcome::~VWelcome() -{ -} - -void VWelcome::draw() -{ - TBBoxx::draw(); - drawClock(); -} - -void VWelcome::drawClock() -{ - // Blank the area first - rectangle(area.w - 60, 0, 60, 30, titleBarColour); - - char timeString[20]; - time_t t; - time(&t); - struct tm* tms = localtime(&t); - strftime(timeString, 19, "%H:%M", tms); - drawTextRJ(timeString, 450, 5, Colour::LIGHTTEXT); - - time_t dt = 60 - (t % 60); // seconds to the next minute - if (dt == 0) dt = 60; // advance a whole minute if necessary - dt += t; // get a time_t value for it rather than using duration - // (so it will occur at the actual second and not second and a half) - - Timers::getInstance()->setTimerT(this, 1, dt); -} - -void VWelcome::timercall(int clientReference) -{ - drawClock(); - boxstack->update(this, &clockRegion); -} - -int VWelcome::handleCommand(int command) -{ - switch(command) - { - case Remote::DF_UP: - case Remote::UP: - { - sl.up(); - sl.draw(); - boxstack->update(this); - return 2; - } - case Remote::DF_DOWN: - case Remote::DOWN: - { - sl.down(); - sl.draw(); - boxstack->update(this); - return 2; - } - case Remote::ONE: - { - doChannelsList(); - return 2; - } - case Remote::TWO: - { - doRadioList(); - return 2; - } - case Remote::THREE: - { - doRecordingsList(); - return 2; - } - case Remote::FOUR: - { - doTimersList(); - return 2; - } - case Remote::FIVE: - { - doMediaList(); - return 2; - } - case Remote::SIX: - { - doOptions(); - return 2; - } - case Remote::SEVEN: - { - Command::getInstance()->doReboot(); - } - case Remote::OK: - { - ULONG option = sl.getCurrentOptionData(); - if (option == 1) - { - doChannelsList(); - return 2; - } - else if (option == 2) - { - doRadioList(); - return 2; - } - else if (option == 3) - { - doRecordingsList(); - return 2; - } - else if (option == 4) - { - doTimersList(); - return 2; - } - else if (option == 5) - { - doMediaList(); - return 2; - } - else if (option == 6) - { - doOptions(); - return 2; - } - else if (option == 7) - { - Command::getInstance()->doReboot(); - return 2; - } - return 2; // never gets here - } -//#ifdef DEV - case Remote::NINE: - { - VScreensaver* vscreensaver = new VScreensaver(); - boxstack->add(vscreensaver); - vscreensaver->draw(); -// boxstack->update(vscreensaver); - - return 2; - } -//#endif - - // Test -// case Remote::BACK: -// { -// return 4; -// } - - } - return 1; -} - -void VWelcome::doChannelsList() -{ - ChannelList* chanList = VDR::getInstance()->getChannelsList(VDR::VIDEO); - - if (chanList) - { - VChannelList* vchan = new VChannelList(VDR::VIDEO); - vchan->setList(chanList); - - vchan->draw(); - boxstack->add(vchan); - boxstack->update(vchan); - } - else - { - Command::getInstance()->connectionLost(); - } -} - -void VWelcome::doRadioList() -{ - ChannelList* chanList = VDR::getInstance()->getChannelsList(VDR::RADIO); - - if (chanList) - { - VChannelList* vchan = new VChannelList(VDR::RADIO); - vchan->setList(chanList); - - vchan->draw(); - boxstack->add(vchan); - boxstack->update(vchan); - } - else - { - Command::getInstance()->connectionLost(); - } -} - -void VWelcome::doRecordingsList() -{ - VRecordingList* vrec = new VRecordingList(); - vrec->draw(); - boxstack->add(vrec); - boxstack->update(vrec); - - if (!vrec->load()) - { - Command::getInstance()->connectionLost(); - } -} - -void VWelcome::doMediaList() -{ - VMediaList::createList(); -} - -void VWelcome::doTimersList() -{ - VTimerList* vtl = new VTimerList(); - if (!vtl->load()) - { - delete vtl; - Command::getInstance()->connectionLost(); - return; - } - - vtl->draw(); - boxstack->add(vtl); - boxstack->update(vtl); -} - -void VWelcome::doOptions() -{ -// VOptionsMenu* voptionsmenu = new VOptionsMenu(); -// voptionsmenu->draw(); -// boxstack->add(voptionsmenu); -// boxstack->updateView(voptionsmenu); - - VOpts* vopts = new VOpts(); - vopts->draw(); - boxstack->add(vopts); - boxstack->update(vopts); -} - -void VWelcome::processMessage(Message* m) -{ - if (m->message == Message::MOUSE_MOVE) - { - if (sl.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) - { - sl.draw(); - boxstack->update(this); - } - } - else if (m->message == Message::MOUSE_LBDOWN) - { - if (sl.mouseLBDOWN((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) - { - boxstack->handleCommand(Remote::OK); //simulate OK press - } - } -} +/* + 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. +*/ + +#include "vwelcome.h" + +#include "remote.h" +#include "vdr.h" +#include "vchannellist.h" +#include "vrecordinglist.h" +#include "vtimerlist.h" +#include "command.h" +#include "message.h" +#include "colour.h" +#include "video.h" +#include "i18n.h" +#include "timers.h" +#include "vscreensaver.h" +#include "vmedialist.h" +#include "boxstack.h" +#include "vopts.h" + +#include "log.h" + +VWelcome::VWelcome() +{ + boxstack = BoxStack::getInstance(); + + clockRegion.x = 400; + clockRegion.y = 0; + clockRegion.w = 60; + clockRegion.h = 30; + + setSize(460, 220); + createBuffer(); + if (Video::getInstance()->getFormat() == Video::PAL) + { + setPosition(140, 170); + } + else + { + setPosition(130, 140); + } + + setTitleBarOn(1); + setTitleBarColour(Colour::TITLEBARBACKGROUND); + + sl.setPosition(20, 40); + sl.setSize(200, 160); + add(&sl); + + setTitleText(tr("Welcome")); + sl.addOption(tr("1. Live TV"), 1, 1); + sl.addOption(tr("2. Radio"), 2, 0); + sl.addOption(tr("3. Recordings"), 3, 0); + sl.addOption(tr("4. Timers"), 4, 0); +#ifdef VOMP_PLATTFORM_MVP + sl.addOption(tr("5. MediaPlayer"), 5, 0); +#endif + + sl.addOption(tr("6. Options"), 6, 0); +#ifdef VOMP_PLATTFORM_MVP + sl.addOption(tr("7. Reboot"), 7, 0); +#else + sl.addOption(tr("7. Exit"), 7, 0); +#endif + + jpeg.setPosition(240, 60); +#ifndef _MIPS_ARCH + jpeg.init("/vdr.jpg"); +#else + jpeg.init("vdr.jpg"); +#endif + add(&jpeg); +} + +void VWelcome::preDelete() +{ + Timers::getInstance()->cancelTimer(this, 1); +} + +VWelcome::~VWelcome() +{ +} + +void VWelcome::draw() +{ + Log::getInstance()->log("VWelcome", Log::DEBUG, "Mark1"); + TBBoxx::draw(); + Log::getInstance()->log("VWelcome", Log::DEBUG, "Mark2"); + drawClock(); + Log::getInstance()->log("VWelcome", Log::DEBUG, "Mark3"); +} + +void VWelcome::drawClock() +{ + // Blank the area first + rectangle(area.w - 60, 0, 60, 30, titleBarColour); + + char timeString[20]; + time_t t; + time(&t); + struct tm* tms = localtime(&t); + strftime(timeString, 19, "%H:%M", tms); + drawTextRJ(timeString, 450, 5, Colour::LIGHTTEXT); + + time_t dt = 60 - (t % 60); // seconds to the next minute + if (dt == 0) dt = 60; // advance a whole minute if necessary + dt += t; // get a time_t value for it rather than using duration + // (so it will occur at the actual second and not second and a half) + + Timers::getInstance()->setTimerT(this, 1, dt); +} + +void VWelcome::timercall(int clientReference) +{ + drawClock(); + boxstack->update(this, &clockRegion); +} + +int VWelcome::handleCommand(int command) +{ + switch(command) + { + case Remote::DF_UP: + case Remote::UP: + { + sl.up(); + sl.draw(); + boxstack->update(this); + return 2; + } + case Remote::DF_DOWN: + case Remote::DOWN: + { + sl.down(); + sl.draw(); + boxstack->update(this); + return 2; + } + case Remote::ONE: + { + doChannelsList(); + return 2; + } + case Remote::TWO: + { + doRadioList(); + return 2; + } + case Remote::THREE: + { + doRecordingsList(); + return 2; + } + case Remote::FOUR: + { + doTimersList(); + return 2; + } + case Remote::FIVE: + { +#ifdef VOMP_PLATTFORM_MVP + doMediaList(); +#endif + return 2; + } + case Remote::SIX: + { + doOptions(); + return 2; + } + case Remote::SEVEN: + { + Command::getInstance()->doReboot(); + } + case Remote::OK: + { + ULONG option = sl.getCurrentOptionData(); + if (option == 1) + { + doChannelsList(); + return 2; + } + else if (option == 2) + { + doRadioList(); + return 2; + } + else if (option == 3) + { + doRecordingsList(); + return 2; + } + else if (option == 4) + { + doTimersList(); + return 2; + } + else if (option == 5) + { + doMediaList(); + return 2; + } + else if (option == 6) + { + doOptions(); + return 2; + } + else if (option == 7) + { + Command::getInstance()->doReboot(); + return 2; + } + return 2; // never gets here + } +//#ifdef DEV + case Remote::NINE: + { + VScreensaver* vscreensaver = new VScreensaver(); + boxstack->add(vscreensaver); + vscreensaver->draw(); +// boxstack->update(vscreensaver); + + return 2; + } +//#endif + + // Test +// case Remote::BACK: +// { +// return 4; +// } + + } + return 1; +} + +void VWelcome::doChannelsList() +{ + ChannelList* chanList = VDR::getInstance()->getChannelsList(VDR::VIDEO); + + if (chanList) + { + VChannelList* vchan = new VChannelList(VDR::VIDEO); + vchan->setList(chanList); + + vchan->draw(); + boxstack->add(vchan); + boxstack->update(vchan); + } + else + { + Command::getInstance()->connectionLost(); + } +} + +void VWelcome::doRadioList() +{ + ChannelList* chanList = VDR::getInstance()->getChannelsList(VDR::RADIO); + + if (chanList) + { + VChannelList* vchan = new VChannelList(VDR::RADIO); + vchan->setList(chanList); + + vchan->draw(); + boxstack->add(vchan); + boxstack->update(vchan); + } + else + { + Command::getInstance()->connectionLost(); + } +} + +void VWelcome::doRecordingsList() +{ + VRecordingList* vrec = new VRecordingList(); + vrec->draw(); + boxstack->add(vrec); + boxstack->update(vrec); + + if (!vrec->load()) + { + Command::getInstance()->connectionLost(); + } +} + +void VWelcome::doMediaList() +{ +#ifdef VOMP_MEDIAPLAYER + VMediaList::createList(); +#endif +} + +void VWelcome::doTimersList() +{ + VTimerList* vtl = new VTimerList(); + if (!vtl->load()) + { + delete vtl; + Command::getInstance()->connectionLost(); + return; + } + + vtl->draw(); + boxstack->add(vtl); + boxstack->update(vtl); +} + +void VWelcome::doOptions() +{ +// VOptionsMenu* voptionsmenu = new VOptionsMenu(); +// voptionsmenu->draw(); +// boxstack->add(voptionsmenu); +// boxstack->updateView(voptionsmenu); + + VOpts* vopts = new VOpts(); + vopts->draw(); + boxstack->add(vopts); + boxstack->update(vopts); +} + +void VWelcome::processMessage(Message* m) +{ + if (m->message == Message::MOUSE_MOVE) + { + if (sl.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) + { + sl.draw(); + boxstack->update(this); + } + } + else if (m->message == Message::MOUSE_LBDOWN) + { + if (sl.mouseLBDOWN((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY())) + { + boxstack->handleCommand(Remote::OK); //simulate OK press + } + } +} diff --git a/vwelcome.h b/vwelcome.h index 90cf754..88e1fcb 100644 --- a/vwelcome.h +++ b/vwelcome.h @@ -29,6 +29,7 @@ #include "wselectlist.h" #include "wjpeg.h" #include "region.h" +#include "defines.h" class Message; class BoxStack; @@ -48,7 +49,7 @@ class VWelcome : public TBBoxx, public TimerReceiver private: WSelectList sl; - WJpeg jpeg; + WJpegTYPE jpeg; BoxStack* boxstack; diff --git a/wbutton.cc b/wbutton.cc index 7340cf1..5f4a6fc 100644 --- a/wbutton.cc +++ b/wbutton.cc @@ -1,111 +1,111 @@ -/* - 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. -*/ - -#include "wbutton.h" - -#include "colour.h" - -WButton::WButton() -{ - int fontHeight = Surface::getFontHeight(); - setSize(70, fontHeight); - - mytext = NULL; - active = 0; - tag = 0; - dimmed = false; -} - -WButton::~WButton() -{ - if (mytext) delete[] mytext; -} - -void WButton::setText(const char* takeText) -{ - int length = strlen(takeText); - mytext = new char[length + 1]; - strcpy(mytext, takeText); -} - -void WButton::setActive(UCHAR tactive) -{ - dimmed = false; - active = tactive; -} - -void WButton::dim() -{ - // a bolt on for now - an active but dimmed state - dimmed = true; - active = false; -} - -void WButton::draw() -{ - if (dimmed) - { - fillColour(Colour::BLACK); - drawTextCentre(mytext, area.w / 2, 0, Colour::SELECTHIGHLIGHT); - } - else if (active) - { - fillColour(Colour::SELECTHIGHLIGHT); - drawTextCentre(mytext, area.w / 2, 0, Colour::DARKTEXT); - } - else - { - fillColour(Colour::BUTTONBACKGROUND); - drawTextCentre(mytext, area.w / 2, 0, Colour::LIGHTTEXT); - } -} - -void WButton::setTag(int newTag) -{ - tag = newTag; -} - -int WButton::getTag() -{ - return tag; -} - -// Sorry, I've broken these in the boxx upgrade - chris - -bool WButton::mouseMove(int x, int y) -{ - if ((x-getRootBoxOffsetX())>=0 && (y-getRootBoxOffsetY())>=0 - && (x-getRootBoxOffsetX())<=(int)area.w && (y-getRootBoxOffsetY())<=(int)area.h && !active) - { - setActive(1); - return true; - } - return false; -} - -bool WButton::mouseLBDOWN(int x, int y) -{ - if ((x-getRootBoxOffsetX())>=0 && (y-getRootBoxOffsetY())>=0 - && (x-getRootBoxOffsetX())<=(int)area.w && (y-getRootBoxOffsetY())<=(int)area.h && active) - { - return true; - } - return false; -} +/* + 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. +*/ + +#include "wbutton.h" + +#include "colour.h" + +WButton::WButton() +{ + + setSize(70, 21/*fontHeight*/); + + mytext = NULL; + active = 0; + tag = 0; + dimmed = false; +} + +WButton::~WButton() +{ + if (mytext) delete[] mytext; +} + +void WButton::setText(const char* takeText) +{ + int length = strlen(takeText); + mytext = new char[length + 1]; + strcpy(mytext, takeText); +} + +void WButton::setActive(UCHAR tactive) +{ + dimmed = false; + active = tactive; +} + +void WButton::dim() +{ + // a bolt on for now - an active but dimmed state + dimmed = true; + active = false; +} + +void WButton::draw() +{ + if (dimmed) + { + fillColour(Colour::BLACK); + drawTextCentre(mytext, area.w / 2, 0, Colour::SELECTHIGHLIGHT); + } + else if (active) + { + fillColour(Colour::SELECTHIGHLIGHT); + drawTextCentre(mytext, area.w / 2, 0, Colour::DARKTEXT); + } + else + { + fillColour(Colour::BUTTONBACKGROUND); + drawTextCentre(mytext, area.w / 2, 0, Colour::LIGHTTEXT); + } +} + +void WButton::setTag(int newTag) +{ + tag = newTag; +} + +int WButton::getTag() +{ + return tag; +} + +// Sorry, I've broken these in the boxx upgrade - chris + +bool WButton::mouseMove(int x, int y) +{ + if ((x-getRootBoxOffsetX())>=0 && (y-getRootBoxOffsetY())>=0 + && (x-getRootBoxOffsetX())<=(int)area.w && (y-getRootBoxOffsetY())<=(int)area.h && !active) + { + setActive(1); + return true; + } + return false; +} + +bool WButton::mouseLBDOWN(int x, int y) +{ + if ((x-getRootBoxOffsetX())>=0 && (y-getRootBoxOffsetY())>=0 + && (x-getRootBoxOffsetX())<=(int)area.w && (y-getRootBoxOffsetY())<=(int)area.h && active) + { + return true; + } + return false; +} diff --git a/wbutton.h b/wbutton.h index 35febac..7e0b7ca 100644 --- a/wbutton.h +++ b/wbutton.h @@ -31,7 +31,7 @@ class WButton : public Boxx { public: WButton(); - ~WButton(); + virtual ~WButton(); void setText(const char* text); void setActive(UCHAR tactive); void dim(); diff --git a/wjpeg.cc b/wjpeg.cc index 2d6b387..6e0993b 100644 --- a/wjpeg.cc +++ b/wjpeg.cc @@ -1,877 +1,60 @@ -/* - 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. -*/ -#include "boxx.h" -#include "wjpeg.h" -#include -#include -#include -#ifndef WIN32 -#include -#else - -#endif - -#include "i18n.h" -#include "log.h" -#include "surface.h" -extern "C" -{ - #include -} - - -//#define USE_BUFFER -//#define EXTENDED_JPEGLIB - -//a struct to store user data for the jpeg decompressor -class jpegUserData{ - public: - WJpeg::JpegControl * ctl; - JpegReader *reader; - jpegUserData() { - ctl=NULL; - reader=NULL; - } - }; - -//the scale factors supported by the jpeg lib - -struct scale{ - UINT num; - UINT denom; -}; - - -struct scale jpegFactors[]={ -#ifndef EXTENDED_JPEGLIB - {1,1},{1,2},{1,4},{1,8} -#else - {1,1},{7,8},{3,4},{5,8},{1,2},{3,8},{1,4},{1,8} -#endif -}; - - -#ifdef WIN32 -#define LocalReader WindowsResourceJpegReader -class WindowsResourceJpegReader: public JpegReader { - public: - virtual ULONG initRead(const char *filename); - virtual ULONG readChunk(ULONG offset,ULONG len,char **buf); - virtual ULONG getSize(); - virtual ~WindowsResourceJpegReader(); -protected: - HGLOBAL hres; - LPVOID buffer; - DWORD size; -}; - -ULONG WindowsResourceJpegReader::initRead(const char *filename) -{ - HRSRC res=FindResource(NULL,filename,RT_RCDATA); - hres=LoadResource(NULL,res); - buffer=LockResource(hres); - size=SizeofResource(NULL,res); - //CloseHandle(hres); - return size; -} - - ULONG WindowsResourceJpegReader::readChunk(ULONG offset,ULONG len,char **buf) -{ - if (offset>size) return 0; - ULONG toread=min(size-offset,len); - char* buffy=(char*)malloc(toread); - memcpy(buffy,((char*)buffer)+offset,toread); - *buf=buffy; - return toread; -} - - WindowsResourceJpegReader::~WindowsResourceJpegReader(){ - buffer=NULL; - size=0; - FreeResource(hres); - } - - ULONG WindowsResourceJpegReader::getSize(){ - return (ULONG)size; -} -#else - -#define LocalReader LocalJpegReader -class LocalJpegReader: public JpegReader { - public: - virtual ULONG initRead(const char *filename); - virtual ULONG readChunk(ULONG offset,ULONG len,char **buf); - virtual ~LocalJpegReader(); - virtual ULONG getSize(); - LocalJpegReader(); -protected: - FILE *file; - ULONG size; -}; - -LocalJpegReader::LocalJpegReader(){ - file=NULL; - size=0; -} - -ULONG LocalJpegReader::initRead(const char *filename) -{ - if (file) fclose(file); - size=0; - file=fopen(filename,"r"); - if (file) { - struct stat st; - if (fstat(fileno(file), &st) != 0) return 0; - size= st.st_size; - return size; - } - Log::getInstance()->log("WJepg", Log::ERR, "localReader unable to open File %s", filename); - return 0; -} - -ULONG LocalJpegReader::readChunk(ULONG offset,ULONG len,char **buf) -{ - *buf=NULL; - ULONG bread=0; - if (file) { - ULLONG cpos=ftell(file); - if (offset != cpos) { - fseek(file,offset-cpos,SEEK_CUR); - } - if (offset != (ULONG)ftell(file)) { - Log::getInstance()->log("WJepg", Log::ERR, "readChunk pos = %lu not available", offset); - } - else { - *buf=(char *)malloc(len); - if ( ! (*buf)) { - Log::getInstance()->log("WJepg", Log::ERR, "readChunk pos = %lu not available", offset); - } - else { - bread=fread(*buf,1,len,file); - } - } - } - return bread; -} - -LocalJpegReader::~LocalJpegReader(){ - if (file) fclose(file); - file=NULL; -} -ULONG LocalJpegReader::getSize(){ - return size; -} -#endif - -/*---------------------------------------------------------------- - the jpeg lib routines - ---------------------------------------------------------------- - */ - -extern "C" { - - -struct my_error_mgr { - struct jpeg_error_mgr pub; /* "public" fields */ - - jmp_buf setjmp_buffer; /* for return to caller */ -}; - -typedef struct my_error_mgr * my_error_ptr; - -/* - * Here's the routine that will replace the standard error_exit method: - */ - -METHODDEF(void) -my_error_exit (j_common_ptr cinfo) -{ - /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */ - my_error_ptr myerr = (my_error_ptr) cinfo->err; - - /* Always display the message. */ - /* We could postpone this until after returning, if we chose. */ - (*cinfo->err->output_message) (cinfo); - /* Return control to the setjmp point */ - longjmp(myerr->setjmp_buffer, 1); -} - -ULONG jpeg_call_reader(ULONG offset,ULONG size,char ** buf,void *cb) { - jpegUserData *user=(jpegUserData *)cb; - return user->reader->readChunk(offset,size,buf); -} -//the memory buffer reader for the jpeg lib -//taken from jdatasrc.c - -#include "jinclude.h" -#include "jpeglib.h" -#include "jerror.h" - - -typedef struct { - struct jpeg_source_mgr pub; /* public fields */ - - JOCTET * buffer; /* start of buffer */ - boolean start_of_file; /* have we gotten any data yet? */ - void * userdata; /* used for callback */ - ULONG offset; -} my_source_mgr; - -typedef my_source_mgr * my_src_ptr; - -#define INPUT_BUF_SIZE (64*4096) /* choose an efficiently fread'able size */ - - -/* - * Initialize source --- called by jpeg_read_header - * before any data is actually read. - */ - -METHODDEF(void) -linit_source (j_decompress_ptr cinfo) -{ - my_src_ptr src = (my_src_ptr) cinfo->src; - - /* We reset the empty-input-file flag for each image, - * but we don't clear the input buffer. - * This is correct behavior for reading a series of images from one source. - */ - src->start_of_file = TRUE; - src->offset=0; -} - - -/* - * Fill the input buffer --- called whenever buffer is emptied. - * - * In typical applications, this should read fresh data into the buffer - * (ignoring the current state of next_input_byte & bytes_in_buffer), - * reset the pointer & count to the start of the buffer, and return TRUE - * indicating that the buffer has been reloaded. It is not necessary to - * fill the buffer entirely, only to obtain at least one more byte. - * - * There is no such thing as an EOF return. If the end of the file has been - * reached, the routine has a choice of ERREXIT() or inserting fake data into - * the buffer. In most cases, generating a warning message and inserting a - * fake EOI marker is the best course of action --- this will allow the - * decompressor to output however much of the image is there. However, - * the resulting error message is misleading if the real problem is an empty - * input file, so we handle that case specially. - * - * In applications that need to be able to suspend compression due to input - * not being available yet, a FALSE return indicates that no more data can be - * obtained right now, but more may be forthcoming later. In this situation, - * the decompressor will return to its caller (with an indication of the - * number of scanlines it has read, if any). The application should resume - * decompression after it has loaded more data into the input buffer. Note - * that there are substantial restrictions on the use of suspension --- see - * the documentation. - * - * When suspending, the decompressor will back up to a convenient restart point - * (typically the start of the current MCU). next_input_byte & bytes_in_buffer - * indicate where the restart point will be if the current call returns FALSE. - * Data beyond this point must be rescanned after resumption, so move it to - * the front of the buffer rather than discarding it. - */ - -METHODDEF(boolean) -lfill_input_buffer (j_decompress_ptr cinfo) -{ - my_src_ptr src = (my_src_ptr) cinfo->src; - size_t nbytes; - if (src->buffer) free(src->buffer); - src->buffer=NULL; - nbytes = jpeg_call_reader(src->offset, INPUT_BUF_SIZE,(char **)&(src->buffer), src->userdata); - - if (nbytes <= 0) { - WARNMS(cinfo, JWRN_JPEG_EOF); - src->buffer = (JOCTET *)malloc(2); - src->buffer[0] = (JOCTET) 0xFF; - src->buffer[1] = (JOCTET) JPEG_EOI; - nbytes = 2; - - } - src->offset+=nbytes; - - src->pub.next_input_byte = src->buffer; - src->pub.bytes_in_buffer = nbytes; - src->start_of_file = FALSE; - - return TRUE; -} - - -/* - * Skip data --- used to skip over a potentially large amount of - * uninteresting data (such as an APPn marker). - * - * Writers of suspendable-input applications must note that skip_input_data - * is not granted the right to give a suspension return. If the skip extends - * beyond the data currently in the buffer, the buffer can be marked empty so - * that the next read will cause a fill_input_buffer call that can suspend. - * Arranging for additional bytes to be discarded before reloading the input - * buffer is the application writer's problem. - */ - -METHODDEF(void) -lskip_input_data (j_decompress_ptr cinfo, long num_bytes) -{ - my_src_ptr src = (my_src_ptr) cinfo->src; - - /* Just a dumb implementation for now. Could use fseek() except - * it doesn't work on pipes. Not clear that being smart is worth - * any trouble anyway --- large skips are infrequent. - */ - if (num_bytes > 0) { - while (num_bytes > (long) src->pub.bytes_in_buffer) { - num_bytes -= (long) src->pub.bytes_in_buffer; - (void) lfill_input_buffer(cinfo); - /* note we assume that fill_input_buffer will never return FALSE, - * so suspension need not be handled. - */ - } - src->pub.next_input_byte += (size_t) num_bytes; - src->pub.bytes_in_buffer -= (size_t) num_bytes; - } -} - - -/* - * An additional method that can be provided by data source modules is the - * resync_to_restart method for error recovery in the presence of RST markers. - * For the moment, this source module just uses the default resync method - * provided by the JPEG library. That method assumes that no backtracking - * is possible. - */ - - -/* - * Terminate source --- called by jpeg_finish_decompress - * after all data has been read. Often a no-op. - * - * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding - * application must deal with any cleanup that should happen even - * for error exit. - */ - -METHODDEF(void) -lterm_source (j_decompress_ptr cinfo) -{ - /* no work necessary here */ -} - - -/* - * Prepare for input from a stdio stream. - * The caller must have already opened the stream, and is responsible - * for closing it after finishing decompression. - */ - -extern "C" void -jpeg_memio_src (j_decompress_ptr cinfo, void * userdata) -{ - my_src_ptr src; - - /* The source object and input buffer are made permanent so that a series - * of JPEG images can be read from the same file by calling jpeg_stdio_src - * only before the first one. (If we discarded the buffer at the end of - * one image, we'd likely lose the start of the next one.) - * This makes it unsafe to use this manager and a different source - * manager serially with the same JPEG object. Caveat programmer. - */ - if (cinfo->src == NULL) { /* first time for this JPEG object? */ - cinfo->src = (struct jpeg_source_mgr *) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, - SIZEOF(my_source_mgr)); - src = (my_src_ptr) cinfo->src; - src->buffer = NULL; - } - - src = (my_src_ptr) cinfo->src; - src->pub.init_source = linit_source; - src->pub.fill_input_buffer = lfill_input_buffer; - src->pub.skip_input_data = lskip_input_data; - src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ - src->pub.term_source = lterm_source; - src->userdata=userdata; - src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ - src->pub.next_input_byte = NULL; /* until buffer loaded */ - src->offset=0; - src->userdata=userdata; - if (src->buffer) { - free(src->buffer); - src->buffer=NULL; - } -} -/* cleanup to be called before cleanup of cinfo*/ -extern "C" void -jpeg_memio_cleanup (j_decompress_ptr cinfo) { - my_src_ptr src=(my_src_ptr)cinfo->src; - Log::getInstance()->log("BJpeg", Log::DEBUG, "cleanup src, src=%p, buf=%p",src,(src?src->buffer:0)); - if (src && src->buffer) { - free(src->buffer); - src->buffer=NULL; - } -} - -//taken from mvpmc -//http://git.mvpmc.org/cgi-bin/gitweb.cgi?p=mvpmc.git;a=blob_plain;h=02d4354c0cbbed802a9aa1478918a49fcbf61d00;f=libs/libwidget/image_jpeg.c - -#define GET2BYTES(cinfo, V, swap, offset) do { \ - if (cinfo->src->bytes_in_buffer == 0) (*mysrc->pub.fill_input_buffer)(cinfo); \ - cinfo->src->bytes_in_buffer--; \ - V = (*cinfo->src->next_input_byte++) << (swap?0:8); \ - if (cinfo->src->bytes_in_buffer == 0) (*mysrc->pub.fill_input_buffer)(cinfo); \ - cinfo->src->bytes_in_buffer--; \ - V += (*cinfo->src->next_input_byte++) << (swap?8:0); \ - offset += 2; } while(0) - -#define GET4BYTES(cinfo, V, swap, offset) do { \ - if (cinfo->src->bytes_in_buffer == 0) (*mysrc->pub.fill_input_buffer)(cinfo); \ - cinfo->src->bytes_in_buffer--; \ - V = (*cinfo->src->next_input_byte++) << (swap?0:24); \ - if (cinfo->src->bytes_in_buffer == 0) (*mysrc->pub.fill_input_buffer)(cinfo); \ - cinfo->src->bytes_in_buffer--; \ - V += (*cinfo->src->next_input_byte++) << (swap?8:16); \ - if (cinfo->src->bytes_in_buffer == 0) (*mysrc->pub.fill_input_buffer)(cinfo); \ - cinfo->src->bytes_in_buffer--; \ - V += (*cinfo->src->next_input_byte++) << (swap?16:8); \ - if (cinfo->src->bytes_in_buffer == 0) (*mysrc->pub.fill_input_buffer)(cinfo); \ - cinfo->src->bytes_in_buffer--; \ - V += (*cinfo->src->next_input_byte++) << (swap?24:0); \ - offset += 4; } while(0) - -static boolean -get_exif_orient (j_decompress_ptr cinfo) -/* Get the Exif orientation info */ -{ - unsigned int tmp, offset, length, numtags; - int orient=-1; - jpegUserData * ud=0; - boolean swap; - orient = 1; - offset = 0; - my_src_ptr mysrc = (my_src_ptr) cinfo->src; - - /* marker length */ - GET2BYTES(cinfo, length, 0, offset); - if (length<8) goto err; - /* Exif header */ - GET4BYTES(cinfo, tmp, 0, offset); - if (tmp != 0x45786966) goto err; - GET2BYTES(cinfo, tmp, 0, offset); - if (tmp != 0x0000) goto err; - /* Byte-order */ - GET2BYTES(cinfo, tmp, 0, offset); - if (tmp == 0x4949) swap = 1; - else if (tmp == 0x4d4d) swap = 0; - else goto err; - GET2BYTES(cinfo, tmp, swap, offset); - if (tmp != 0x002A) goto err; - /* offset to first IFD */ - GET4BYTES(cinfo, tmp, swap, offset); - offset += tmp-8; - (*mysrc->pub.skip_input_data)(cinfo, tmp-8); - /* number of tags in IFD */ - GET2BYTES(cinfo, numtags, swap, offset); - if (numtags == 0) goto err; - - /* Search for Orientation Tag in IFD0 */ - for (;;) { - if (offset > length-12) goto err; - GET2BYTES(cinfo, tmp, swap, offset); - if (tmp == 0x0112) break; /* found Orientation Tag */ - if (--numtags == 0) goto err; - offset += 10; - (*mysrc->pub.skip_input_data)(cinfo, 10); - } - offset += 6; - (*mysrc->pub.skip_input_data)(cinfo, 6); - GET2BYTES(cinfo, orient, swap, offset); - if( orient==0 || orient>8 ) orient = 1; - - Log::getInstance()->log("WJpeg", Log::DEBUG, "read exif orientation %u", orient); - ud=(jpegUserData *)(mysrc->userdata); - switch(orient) { - case 3: - ud->ctl->exifRotation=WJpeg::ROT_180; - break; - case 6: - ud->ctl->exifRotation=WJpeg::ROT_90; - break; - case 8: - ud->ctl->exifRotation=WJpeg::ROT_270; - break; - } - -err: - (*mysrc->pub.skip_input_data)(cinfo, length-offset); - return TRUE; -} -} - -/*---------------------------------------------------------------- - the implementation - ---------------------------------------------------------------- - */ - - -WJpeg::WJpeg(){ - reader=NULL; - owningReader=false; - errbuf[0]=0; -} - -WJpeg::~WJpeg() { - if (owningReader && reader) delete reader; -} - -int WJpeg::init(char* tfileName) -{ - if (owningReader && reader) delete reader; - errbuf[0]=0; //clean error state - LocalReader *myreader=new LocalReader(); - reader=myreader; - owningReader=true; - ULONG psize=myreader->initRead(tfileName); - if (psize == 0) { - delete reader; - reader=NULL; - owningReader=false; - SNPRINTF(errbuf,200,"unable to open %s",tfileName); - return 0; - } - return 1; -} - - - - -bool WJpeg::hasError() { - return (errbuf[0] != 0); -} -void WJpeg::draw() -{ - bool ok=false; - JpegControl ctl; - if (reader) { - Region myRegion; - Surface *sfc=getSurface(myRegion); - myRegion.w=area.w; - myRegion.h=area.h; - ctl.area=myRegion; - ctl.enlarge=true; - if (drawJpeg(&ctl,sfc,reader,backgroundColour) ) ok=true; - } - else { - SNPRINTF(errbuf,200,"jpeg reader not initialized"); - } - if (! ok) { - drawTextCentre(tr("Jpeg ERROR"), 240, 170, Colour::LIGHTTEXT); - if (errbuf[0] != 0) drawTextCentre(errbuf,240,200, Colour::LIGHTTEXT); - if (ctl.error[0] != 0) drawTextCentre(ctl.error,240,230, Colour::LIGHTTEXT); - } -} - -/** - the main drawing function - this will read the pciture via the reader - and draw directly into the surface - it will compute an appropriate scale and set the infos in the - JpegControl structure -**/ - -bool WJpeg::drawJpeg(JpegControl * ctl,Surface * sfc,JpegReader *rdr, Colour & backgroundColour) { - Log* logger = Log::getInstance(); - if (! rdr || ! sfc) { - logger->log("BJpeg", Log::ERR, "JPEG error rdr=NULL or sfc=NULL"); - return false; - } - logger->log("BJpeg", Log::DEBUG, "draw Jpeg Started sfc=%p, rdr=%p",sfc,rdr); - unsigned char* buffer =NULL; - struct jpeg_decompress_struct cinfo; - struct my_error_mgr jerr; - cinfo.err = jpeg_std_error(&jerr.pub); - jerr.pub.error_exit = my_error_exit; - /* Establish the setjmp return context for my_error_exit to use. */ - if (setjmp(jerr.setjmp_buffer)) { - /* If we get here, the JPEG code has signaled an error. - * We need to clean up the JPEG object, close the input file, and return. - */ - if (rdr) jpeg_memio_cleanup(&cinfo); - jpeg_destroy_decompress(&cinfo); - logger->log("BJpeg", Log::ERR, "JPEG error"); - if (buffer) free(buffer); - return false; - } - jpegUserData userdata; - int xpos=0; - int ypos=0; - jpeg_create_decompress(&cinfo); - userdata.reader=rdr; - userdata.ctl=ctl; - ctl->exifRotation=ROT_0; - ctl->error[0]=0; - ctl->exifDate[0]=0; - //create the input for the jpeg lib - jpeg_memio_src(&cinfo,(void *)(&userdata)); - //processor for EXIF data - jpeg_set_marker_processor(&cinfo, JPEG_APP0+1, get_exif_orient); - //read in header info - jpeg_read_header(&cinfo, TRUE); - ctl->picw=cinfo.image_width; - ctl->pich=cinfo.image_height; - ctl->compressedSize=rdr->getSize(); - //now we have important info available in ctl (pictuerw,h, exif orientation,size) - //compute rotation due to the defined enum values we can simply add them - ctl->finalRotation=(enum Rotation)((ctl->rotation+ctl->exifRotation)%4); - logger->log("BJpeg", Log::DEBUG, "JPEG read header w=%i h=%i, rot=%i", ctl->picw,ctl->pich,ctl->finalRotation); - //now we have to compute the scale - //input: ctl->picw,ctl->pich,ctl->finalRotation,ctl->mode,ctl->scaleAmount, ctl->scaleafter - // list of available jpeg scale factors - //out: scalenum,scaledenom,scaleafter - - UINT picturew=ctl->picw; - UINT pictureh=ctl->pich; - switch (ctl->finalRotation){ - case ROT_90: - case ROT_270: - pictureh=ctl->picw; - picturew=ctl->pich; - break; - default: - break; - } - UINT scalenum=1; - UINT scaledenom=1; - UINT scaleafter=1; - if (! ctl->enlarge) { - //scale - compute the factors to fit 100% - int scalew=1000*picturew/ctl->area.w; - int scaleh=1000*pictureh/ctl->area.h; - int scale=scaleh; - if (scalew > scaleh) scale=scalew; - - //OK now find out which is the optimal setting - //rule: find settings being nearest to: - // mode=LETTER - smaller or equal screen size (i.e. scale*scalefactor <= 1000) - // mode=CROP - bigger or equal screen size (i.e. scale *scalefactor>= 1000) - // mode=CROPPERCENT - smaller or equal screensize*scaleamount (i.e. scale*scalefactor<= 1000*scaleamount) - // scaleamount is in % - so in reality we use scaleamount*10 instead scaleamount*1000 - //the scalefactor computes as scalenum/(scaledenom*scaleafter) - scaledenom=8; - int minDiff=1000; - logger->log("BJpeg", Log::DEBUG, "start scaling screenw=%u, screenh=%u, pw=%u,ph=%u, scale=%d, mode=%d, %%=%u, after=%u", - ctl->area.w,ctl->area.h,picturew,pictureh,scale,(int)ctl->mode,ctl->scaleAmount, ctl->scaleafter); - for (UINT j=0;jscaleafter;sa++) { - int curf=(scale*jpegFactors[j].num)/(jpegFactors[j].denom*sa); - bool setThis=false; - logger->log("BJpeg", Log::DEBUG, "testing scale curf=%d,num=%u,denom=%u,after=%u,minDiff=%d", - curf,jpegFactors[j].num,jpegFactors[j].denom,sa,minDiff); - switch(ctl->mode) { - case CROP: - if (curf >= 1000 && curf < (minDiff +1000)) { - setThis=true; - minDiff=curf-1000; - } - break; - case LETTER: - if (curf <= 1000 && curf > (1000-minDiff)) { - setThis=true; - minDiff=1000-curf; - } - break; - case CROPPERCENT: - if (curf <= 10*(int)ctl->scaleAmount ) { - int abs=curf-1000; - if (abs < 0) abs=-abs; - if (abs < minDiff) { - setThis=true; - minDiff=abs; - } - } - break; - } - if (setThis) { - logger->log("BJpeg", Log::DEBUG, "testing scale curf=%d,take this",curf); - scalenum=jpegFactors[j].num; - scaledenom=jpegFactors[j].denom; - scaleafter=sa; - } - } - } - ctl->scale=100*scalenum/(scaledenom*scaleafter); - - logger->log("BJpeg", Log::DEBUG, "JPEG scaling found scale=%i num=%i denom=%i after=%i result=%i scale=%i%% ", - scale,scalenum,scaledenom,scaleafter,scale*ctl->scale/100,ctl->scale); - - cinfo.scale_denom=scaledenom; - cinfo.scale_num=scalenum; - } - else - { - //set drawing area according to picture - ctl->area.w=ctl->picw; - ctl->area.h=ctl->pich; - } - - //now we know the scaling - //compute the scaled size and position - - jpeg_start_decompress(&cinfo); - //picturew/h is now the output width from the decompressor and afterscaler - //this is unrotated - picturew=cinfo.output_width; - pictureh=cinfo.output_height; - if (scaleafter > 1) { - picturew=picturew/scaleafter; - pictureh=pictureh/scaleafter; - } - //if our image is smaller - center it - if (! ctl->enlarge) { - if (ctl->finalRotation == ROT_90 || ctl->finalRotation == ROT_270) { - int dim=pictureh; - xpos=(((int)ctl->area.w)-dim)/2; - dim=picturew; - ypos=(((int)ctl->area.h)-dim)/2; - } else { - int dim=picturew; - xpos=(((int)ctl->area.w)-dim)/2; - dim=pictureh; - ypos=(((int)ctl->area.h)-dim)/2; - } - if (xpos <0) xpos=0; - if (ypos <0) ypos=0; - } - xpos+=ctl->area.x; - ypos+=ctl->area.y; - //remember the jpeg dimensions for computing the buffer - UINT jpegwidth=cinfo.output_width; - UINT jpegheight=cinfo.output_height; - logger->log("BJpeg", Log::DEBUG, "JPEG startup done pw=%i ph=%i, xo=%i,yo=%i, jw=%i, jh=%i, rot=%d", - picturew, pictureh,xpos,ypos,jpegwidth,jpegheight,(int)ctl->finalRotation*90); - - //fill the background - sfc->fillblt(ctl->area.x,ctl->area.y,ctl->area.w,ctl->area.h,backgroundColour.rgba()); - - //line len in bytes (3 bytes per Pixel) - for buffer (can be bigger then surface) - int linelen=jpegwidth*3; -#ifdef USE_BUFFER - // MAKE THE 2D ARRAY - buffer = (unsigned char*)malloc(jpegheight * linelen); - logger->log("BJpeg", Log::DEBUG, "Buffer allocated at %p, lines = %i linelen = %i", buffer, jpegheight, linelen); - if (buffer == NULL) { - if (rdr) jpeg_memio_cleanup(&cinfo); - jpeg_destroy_decompress(&cinfo); - logger->log("BJpeg", Log::ERR, "JPEG error - no room for buffer"); - SNPRINTF(ctl->error,100,"no room for buffer"); - return false; - } -#endif - -#ifndef USE_BUFFER - //unsigned char lbuf[linelen]; - unsigned char *lbuf=new unsigned char[linelen*scaleafter]; - unsigned char * ptr=lbuf; - UINT outy=0; -#else - unsigned char * ptr=buffer; -#endif - - int rowsread = 0; - - Colour c; - sfc->startFastDraw();//Tell the surface, that we will draw a lot of pixel, - //so that performance, can be optimized - logger->log("BJpeg", Log::DEBUG, "start drawing "); - UINT colincr=0; - //factors to base 1024 - UINT fac=1024; - if (scaleafter > 1) { - colincr=3*scaleafter-3; - fac=1024/(scaleafter*scaleafter); - } - logger->log("BJpeg", Log::DEBUG, "jpeg draw scale %d image: %u %u, picture: %u %u", scaleafter,picturew,pictureh,jpegwidth,jpegheight); - while (cinfo.output_scanline < jpegheight) - { -// logger->log("BJpeg", Log::DEBUG, "%i", rowsread); - rowsread += jpeg_read_scanlines(&cinfo,&ptr,1); -#ifdef USE_BUFFER - ptr+=linelen; -#else - if (scaleafter > 1) { - if (rowsread % scaleafter != scaleafter-1) { - //this simple approach wold maybe forget scaleafter -1 lines at the end... - ptr+=linelen; - continue; - } - ptr=lbuf; - } - drawLine(sfc,ctl->finalRotation,ptr,scaleafter,picturew,pictureh,xpos,ypos,outy,linelen,colincr,scaleafter,fac); - outy++; - -#endif - } - sfc->endFastDraw(); - - logger->log("BJpeg", Log::DEBUG, "Done all jpeg_read"); - - jpeg_finish_decompress(&cinfo); - jpeg_memio_cleanup(&cinfo); - jpeg_destroy_decompress(&cinfo); - - logger->log("BJpeg", Log::DEBUG, "jpeg shutdown done"); - rdr->drawingDone(); - -#ifdef USE_BUFFER - UINT y; - //Tell the surface, that we will draw a lot of pixel, - //so that performance, can be optimized - sfc->startFastDraw(); - logger->log("BJpeg", Log::DEBUG, "jpeg start buffer draw" ); - unsigned char* p=buffer; //start of first row - UINT rowincr=linelen*scaleafter; - //for simplicity omit last line to avoid running out of buffer - for (y = 0; y < pictureh-1 ;y++) - { - drawLine(sfc,ctl->finalRotation,p,scaleafter,picturew,pictureh,xpos,ypos,y,linelen,colincr,scaleafter,fac); - p+=rowincr; - } - sfc->endFastDraw(); - logger->log("BJpeg", Log::DEBUG, "end draw"); - free(buffer); -#else - delete[] lbuf; -#endif - logger->log("BJpeg", Log::DEBUG, "deleted buffer"); - return true; -} - -//get my own surface -Surface * WJpeg::getSurface(Region & r) { - r.x=getRootBoxOffsetX(); - r.y=getRootBoxOffsetY(); - r.w=area.w; - r.h=area.h; - return Boxx::getSurface(); -} - - +/* + 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. +*/ +#include "boxx.h" +#include "wjpeg.h" +#include +#include +#include + +#ifndef WIN32 +#include +#else + +#endif + +#include "i18n.h" +#include "log.h" +#include "surface.h" + + +/*---------------------------------------------------------------- + the implementation + ---------------------------------------------------------------- + */ + + +WJpeg::WJpeg(){ + +} + +WJpeg::~WJpeg() { +} + + + + + + +void WJpeg::draw() +{ + +} + + diff --git a/wjpeg.h b/wjpeg.h index 154cfc9..4de5478 100644 --- a/wjpeg.h +++ b/wjpeg.h @@ -1,231 +1,59 @@ -/* - 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 WJPEG_H -#define WJPEG_H - -#include -#include - -#ifdef WIN32 - -#include -#include - - -//#define NEED_FAR_POINTERS -#define XMD_H //workaround some compiling issues -#endif - -class Surface; -class Boxx; - -//a reader to be implemented by the caller -class JpegReader { - public: - //read the next chunk of jpeg data - //offset - from start of file - //len I buf len (max bytes to read) - //return read len, 0 on EOF, -1 on error, *buf set to buffer - //will be released with free(!!!) after decoding - virtual ULONG readChunk(ULONG offset,ULONG len,char **buf)=0; - //a callback when the drawing is complete - //the implementation is optional - virtual void drawingDone(){}; - //get the size of the current picture - virtual ULONG getSize(){ return 0;} - virtual ~JpegReader(){}; -}; -class WJpeg : public Boxx -{ - public: - - // temp for boxx - void setDimensions(int width, int height) {area.w=width;area.h=height;}; - - - WJpeg(); - virtual ~WJpeg(); - //old style usage - load local file - //the sequence is init(filename), draw - //the new usage is drawJpeg (with having the right offsets computed) - int init(char* fileName); - void draw(); - - bool hasError(); - - //mode for scaling pictures - enum ScaleMode { - LETTER=0, - CROP=1, - CROPPERCENT=2}; - //rotations - enum Rotation{ - ROT_0=0, - ROT_90=1, - ROT_180=2, - ROT_270=3 - }; - //jpeg info - struct JpegControl { - public: - //the available drawing area - Region area; - bool enlarge; - //the maximum allowed scale factor after decompress - UINT scaleafter; - //the scale mode - enum ScaleMode mode; - //the size value if scaleMode==cropPercent - //%of the drawing area size - UINT scaleAmount; - //the rotation (user defined as input) - //if exif rotation is found this will be added - enum Rotation rotation; - - //paremeters filled during Jpeg parsing - enum Rotation exifRotation; - char exifDate[30]; - char error[100]; - UINT picw; - UINT pich; - ULONG compressedSize; - - //parameters computed to display picture - enum Rotation finalRotation; - //scale in % - UINT scale; - JpegControl() { - area.x=0; - area.y=0; - area.w=0; - area.h=0; - enlarge=false; - scaleafter=3; - scaleAmount=110; - mode=CROPPERCENT; - rotation=ROT_0; - exifRotation=ROT_0; - finalRotation=ROT_0; - exifDate[0]='\0'; - error[0]='\0'; - picw=0; - pich=0; - compressedSize=0; - scale=100; - } - }; - - //the standalone drawing function - //this will draw into the provided surface - //the reader has to be initialized before - //calling this function does not need a WJpeg being instantiated - //it simply draws into the surface - bool static drawJpeg(JpegControl * control, Surface* sfc, JpegReader *rdr, Colour & backgroundColour); - - private: - //our drawPixel with considers rotation - /* handle picture rotation - 90: xr=h-y - yr=x - 180:xr=w-x - yr=h-y - 270:xr=y - yr=w-x - */ - inline static void drawPixel(Surface * sfc,enum Rotation rotate,int x, int y,int w,int h,int xpos, int ypos,Colour c){ - int xb=0; - int yb=0; - switch(rotate) { - case ROT_0: - xb=x; - yb=y; - break; - case ROT_90: - xb=h-y; - yb=x; - break; - case ROT_180: - xb=w-x; - yb=h-y; - break; - case ROT_270: - xb=y; - yb=w-x; - break; - } - xb+=xpos; - yb+=ypos; - if (xb < 0 || yb < 0 ) { - return; - } - sfc->drawPixel((UINT)xb,(UINT)yb,c,true); - } - - /** - draw a line of pixels coming from the decompressor - if scaleafter > 1 we draw that many lines (numlines is the# lines in the buffer) - picturew is the resulting width of the picture - **/ - inline static void drawLine(Surface *sfc,enum Rotation rotate, unsigned char *cp,UINT scaleafter,UINT picturew,UINT pictureh, - UINT xpos, UINT ypos, UINT outy, UINT linelen,UINT pixeloffset, UINT numlines, UINT fac) { - Colour c; - for (UINT x = 0; x < picturew; x++) - { - if (scaleafter > 1 ) { - //boxfilter scalefactor*scalefactor - //take 0...scalefactor pixels in x and y direction - for (int colornum=0;colornum<3;colornum++) { - UINT comp=0; - unsigned char * accp=cp; - for (UINT rows=0;rows> 10; - if (colornum == 0) c.red=comp; - if (colornum == 1) c.green=comp; - if (colornum == 2) c.blue=comp; - cp++; - } - - } - else { - c.red = *cp;cp++; - c.green = *cp;cp++; - c.blue = *cp;cp++; - } - cp+=pixeloffset; - drawPixel(sfc,rotate,x, outy, picturew,pictureh,xpos,ypos,c); - } - } - //find my own surface and fill the area with my x and y offset within - Surface * getSurface(Region &a); - - JpegReader *reader; - bool owningReader; - char errbuf[200]; -}; - -#endif +/* + 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 WJPEG_H +#define WJPEG_H + +#include +#include +#include "boxx.h" + + + + +class WJpeg : public Boxx +{ + public: + + // temp for boxx + void setDimensions(int width, int height) {area.w=width;area.h=height;}; + + + WJpeg(); + virtual ~WJpeg(); + //old style usage - load local file + //the sequence is init(filename), draw + //the new usage is drawJpeg (with having the right offsets computed) + virtual int init(const char* fileName)=0; + virtual void draw(); + + +}; + + +#if !defined(WIN32) || !defined(__ANDROID) +#define WJpegTYPE WJpegSimple +#include "wjpegsimple.h" +#else +#define WJpegTYPE WJpegComplex +#include "wjpegcomplex.h" +#endif + +#endif diff --git a/wol.cc b/wol.cc index 22751c8..6b23680 100644 --- a/wol.cc +++ b/wol.cc @@ -24,14 +24,20 @@ #include #include + + +#if !defined(__ANDROID__) && !defined(WIN32) +#include +#else +#define ETH_ALEN 6 +#endif + + #ifndef WIN32 #include -#include #include #include #else -#define ETH_ALEN 6 - #include #include #include diff --git a/woptionbox.cc b/woptionbox.cc index b487ab8..6e43230 100644 --- a/woptionbox.cc +++ b/woptionbox.cc @@ -21,6 +21,7 @@ #include "woptionbox.h" #include "colour.h" +#include "log.h" #ifndef WIN32 #include "unistd.h" #endif @@ -31,11 +32,10 @@ WOptionBox::WOptionBox() options = NULL; active = 0; currentOption = 0; - mode = MODE_TEXT; textbox.setPosition(20, 0); - textbox.setSize(10, surface->getFontHeight()); + textbox.setSize(10, getFontHeight()); textbox.setParaMode(false); textbox.setTextPos(0, 0); add(&textbox); @@ -60,7 +60,7 @@ WOptionBox::~WOptionBox() void WOptionBox::setSize(UINT w, UINT h) { Boxx::setSize(w, h); - textbox.setSize(w - 40, surface->getFontHeight()); + textbox.setSize(w - 40, getFontHeight()); rightArrow.setPosition(w - 18, 2); } diff --git a/woptionpane.cc b/woptionpane.cc index 036ad12..00df782 100644 --- a/woptionpane.cc +++ b/woptionpane.cc @@ -60,18 +60,19 @@ void WOptionPane::saveOpts() void WOptionPane::addOptionLine(Option* option) { - int fontHeight = surface->getFontHeight(); + int fontHeight = getFontHeight(); + options.resize(numOptions+1); options[numOptions] = option; - + WTextbox* tb = new WTextbox(); tb->setPosition(4, 4 + (numOptions * 30)); tb->setSize(300, fontHeight); tb->setText(tr(option->displayText)); tb->setTextPos(0, 0); add(tb); - + textBoxes.resize(numOptions+1); textBoxes[numOptions] = tb; @@ -79,10 +80,10 @@ void WOptionPane::addOptionLine(Option* option) ob->setPosition(310, 4 + (numOptions * 30)); ob->setSize(190, fontHeight); add(ob); - + optionBoxes.resize(numOptions+1); optionBoxes[numOptions] = ob; - + if (option->optionType == Option::TYPE_TEXT || option->optionType == Option::TYPE_KEYED_TEXT) { diff --git a/wselectlist.cc b/wselectlist.cc index 707932d..73177f4 100644 --- a/wselectlist.cc +++ b/wselectlist.cc @@ -1,285 +1,305 @@ -/* - 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. -*/ - -#include "wselectlist.h" - -#include "colour.h" - -WSelectList::WSelectList() -{ - selectedOption = 0; - topOption = 0; - numOptionsDisplayable = 0; - numColumns = 0; - noLoop = 0; - gap = 1; - showseloption = true; - darkseloption = false; - backgroundColour = Colour::VIEWBACKGROUND; -} - -WSelectList::~WSelectList() -{ - clear(); -} - -void WSelectList::clear() -{ - int vsize = options.size(); - for (int i = 0; i < vsize; i++) - { - delete[] options[i].text; - } - options.clear(); - - selectedOption = 0; - topOption = 0; - numOptionsDisplayable = 0; - numColumns = 0; -} - -void WSelectList::setNoLoop() -{ - noLoop = 1; -} - -void WSelectList::setBackgroundColour(const Colour& colour) -{ - backgroundColour = colour; -} - -void WSelectList::hintSetCurrent(int idx) -{ - selectedOption = idx; - if (selectedOption >= options.size()) selectedOption = options.size() - 1; -} - -void WSelectList::hintSetTop(int idx) -{ - topOption = idx; -} - -int WSelectList::addOption(const char* text, ULONG data, int selected) -{ - int thisNewOption = options.size(); - - wsloption wslo; - wslo.text = new char[strlen(text) + 1]; - strcpy(wslo.text, text); - wslo.data = data; - options.push_back(wslo); - if (selected) selectedOption = thisNewOption; - return thisNewOption; -} - -void WSelectList::draw() -{ - int fontHeight = surface->getFontHeight(); - int ySeperation = fontHeight + gap; - - numOptionsDisplayable = (area.h - 5) / ySeperation; - - if (selectedOption == (topOption + numOptionsDisplayable)) topOption++; - if (selectedOption == ((UINT)topOption - 1)) topOption--; - // if still not visible... - if ((selectedOption < (UINT)topOption) || (selectedOption > (topOption + numOptionsDisplayable))) - { - topOption = selectedOption - (numOptionsDisplayable / 2); - } - - if (topOption < 0) topOption = 0; - - - fillColour(backgroundColour); - - UINT ypos = 5; - for (UINT i = topOption; i < (topOption + numOptionsDisplayable); i++) - { - if (i == options.size()) return; - if ((ypos + ySeperation) > area.h) break; - - if (i == selectedOption && showseloption) - { - rectangle(0, ypos, area.w, fontHeight, darkseloption ? Colour::SELECTDARKHIGHLIGHT: Colour::SELECTHIGHLIGHT); - drawOptionLine(options[i].text, 5, ypos, area.w - 5, Colour::DARKTEXT); - } - else - { - drawOptionLine(options[i].text, 5, ypos, area.w - 5, Colour::LIGHTTEXT); - } - ypos += ySeperation; - } -} - -void WSelectList::addColumn(int x) -{ - if (numColumns == 10) return; - columns[numColumns++] = x; -} - -void WSelectList::drawOptionLine(char* text, int xpos, int ypos, int width, const Colour& colour) -{ - if (!numColumns) - { - drawText(text, xpos, ypos, width, colour); - } - else - { - char buffer[200]; - strncpy(buffer, text, 199); - int currentColumn = 0; - char* pointer; - - pointer = strtok(buffer, "\t"); - while(pointer) - { - drawText(pointer, xpos + columns[currentColumn], ypos, width - columns[currentColumn], colour); - currentColumn++; - if (currentColumn == 10) return; - pointer = strtok(NULL, "\t"); - } - } -} - -void WSelectList::up() -{ - if (selectedOption > 0) - { - selectedOption--; - } - else - { - if (!noLoop) selectedOption = options.size() - 1; - } -} - -void WSelectList::down() -{ - if (selectedOption < options.size() - 1) - { - selectedOption++; - } - else - { - if (!noLoop) selectedOption = 0; - } -} - -void WSelectList::pageUp() -{ - topOption -= numOptionsDisplayable; - if (topOption < 0) topOption = 0; - - selectedOption = topOption; -} - -void WSelectList::pageDown() -{ - if ((topOption + numOptionsDisplayable) >= options.size()) - { - selectedOption = options.size() - 1; - } - else - { - topOption += numOptionsDisplayable; - selectedOption = topOption; - } -} - -int WSelectList::getTopOption() -{ - return topOption; -} - -int WSelectList::getNumOptions() -{ - return options.size(); -} - -int WSelectList::getBottomOption() -{ - UINT retval = topOption + numOptionsDisplayable; - if (retval > options.size()) return options.size(); - else return retval; -} - -int WSelectList::getCurrentOption() -{ - return selectedOption; -} - -ULONG WSelectList::getCurrentOptionData() -{ - if (!options.size()) return 0; - return options[selectedOption].data; -} - -bool WSelectList::mouseMove(int x, int y) -{ - int ml = getMouseLine(x-getRootBoxOffsetX(), y-getRootBoxOffsetY()); - if (ml>=0 && ml!=(int)selectedOption) - { - selectedOption = ml; - return true; - } - return false; -} - -bool WSelectList::mouseLBDOWN(int x, int y) -{ - int ml = getMouseLine(x-getRootBoxOffsetX(), y-getRootBoxOffsetY()); - if (ml == (int)selectedOption) - { - /* caller should generate a OK message*/ - return true; - } - return false; -} - -int WSelectList::getMouseLine(int x,int y) -{ - int fontHeight = surface->getFontHeight(); - int ySeperation = fontHeight + gap; - - if (y<0) return -1; - if (x<0 || x>(int)area.w) return -1; - if (y>(int)(10+numOptionsDisplayable*ySeperation)) return -1; - - int cy = y - 5; - - int selected=cy/ySeperation; - if (y<5) selected=-1; - if (selected> ((int)numOptionsDisplayable)) return -1; - /* Important: should be the same algorithm used in draw! */ - if (selectedOption == (topOption + numOptionsDisplayable)) topOption++; - if (selectedOption == ((UINT)topOption - 1)) topOption--; - // if still not visible... - if ((selectedOption < (UINT)topOption) || (selectedOption > (topOption + numOptionsDisplayable))) - { - topOption = selectedOption - (numOptionsDisplayable / 2); - } - - if (topOption < 0) topOption = 0; - - if ((selected+topOption >= (int) options.size()) || - (selected + topOption < 0)) return -1; - - return selected + topOption; -} +/* + 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. +*/ + +#include "wselectlist.h" + +#include "colour.h" +#include "log.h" + +WSelectList::WSelectList() +{ + selectedOption = 0; + topOption = 0; + numOptionsDisplayable = 0; + numColumns = 0; + noLoop = 0; + gap = 1; + showseloption = true; + darkseloption = false; + backgroundColour = Colour::VIEWBACKGROUND; +} + +WSelectList::~WSelectList() +{ + clear(); +} + +void WSelectList::clear() +{ + int vsize = options.size(); + for (int i = 0; i < vsize; i++) + { + delete[] options[i].text; + } + options.clear(); + + selectedOption = 0; + topOption = 0; + numOptionsDisplayable = 0; + numColumns = 0; +} + +void WSelectList::setNoLoop() +{ + noLoop = 1; +} + +void WSelectList::setBackgroundColour(const Colour& colour) +{ + backgroundColour = colour; +} + +void WSelectList::hintSetCurrent(int idx) +{ + selectedOption = idx; + if (selectedOption >= options.size()) selectedOption = options.size() - 1; +} + +void WSelectList::hintSetTop(int idx) +{ + topOption = idx; +} + +int WSelectList::addOption(const char* text, ULONG data, int selected) +{ + int thisNewOption = options.size(); + + wsloption wslo; + wslo.text = new char[strlen(text) + 1]; + strcpy(wslo.text, text); + wslo.data = data; + options.push_back(wslo); + if (selected) selectedOption = thisNewOption; + return thisNewOption; +} + +void WSelectList::draw() +{ + int fontHeight = getFontHeight(); + int ySeperation = fontHeight + gap; + + numOptionsDisplayable = (area.h - 5) / ySeperation; + + if (selectedOption == (topOption + numOptionsDisplayable)) topOption++; + if (selectedOption == ((UINT)topOption - 1)) topOption--; + // if still not visible... + if ((selectedOption < (UINT)topOption) || (selectedOption > (topOption + numOptionsDisplayable))) + { + topOption = selectedOption - (numOptionsDisplayable / 2); + } + + if (topOption < 0) topOption = 0; + + + fillColour(backgroundColour); + + UINT ypos = 5; + for (UINT i = topOption; i < (topOption + numOptionsDisplayable); i++) + { + if (i == options.size()) return; + if ((ypos + ySeperation) > area.h) break; + + if (i == selectedOption && showseloption) + { + + rectangle(0, ypos, area.w, fontHeight, darkseloption ? Colour::SELECTDARKHIGHLIGHT: Colour::SELECTHIGHLIGHT); + + drawOptionLine(options[i].text, 5, ypos, area.w - 5, Colour::DARKTEXT); + } + else + { + + drawOptionLine(options[i].text, 5, ypos, area.w - 5, Colour::LIGHTTEXT); + } + ypos += ySeperation; + } + +} + +void WSelectList::addColumn(int x) +{ + if (numColumns == 10) return; + columns[numColumns++] = x; +} + +void WSelectList::drawOptionLine(char* text, int xpos, int ypos, int width, const Colour& colour) +{ + if (!numColumns) + { + + drawText(text, xpos, ypos, width, colour); + } + else + { + char buffer[200]; + strncpy(buffer, text, 199); + int currentColumn = 0; + char* pointer; + + pointer = strtok(buffer, "\t"); + while(pointer) + { + + drawText(pointer, xpos + columns[currentColumn], ypos, width - columns[currentColumn], colour); + + currentColumn++; + if (currentColumn == 10) return; + pointer = strtok(NULL, "\t"); + } + } +} + +void WSelectList::up() +{ + if (selectedOption > 0) + { + selectedOption--; + } + else + { + if (!noLoop) selectedOption = options.size() - 1; + } +} + +void WSelectList::down() +{ + if (selectedOption < options.size() - 1) + { + selectedOption++; + } + else + { + if (!noLoop) selectedOption = 0; + } +} + +void WSelectList::pageUp() +{ + topOption -= numOptionsDisplayable; + if (topOption < 0) topOption = 0; + + selectedOption = topOption; +} + +void WSelectList::pageDown() +{ + if ((topOption + numOptionsDisplayable) >= options.size()) + { + selectedOption = options.size() - 1; + } + else + { + topOption += numOptionsDisplayable; + selectedOption = topOption; + } +} + +int WSelectList::getTopOption() +{ + return topOption; +} + +int WSelectList::getNumOptions() +{ + return options.size(); +} + +int WSelectList::getBottomOption() +{ + UINT retval = topOption + numOptionsDisplayable; + if (retval > options.size()) return options.size(); + else return retval; +} + +int WSelectList::getCurrentOption() +{ + return selectedOption; +} + +ULONG WSelectList::getCurrentOptionData() +{ + if (!options.size()) return 0; + return options[selectedOption].data; +} + +bool WSelectList::mouseAndroidScroll(int x, int y,int sx, int sy) +{ +/* int fontHeight = getFontHeight(); + int movelines= sy/fontHeight; + + int seloption=selectedOption+movelines; + if (seloption<0) seloption=0; + else if (seloption>options.size()-1) seloption=options.size()-1; + selectedOption=seloption;*/ + +} + +bool WSelectList::mouseMove(int x, int y) +{ + int ml = getMouseLine(x-getRootBoxOffsetX(), y-getRootBoxOffsetY()); + if (ml>=0 && ml!=(int)selectedOption) + { + selectedOption = ml; + return true; + } + return false; +} + +bool WSelectList::mouseLBDOWN(int x, int y) +{ + int ml = getMouseLine(x-getRootBoxOffsetX(), y-getRootBoxOffsetY()); + if (ml == (int)selectedOption) + { + /* caller should generate a OK message*/ + return true; + } + return false; +} + +int WSelectList::getMouseLine(int x,int y) +{ + int fontHeight = getFontHeight(); + int ySeperation = fontHeight + gap; + + if (y<0) return -1; + if (x<0 || x>(int)area.w) return -1; + if (y>(int)(10+numOptionsDisplayable*ySeperation)) return -1; + + int cy = y - 5; + + int selected=cy/ySeperation; + if (y<5) selected=-1; + if (selected> ((int)numOptionsDisplayable)) return -1; + /* Important: should be the same algorithm used in draw! */ + if (selectedOption == (topOption + numOptionsDisplayable)) topOption++; + if (selectedOption == ((UINT)topOption - 1)) topOption--; + // if still not visible... + if ((selectedOption < (UINT)topOption) || (selectedOption > (topOption + numOptionsDisplayable))) + { + topOption = selectedOption - (numOptionsDisplayable / 2); + } + + if (topOption < 0) topOption = 0; + + if ((selected+topOption >= (int) options.size()) || + (selected + topOption < 0)) return -1; + + return selected + topOption; +} diff --git a/wselectlist.h b/wselectlist.h index 03ba361..c27f73e 100644 --- a/wselectlist.h +++ b/wselectlist.h @@ -1,89 +1,90 @@ -/* - 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 WSELECTLIST_H -#define WSELECTLIST_H - -#include -#include - -#include - -#include "defines.h" -#include "boxx.h" - -using namespace std; - -typedef struct -{ - char* text; - ULONG data; -} wsloption; - -class WSelectList : public Boxx -{ - public: - WSelectList(); - ~WSelectList(); - void clear(); - void addColumn(int x); - - void setNoLoop(); - void setShowSelOption(bool set) { showseloption = set; }; - void setDarkSelOption(bool set) { darkseloption = set; }; - int addOption(const char* text, ULONG data, int selected); - void draw(); - void setBackgroundColour(const Colour& colour); - - void down(); - void up(); - void pageUp(); - void pageDown(); - - int getTopOption(); - int getNumOptions(); - int getBottomOption(); // actually returns bottom + 1 i.e. the one just past display ?! - int getCurrentOption(); - ULONG getCurrentOptionData(); - - void hintSetCurrent(int index); - void hintSetTop(int index); - - virtual bool mouseMove(int x, int y); - virtual bool mouseLBDOWN(int x, int y); - - private: - void drawOptionLine(char* text, int xpos, int ypos, int width, const Colour& colour); - int getMouseLine(int x, int y); - - vector options; - UINT selectedOption; - int topOption; - UINT numOptionsDisplayable; - int columns[10]; - int numColumns; - int noLoop; - bool showseloption, darkseloption; - - UINT gap; - Colour backgroundColour; -}; - -#endif +/* + 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 WSELECTLIST_H +#define WSELECTLIST_H + +#include +#include + +#include + +#include "defines.h" +#include "boxx.h" + +using namespace std; + +typedef struct +{ + char* text; + ULONG data; +} wsloption; + +class WSelectList : public Boxx +{ + public: + WSelectList(); + ~WSelectList(); + void clear(); + void addColumn(int x); + + void setNoLoop(); + void setShowSelOption(bool set) { showseloption = set; }; + void setDarkSelOption(bool set) { darkseloption = set; }; + int addOption(const char* text, ULONG data, int selected); + void draw(); + void setBackgroundColour(const Colour& colour); + + void down(); + void up(); + void pageUp(); + void pageDown(); + + int getTopOption(); + int getNumOptions(); + int getBottomOption(); // actually returns bottom + 1 i.e. the one just past display ?! + int getCurrentOption(); + ULONG getCurrentOptionData(); + + void hintSetCurrent(int index); + void hintSetTop(int index); + + virtual bool mouseMove(int x, int y); + virtual bool mouseLBDOWN(int x, int y); + virtual bool mouseAndroidScroll(int x, int y,int sx, int sy); + + private: + void drawOptionLine(char* text, int xpos, int ypos, int width, const Colour& colour); + int getMouseLine(int x, int y); + + vector options; + UINT selectedOption; + int topOption; + UINT numOptionsDisplayable; + int columns[10]; + int numColumns; + int noLoop; + bool showseloption, darkseloption; + + UINT gap; + Colour backgroundColour; +}; + +#endif diff --git a/wsymbol.cc b/wsymbol.cc index a4d8262..903f1e6 100644 --- a/wsymbol.cc +++ b/wsymbol.cc @@ -1078,6 +1078,7 @@ void WSymbol::draw() int x, y, bytesIn, bitsIn; + startFastDraw(); for (y = 0; y < sHeight; y++) { for (x = 0; x < widthBits; x++) @@ -1087,10 +1088,11 @@ void WSymbol::draw() if ((base[bytesIn] >> (7 - bitsIn)) & 0x01) { - drawPixel(x, y, nextColour); + drawPixel(x, y, nextColour,true); } } } + endFastDraw(); } bool WSymbol::mouseLBDOWN(int x, int y) diff --git a/wtabbar.cc b/wtabbar.cc index 2194b2c..eb8ec53 100644 --- a/wtabbar.cc +++ b/wtabbar.cc @@ -67,7 +67,7 @@ void WTabBar::addTab(const char* name, Boxx* boxx) WButton* newButton = new WButton(); newButton->setText(name); newButton->setPosition(newButtonX, 2); - newButton->setSize(td.nameWidth + 6, Surface::getFontHeight()); + newButton->setSize(td.nameWidth + 6, getFontHeight()); if ((newButtonX + newButton->getWidth()) > (getWidth() - 22)) newButton->setVisible(false); add(newButton); -- 2.39.2