-vomp_platform =raspberry\r
-# valid platforms are raspberry and mvp\r
-vomp_options=\r
-#uncomment the line below, if you want to vomp application like, without a reboot option, automatically set for windows!\r
-#vomp_options+= -DVOMP_HAS_EXIT\r
-ifeq ($(vomp_platform),mvp)\r
-\r
-$(info MVP crosscompiler)\r
-include ../crosstool/cross-var\r
-CC=$(CROSS)gcc\r
-STRIP=$(CROSS)strip\r
-CXX=$(CROSS)g++\r
-LD=$(CROSS)g++\r
-\r
-endif\r
-\r
-ifeq ($(vomp_platform),raspberry)\r
-\r
-$(info raspberry normal compiler)\r
-CC=gcc\r
-STRIP=strip\r
-CXX=g++\r
-LD=g++\r
-\r
-endif\r
-\r
-\r
-\r
-CXXFLAGS_DEV = -g -O0 -Wall -Wshadow -DDEV -D_GNU_SOURCE $(INCLUDES)\r
-CXXFLAGS_REL = -O3 -Wall -Wshadow -D_GNU_SOURCE $(INCLUDES)\r
-\r
-\r
-LIBPATHS =\r
-\r
-\r
-$(info Setting up objects)\r
-# This is the only thing windows and linux share\r
-include objects.mk\r
-\r
-OBJECTSWIN = threadwin.o remotewin.o ledwin.o mtdwin.o videowin.o audiowin.o osdwin.o surfacewin.o\r
-\r
-OBJECTS = $(OBJECTS1) \r
-\r
-\r
-ifeq ($(vomp_platform),mvp)\r
-$(info MVP flags)\r
-LDFLAGS = -Wall -static\r
-LIBS = -lpthread -lrt\r
-\r
-OBJECTS += wwss.o main.o threadp.o remotemvp.o ledmvp.o mtdmvp.o videomvp.o audiomvp.o osdmvp.o surfacemvp.o wjpegcomplex.o vmedialist.o vcolourtuner.o vmediaview.o vvideomedia.o \r
-TIOBJECT = ticonfig.o\r
-CROSSLIBS = ../jpeg/jpeg-6b/libjpeg.a\r
-INCLUDES = -I../jpeg/jpeg-6b -DVOMP_PLATTFORM_MVP \r
-\r
-\r
-endif\r
-\r
-ifeq ($(vomp_platform),raspberry)\r
-$(info Raspberry pi flags)\r
-LDFLAGS = -Wall -Wl,--format=binary -Wl,fonts/sourcesans.ttf -Wl,other/vdrhires.jpg -Wl,other/wallpaper720p.jpg -Wl,--format=default \r
-LIBS = -L/opt/vc/lib -lpthread -lrt -lEGL -lOpenVG -lopenmaxil -lbcm_host -lavformat -lavcodec -lavutil\r
-\r
-OBJECTS += main.o threadp.o osdvector.o surfacevector.o osdopenvg.o ledraspberry.o mtdraspberry.o videoomx.o audioomx.o wjpegsimple.o remotelinux.o \r
-LIBS+= -lfreetype -lMagick++ -lMagickCore\r
-CROSSLIBS = \r
-INCLUDES = -DVOMP_PLATTFORM_RASPBERRY -I/opt/vc/include -I/opt/vc/include/interface/vcos/pthreads -I/usr/include/freetype2 -I/usr/include/ImageMagick\r
-CXXFLAGS_DEV += -D__STDC_CONSTANT_MACROS\r
-CXXFLAGS_REL += -D__STDC_CONSTANT_MACROS\r
-\r
-endif\r
-\r
-.PHONY: clean fresh all install strip\r
-\r
-default: dev\r
-fresh: clean default\r
-\r
-vompclient: $(OBJECTS) $(TIOBJECT) $(CROSSLIBS)\r
- $(LD) $(LDFLAGS) $(LIBPATHS) $(RELEASE) -o vompclient $(TIOBJECT) $(OBJECTS) $(CROSSLIBS) $(LIBS)\r
-\r
-# A slight hash up\r
-ticonfig.o:\r
- $(CC) $(CXXFLAGS_REL) -c -o ticonfig.o ticonfig.c\r
-\r
-strip:\r
- $(STRIP) vompclient\r
-\r
-install:\r
- rm -f /mnt/auto/defiant/diskless/nfs/mvp/vompclient\r
- cp vompclient /mnt/auto/defiant/diskless/nfs/mvp\r
-\r
-install-wmp:\r
- rm -f /diskless/nfs/wmvp/vompclient\r
- cp vompclient /diskless/nfs/wmvp\r
-\r
-install-dev:\r
- rm -f /mnt/auto/defiant/diskless/nfs/mvp-dev/vompclient\r
- cp vompclient /mnt/auto/defiant/diskless/nfs/mvp-dev\r
-\r
-debug:\r
- ../../gdb/gdb-6.7/gdb/gdb /mnt/auto/defiant/diskless/nfs/mvp/vompclient /mnt/auto/defiant/diskless/nfs/mvp/core.*\r
-\r
-debug2:\r
- ../../gdb/gdb-6.7/gdb/gdb /mnt/auto/defiant/diskless/nfs/mvp-dev/vompclient /mnt/auto/defiant/diskless/nfs/mvp-dev/core.*\r
-\r
-dev: CXXFLAGS := $(CXXFLAGS_DEV)\r
-dev: vompclient\r
-\r
-release: CXXFLAGS := $(CXXFLAGS_REL)\r
-release: clean vompclient strip\r
-\r
-deps: GNUmakefile\r
- $(CC) -MM $(INCLUDES) $(vomp_options) $(OBJECTS:%.o=%.cc) > deps\r
-\r
--include deps\r
-\r
-clean:\r
- rm -f *.o deps vompclient *~ fonts/*.o fonts/*~ teletxt/*.o\r
-\r
+vomp_platform =raspberry
+# valid platforms are raspberry and mvp
+vomp_options=
+#uncomment the line below, if you want to vomp application like, without a reboot option, automatically set for windows!
+#vomp_options+= -DVOMP_HAS_EXIT
+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 += wwss.o main.o threadp.o remotemvp.o ledmvp.o mtdmvp.o videomvp.o audiomvp.o osdmvp.o surfacemvp.o wjpegcomplex.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 -Wl,--format=binary -Wl,fonts/sourcesans.ttf -Wl,other/vdrhires.jpg -Wl,other/wallpaper720p.jpg -Wl,--format=default
+LIBS = -L/opt/vc/lib -lpthread -lrt -lEGL -lOpenVG -lopenmaxil -lbcm_host -lavformat -lavcodec -lavutil
+
+OBJECTS += main.o threadp.o osdvector.o surfacevector.o osdopenvg.o ledraspberry.o mtdraspberry.o videoomx.o audioomx.o wjpegsimple.o remotelinux.o
+LIBS+= -lfreetype -lMagick++ -lMagickCore
+CROSSLIBS =
+INCLUDES = -DVOMP_PLATTFORM_RASPBERRY -I/opt/vc/include -I/opt/vc/include/interface/vcos/pthreads -I/usr/include/freetype2 -I/usr/include/ImageMagick
+CXXFLAGS_DEV += -D__STDC_CONSTANT_MACROS
+CXXFLAGS_REL += -D__STDC_CONSTANT_MACROS
+
+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) $(vomp_options) $(OBJECTS:%.o=%.cc) > deps
+
+-include deps
+
+clean:
+ rm -f *.o deps vompclient *~ fonts/*.o fonts/*~ teletxt/*.o
+
-/*\r
- Copyright 2004-2005 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#include "afeed.h"\r
-\r
-#include "log.h"\r
-#include "demuxer.h"\r
-#include "callback.h"\r
-\r
-\r
-AFeed::AFeed(Callback* tcb)\r
-: cb(*tcb)\r
-{\r
- audioEnabled = 1;\r
-}\r
-\r
-int AFeed::init()\r
-{\r
- return 1;\r
-}\r
-\r
-int AFeed::shutdown()\r
-{\r
- // FIXME\r
- return 1;\r
-}\r
-\r
-void AFeed::disable()\r
-{\r
- audioEnabled = 0;\r
-}\r
-\r
-void AFeed::enable()\r
-{\r
- audioEnabled = 1;\r
-}\r
-\r
-int AFeed::start()\r
-{\r
- audioEnabled = 1;\r
- return threadStart();\r
-}\r
-\r
-void AFeed::stop()\r
-{\r
- Log::getInstance()->log("AFeed", Log::DEBUG, "Stop1");\r
- threadCancel();\r
- Log::getInstance()->log("AFeed", Log::DEBUG, "Stop2");\r
-}\r
-\r
-void AFeed::threadMethod()\r
-{\r
- bool alen;\r
-\r
- while(1)\r
- {\r
- threadCheckExit();\r
-\r
- if (audioEnabled)\r
- {\r
- bool newdata=false;\r
- alen = Demuxer::getInstance()->writeAudio(&newdata);\r
-\r
- if (newdata) cb.call(this);\r
- if (alen)\r
- {\r
- //Log::getInstance()->log("Afeed", Log::DEBUG, "written");\r
- cb.call(this);\r
- }\r
- else\r
- {\r
- //MILLISLEEP(100);\r
- MILLISLEEP(5); //Performance Issue Marten\r
- }\r
- }\r
- else\r
- {\r
- Demuxer::getInstance()->flushAudio();\r
- //Log::getInstance()->log("AFeed", Log::DEBUG, "No data delay");\r
- //MILLISLEEP(100);\r
- MILLISLEEP(5); //Performance Issue\r
- }\r
- }\r
-}\r
-\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 "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()
+{
+ Log::getInstance()->log("AFeed", Log::DEBUG, "Stop1");
+ threadCancel();
+ Log::getInstance()->log("AFeed", Log::DEBUG, "Stop2");
+}
+
+void AFeed::threadMethod()
+{
+ bool alen;
+
+ while(1)
+ {
+ threadCheckExit();
+
+ if (audioEnabled)
+ {
+ bool newdata=false;
+ alen = Demuxer::getInstance()->writeAudio(&newdata);
+
+ if (newdata) cb.call(this);
+ if (alen)
+ {
+ //Log::getInstance()->log("Afeed", Log::DEBUG, "written");
+ cb.call(this);
+ }
+ 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
+ }
+ }
+}
+
-/*\r
- Copyright 2004-2005 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef AUDIO_H\r
-#define AUDIO_H\r
-\r
-#include <stdio.h>\r
-#include "defines.h"\r
-#include "draintarget.h"\r
-#include "abstractoption.h"\r
-\r
-typedef struct\r
-{\r
- unsigned char frontleft;\r
- unsigned char frontright;\r
- unsigned char rearleft;\r
- unsigned char rearright;\r
- unsigned char center;\r
- unsigned char lfe;\r
-} audio_volume;\r
-\r
-class Audio : public DrainTarget, public AbstractOption\r
-{\r
- public:\r
- Audio();\r
- virtual ~Audio();\r
- static Audio* getInstance();\r
- // static void setInstance(Audio* );\r
-\r
- virtual int init(UCHAR streamType)=0;\r
- virtual int shutdown()=0;\r
- virtual int setStreamType(UCHAR streamType)=0;\r
- virtual int setChannel()=0;\r
- virtual int setSource()=0;\r
- virtual int sync()=0;\r
- virtual int play()=0;\r
- virtual int stop()=0;\r
- virtual int pause()=0;\r
- virtual int unPause()=0;\r
- virtual int reset()=0;\r
- virtual int setVolume(int volume)=0;\r
- virtual int mute()=0;\r
- virtual int unMute()=0;\r
- virtual bool supportsAc3()=0;\r
- virtual bool maysupportAc3(){return false;}\r
- virtual bool streamTypeSupported(int streamtype)\r
- {\r
- switch (streamtype) {\r
- case 3:\r
- case 4:\r
- return true;\r
- default:\r
- return false;\r
- };\r
- }\r
-\r
- int volumeUp();\r
- int volumeDown();\r
- int getVolume();\r
- int toggleUserMute();\r
- int systemMuteOn();\r
- int systemMuteOff();\r
- int doMuting();\r
-\r
- // Audio stream type // FIXME these are MVP specific (probably!)\r
- const static UCHAR MPEG2_PES = 2;\r
- const static UCHAR MPEG1_PES = 3; // unused\r
- const static UCHAR MP3 = 0; //media player\r
-\r
-#ifdef DEV\r
- virtual int test()=0;\r
-#endif\r
-\r
- protected:\r
- static Audio* instance;\r
- int initted;\r
- UCHAR userMute;\r
- UCHAR systemMute;\r
- UCHAR muted;\r
- int volume;\r
-\r
- audio_volume Aoffset;\r
-};\r
-\r
-#endif\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.
+*/
+
+#ifndef AUDIO_H
+#define AUDIO_H
+
+#include <stdio.h>
+#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;}
+ virtual bool streamTypeSupported(int streamtype)
+ {
+ switch (streamtype) {
+ case 3:
+ case 4:
+ return true;
+ default:
+ 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
-/*\r
- Copyright 2004-2005 Chris Tallon, 2009,2012 Marten Richter\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef AUDIOOMX_H\r
-#define AUDIOOMX_H\r
-\r
-#include <stdio.h>\r
-#include <unistd.h>\r
-#include <fcntl.h>\r
-#include <sys/ioctl.h>\r
-\r
-#include "defines.h"\r
-#include "audio.h"\r
-#include "videoomx.h"\r
-\r
-extern "C" {\r
-#include <libavcodec/avcodec.h>\r
-#include <libavformat/avformat.h>\r
-}\r
-\r
-\r
-\r
-\r
-class AudioOMX : public Audio\r
-{\r
- friend class VideoOMX;\r
- public:\r
- AudioOMX();\r
- virtual ~AudioOMX();\r
-\r
- int init(UCHAR streamType);\r
- int shutdown();\r
-\r
- int setStreamType(UCHAR streamType);\r
- int setChannel();\r
- int setSource();\r
- int sync();\r
- int play();\r
- int stop();\r
- int pause();\r
- int unPause();\r
- int reset();\r
- int setVolume(int volume);\r
- int mute();\r
- int unMute();\r
- bool supportsAc3() { return true; }\r
- bool maysupportAc3(){return true;}\r
- bool streamTypeSupported(int streamtype)\r
- {\r
- switch (streamtype) {\r
- case 0x11: //AAC LATM packaging\r
- case 0x6A://ac3\r
- case 3: //mpeg 1 layer 1 and 2\r
- case 4:\r
- return true;\r
- default:\r
- return false;\r
- };\r
- }\r
-\r
-\r
- //Writing Data to Audiodevice\r
- virtual void PrepareMediaSample(const MediaPacketList&, UINT samplepos);\r
- virtual UINT DeliverMediaSample(UCHAR* buffer, UINT *samplepos);\r
- UINT DeliverMediaPacket(MediaPacket packet, const UCHAR* buffer,UINT *samplepos);\r
-\r
- virtual long long SetStartOffset(long long curreftime, bool *rsync);\r
- virtual void ResetTimeOffsets();\r
-\r
- virtual bool DrainTargetReady() {return omx_running;};\r
-\r
-\r
- UCHAR getLastAType() {return lastAType;}\r
-\r
- bool loadOptionsfromServer(VDR* vdr);\r
- bool saveOptionstoServer();\r
- bool addOptionsToPanes(int panenumber,Options *options,WOptionPane* pane);\r
- bool handleOptionChanges(Option* option);\r
-\r
-\r
-#ifdef DEV\r
- int test();\r
-#endif\r
-\r
- private:\r
- int initAllParams();\r
- UCHAR streamType;\r
- UCHAR lastAType;\r
- bool firstsynched;\r
-\r
- MediaPacket packet;\r
- UINT packetpos;\r
-\r
- bool paused;\r
-\r
- bool hdmi; // use hdmi as audio output\r
- bool passthrough; // use audio passthrough for the current audio type\r
-\r
- bool canpass_aac;\r
- bool canpass_ac3;\r
- bool canpass_mp2;\r
- bool canpass_mp3;\r
- bool canpass_pcm_mch;\r
-\r
-\r
- int prefered_aac;\r
- int prefered_ac3; //0 stereo PCM, 1 passthrough 2 Multichannel PCM\r
- int prefered_mp2;\r
- int prefered_mp3;\r
-\r
-\r
-\r
- static OMX_ERRORTYPE EmptyBufferDone_OMX(OMX_IN OMX_HANDLETYPE hcomp,OMX_IN OMX_PTR appdata,OMX_IN OMX_BUFFERHEADERTYPE* bulibaver);\r
- static OMX_ERRORTYPE FillBufferDone_OMX(OMX_IN OMX_HANDLETYPE hcomp, OMX_IN OMX_PTR appdata,OMX_IN OMX_BUFFERHEADERTYPE* bulibaver);\r
-\r
-\r
-\r
- unsigned int AdvanceAc3AudioSync(const UCHAR *data,unsigned int size,unsigned int *framesize);\r
- unsigned int AdvanceAacLatmAudioSync(const UCHAR *data,unsigned int size,unsigned int *framesize);\r
- unsigned int AdvanceMpAudioSync(const UCHAR *data,unsigned int size,unsigned int *framesize);\r
-\r
-\r
- void ReturnEmptyOMXBuffer(OMX_BUFFERHEADERTYPE* bulibaver);\r
-\r
- // OMX_HANDLETYPE omx_aud_dec;\r
- OMX_HANDLETYPE omx_aud_rend;\r
- OMX_HANDLETYPE omx_clock;\r
-\r
- //OMX_U32 omx_codec_input_port;\r
- //OMX_U32 omx_codec_output_port;\r
- OMX_U32 omx_rend_input_port;\r
- OMX_U32 omx_rend_clock_port;\r
- OMX_U32 omx_clock_output_port;\r
-\r
- long long lastreftimeOMX;\r
- ULLONG lastreftimePTS;\r
-\r
-\r
-\r
- int AllocateCodecsOMX();\r
- int DeAllocateCodecsOMX();\r
-\r
- int PrepareInputBufsOMX(bool setportdef);\r
- int DestroyInputBufsOMX();\r
- int DestroyInputBufsOMXwhilePlaying();\r
-\r
- int ChangeAudioPortConfig(bool disport);\r
- int ChangeAudioDestination();\r
- long long correctAudioLatency(long long pts,int addsamples,int srate);\r
-\r
- vector<OMX_BUFFERHEADERTYPE*> input_bufs_omx_all;\r
- list<OMX_BUFFERHEADERTYPE*> input_bufs_omx_free;\r
- Mutex input_bufs_omx_mutex;\r
- OMX_BUFFERHEADERTYPE* cur_input_buf_omx;\r
-\r
- bool omx_running;\r
- bool omx_first_frame;\r
- Mutex libav_mutex;\r
-\r
- AVCodec *aaclatmcodec_libav;\r
- AVCodecContext *aaclatmcodec_context_libav;\r
- AVCodec *ac3codec_libav;\r
- AVCodecContext *ac3codec_context_libav;\r
- AVCodec *mp23codec_libav;\r
- AVCodecContext *mp23codec_context_libav;\r
- AVPacket incoming_paket_libav;\r
- AVFrame *decode_frame_libav;\r
-\r
- UCHAR * decompress_buffer;\r
- unsigned int decompress_buffer_size;\r
- unsigned int decompress_buffer_filled;\r
- bool lsync;\r
-\r
-\r
- int InitDecoderLibAV();\r
- void DeinitDecoderLibAV();\r
-\r
-};\r
-\r
-#endif\r
+/*
+ Copyright 2004-2005 Chris Tallon, 2009,2012 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 AUDIOOMX_H
+#define AUDIOOMX_H
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#include "defines.h"
+#include "audio.h"
+#include "videoomx.h"
+
+extern "C" {
+#include <libavcodec/avcodec.h>
+#include <libavformat/avformat.h>
+}
+
+
+
+
+class AudioOMX : public Audio
+{
+ friend class VideoOMX;
+ public:
+ AudioOMX();
+ virtual ~AudioOMX();
+
+ int init(UCHAR streamType);
+ int shutdown();
+
+ int setStreamType(UCHAR streamType);
+ int setChannel();
+ int setSource();
+ int sync();
+ int play();
+ int stop();
+ int pause();
+ int unPause();
+ int reset();
+ int setVolume(int volume);
+ int mute();
+ int unMute();
+ bool supportsAc3() { return true; }
+ bool maysupportAc3(){return true;}
+ bool streamTypeSupported(int streamtype)
+ {
+ switch (streamtype) {
+ case 0x11: //AAC LATM packaging
+ case 0x6A://ac3
+ case 3: //mpeg 1 layer 1 and 2
+ case 4:
+ return true;
+ default:
+ return false;
+ };
+ }
+
+
+ //Writing Data to Audiodevice
+ virtual void PrepareMediaSample(const MediaPacketList&, UINT samplepos);
+ virtual UINT DeliverMediaSample(UCHAR* buffer, UINT *samplepos);
+ UINT DeliverMediaPacket(MediaPacket packet, const UCHAR* buffer,UINT *samplepos);
+
+ virtual long long SetStartOffset(long long curreftime, bool *rsync);
+ virtual void ResetTimeOffsets();
+
+ virtual bool DrainTargetReady() {return omx_running;};
+
+
+ UCHAR getLastAType() {return lastAType;}
+
+ bool loadOptionsfromServer(VDR* vdr);
+ bool saveOptionstoServer();
+ bool addOptionsToPanes(int panenumber,Options *options,WOptionPane* pane);
+ bool handleOptionChanges(Option* option);
+
+
+#ifdef DEV
+ int test();
+#endif
+
+ private:
+ int initAllParams();
+ UCHAR streamType;
+ UCHAR lastAType;
+ bool firstsynched;
+
+ MediaPacket packet;
+ UINT packetpos;
+
+ bool paused;
+
+ bool hdmi; // use hdmi as audio output
+ bool passthrough; // use audio passthrough for the current audio type
+
+ bool canpass_aac;
+ bool canpass_ac3;
+ bool canpass_mp2;
+ bool canpass_mp3;
+ bool canpass_pcm_mch;
+
+
+ int prefered_aac;
+ int prefered_ac3; //0 stereo PCM, 1 passthrough 2 Multichannel PCM
+ int prefered_mp2;
+ int prefered_mp3;
+
+
+
+ static OMX_ERRORTYPE EmptyBufferDone_OMX(OMX_IN OMX_HANDLETYPE hcomp,OMX_IN OMX_PTR appdata,OMX_IN OMX_BUFFERHEADERTYPE* bulibaver);
+ static OMX_ERRORTYPE FillBufferDone_OMX(OMX_IN OMX_HANDLETYPE hcomp, OMX_IN OMX_PTR appdata,OMX_IN OMX_BUFFERHEADERTYPE* bulibaver);
+
+
+
+ unsigned int AdvanceAc3AudioSync(const UCHAR *data,unsigned int size,unsigned int *framesize);
+ unsigned int AdvanceAacLatmAudioSync(const UCHAR *data,unsigned int size,unsigned int *framesize);
+ unsigned int AdvanceMpAudioSync(const UCHAR *data,unsigned int size,unsigned int *framesize);
+
+
+ void ReturnEmptyOMXBuffer(OMX_BUFFERHEADERTYPE* bulibaver);
+
+ // OMX_HANDLETYPE omx_aud_dec;
+ OMX_HANDLETYPE omx_aud_rend;
+ OMX_HANDLETYPE omx_clock;
+
+ //OMX_U32 omx_codec_input_port;
+ //OMX_U32 omx_codec_output_port;
+ OMX_U32 omx_rend_input_port;
+ OMX_U32 omx_rend_clock_port;
+ OMX_U32 omx_clock_output_port;
+
+ long long lastreftimeOMX;
+ ULLONG lastreftimePTS;
+
+
+
+ int AllocateCodecsOMX();
+ int DeAllocateCodecsOMX();
+
+ int PrepareInputBufsOMX(bool setportdef);
+ int DestroyInputBufsOMX();
+ int DestroyInputBufsOMXwhilePlaying();
+
+ int ChangeAudioPortConfig(bool disport);
+ int ChangeAudioDestination();
+ long long correctAudioLatency(long long pts,int addsamples,int srate);
+
+ vector<OMX_BUFFERHEADERTYPE*> input_bufs_omx_all;
+ list<OMX_BUFFERHEADERTYPE*> input_bufs_omx_free;
+ Mutex input_bufs_omx_mutex;
+ OMX_BUFFERHEADERTYPE* cur_input_buf_omx;
+
+ bool omx_running;
+ bool omx_first_frame;
+ Mutex libav_mutex;
+
+ AVCodec *aaclatmcodec_libav;
+ AVCodecContext *aaclatmcodec_context_libav;
+ AVCodec *ac3codec_libav;
+ AVCodecContext *ac3codec_context_libav;
+ AVCodec *mp23codec_libav;
+ AVCodecContext *mp23codec_context_libav;
+ AVPacket incoming_paket_libav;
+ AVFrame *decode_frame_libav;
+
+ UCHAR * decompress_buffer;
+ unsigned int decompress_buffer_size;
+ unsigned int decompress_buffer_filled;
+ bool lsync;
+
+
+ int InitDecoderLibAV();
+ void DeinitDecoderLibAV();
+
+};
+
+#endif
-/*\r
- Copyright 2004-2005 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#include "audiowin.h"\r
-#include "videowin.h"\r
-#include "vdr.h"\r
-#include "wtabbar.h"\r
-#include "wwinaudiofilter.h"\r
-#include "wwinmp3audiofilter.h"\r
-#include "i18n.h"\r
-\r
-\r
-\r
-\r
-AudioWin::AudioWin()\r
-{\r
- initted = 0;\r
- firstsynched=false;\r
- winvolume=0;\r
- volume=20;\r
-audiofilterselected=-1;\r
- mp3audiofilterselected=-1;\r
- aud_type=Audio::MPEG2_PES;\r
-\r
-}\r
-\r
-AudioWin::~AudioWin()\r
-{\r
-\r
- int i;\r
- for (i=0;i<audiofilterlist.size();i++)\r
- {\r
- if (audiofilterlist[i].displayname) delete [] audiofilterlist[i].displayname;\r
- if (audiofilterlist[i].friendlyname) delete [] audiofilterlist[i].friendlyname;\r
- }\r
- audiofilterlist.clear();\r
-\r
- for (i=0;i<mp3audiofilterlist.size();i++)\r
- {\r
- if (mp3audiofilterlist[i].displayname) delete [] mp3audiofilterlist[i].displayname;\r
- if (mp3audiofilterlist[i].friendlyname) delete [] mp3audiofilterlist[i].friendlyname;\r
- }\r
- mp3audiofilterlist.clear();\r
-\r
-}\r
-\r
-int AudioWin::init(UCHAR tstreamType)\r
-{\r
- if (initted) return 0;\r
- initFilterDatabase();\r
- initMp3FilterDatabase();\r
- initted = 1;\r
- return 1;\r
-}\r
-\r
-int AudioWin::shutdown()\r
-{\r
- if (!initted) return 0;\r
- initted = 0;\r
- return 1;\r
-}\r
-\r
-int AudioWin::write(char *buf, int len)\r
-{\r
- return 0; //write(fdAudio, buf, len);\r
-}\r
-\r
-int AudioWin::setStreamType(UCHAR type)\r
-{\r
- ((VideoWin*)VideoWin::getInstance())->setAudioStreamType(type);\r
- aud_type=type;\r
- if (!initted) return 0;\r
- return 1;\r
-}\r
-\r
-int AudioWin::setChannel()\r
-{\r
- if (!initted) return 0;\r
- return 1;\r
-}\r
-\r
-int AudioWin::setSource()\r
-{\r
- if (!initted) return 0;\r
- return 1;\r
-}\r
-\r
-int AudioWin::sync()\r
-{\r
- if (!initted) return 0;\r
- return 1;\r
-}\r
-\r
-int AudioWin::play()\r
-{\r
- if (!initted) return 0;\r
- firstsynched=false;\r
- return ((VideoWin*)Video::getInstance())->dsplay();\r
-\r
-}\r
-\r
-int AudioWin::stop()\r
-{\r
- if (!initted) return 0;\r
- return ((VideoWin*)Video::getInstance())->dsstop();\r
-}\r
-\r
-int AudioWin::pause()\r
-{\r
- if (!initted) return 0;\r
- return ((VideoWin*)Video::getInstance())->dspause();\r
-}\r
-\r
-int AudioWin::unPause()\r
-{\r
- if (!initted) return 0;\r
- return ((VideoWin*)Video::getInstance())->dsunPause();\r
-}\r
-\r
-int AudioWin::reset()\r
-{\r
- \r
- if (!initted){return 0;}\r
- return ((VideoWin*)Video::getInstance())->dsreset();\r
-}\r
-\r
-int AudioWin::setVolume(int tvolume)\r
-{\r
- // parameter: 0 for silence, 20 for full\r
- if ((tvolume < 0) || (tvolume > 20)) return 0;\r
- winvolume=((tvolume-20)*100*30)/20;\r
- if (tvolume==0) winvolume=-10000;\r
- ((VideoWin*)Video::getInstance())->SetAudioVolume(winvolume);\r
-\r
-\r
- return 1;\r
-}\r
-\r
-int AudioWin::mute()\r
-{\r
- if (!initted) return 0;\r
- ((VideoWin*)Video::getInstance())->SetAudioState(false);\r
- ((VideoWin*)Video::getInstance())->SetAudioVolume(-10000);\r
- return 1;\r
-}\r
-\r
-int AudioWin::unMute()\r
-{\r
- if (!initted) return 0;\r
- ((VideoWin*)Video::getInstance())->SetAudioState(true);\r
- ((VideoWin*)Video::getInstance())->SetAudioVolume(winvolume);\r
- return 1;\r
-}\r
-\r
-void AudioWin::PrepareMediaSample(const MediaPacketList& mplist,UINT samplepos)\r
-{\r
- mediapacket = mplist.front();\r
-}\r
-\r
-void AudioWin::initFilterDatabase()\r
-{\r
- /* This method should determine all availiable DirectShow Filters */\r
- IFilterMapper2* filtmap=NULL;\r
- HRESULT result;\r
- result = CoCreateInstance(CLSID_FilterMapper2,NULL,CLSCTX_INPROC,\r
- IID_IFilterMapper2,(void**)&filtmap);\r
- if (result != S_OK)\r
- {\r
- Log::getInstance()->log("AudioWin", Log::ERR , "Unable to create FilterMapper!");\r
- return;\r
- }\r
- /* Wishlist, what Mediatypes do we want */\r
- GUID mtypesin[]={MEDIATYPE_Audio,MEDIASUBTYPE_MPEG2_AUDIO,\r
- /*MEDIATYPE_Audio,MEDIASUBTYPE_MPEG1Payload,*/\r
- MEDIATYPE_Audio,MEDIASUBTYPE_DOLBY_AC3,\r
- MEDIATYPE_Audio, MEDIASUBTYPE_DOLBY_AC3_SPDIF};\r
- IEnumMoniker *myenum;\r
- result = filtmap->EnumMatchingFilters(&myenum,0,TRUE,MERIT_DO_NOT_USE+1,\r
- TRUE,3,mtypesin,NULL,NULL,FALSE,TRUE,0,NULL,NULL,NULL);\r
- if (result != S_OK)\r
- {\r
- filtmap->Release();\r
- Log::getInstance()->log("AudioWin", Log::ERR , "Unable to enum Filters!");\r
- return;\r
- }\r
- ULONG gethowmany;\r
- IMoniker * moni;\r
- while(myenum->Next(1,&moni,&gethowmany)==S_OK)\r
- {\r
- AudioFilterDesc desc;\r
- ZeroMemory(&desc,sizeof(desc));\r
- \r
- LPOLESTR string;\r
- moni->GetDisplayName(0,0,&string);\r
- desc.displayname=new char[wcslen(string)+1];\r
- wcstombs(desc.displayname,string,wcslen(string)+1);\r
- CoTaskMemFree(string);\r
- IPropertyBag *bag;\r
- if (moni->BindToStorage(0,0,IID_IPropertyBag,(void**)&bag) == S_OK)\r
- {\r
- VARIANT vari;\r
- VariantInit(&vari);\r
- result = bag->Read(L"FriendlyName",&vari,NULL);\r
- if (result == S_OK)\r
- {\r
- desc.friendlyname=new char[wcslen(vari.bstrVal)+1];\r
- wcstombs(desc.friendlyname,vari.bstrVal,wcslen(vari.bstrVal)+1);\r
- }\r
- VariantClear(&vari);\r
- bag->Release();\r
-\r
- }\r
- \r
- \r
- audiofilterlist.push_back(desc);\r
- \r
-\r
- \r
- moni->Release();\r
- // bctx->Release();\r
- }\r
- int i;\r
- audiofilterselected=-1;\r
- myenum->Release();\r
- filtmap->Release();\r
-}\r
-\r
-void AudioWin::initMp3FilterDatabase()\r
-{\r
- /* This method should determine all availiable DirectShow Filters */\r
- IFilterMapper2* filtmap=NULL;\r
- HRESULT result;\r
- result = CoCreateInstance(CLSID_FilterMapper2,NULL,CLSCTX_INPROC,\r
- IID_IFilterMapper2,(void**)&filtmap);\r
- if (result != S_OK)\r
- {\r
- Log::getInstance()->log("AudioWin", Log::ERR , "Unable to create FilterMapper!");\r
- return;\r
- }\r
- /* Wishlist, what Mediatypes do we want */\r
- GUID mtypesin[]={MEDIATYPE_Audio,MEDIATYPE_WaveFmt_Mpeg1Layer3,\r
- MEDIATYPE_Audio,MEDIASUBTYPE_MPEG2_AUDIO};\r
- IEnumMoniker *myenum;\r
- result = filtmap->EnumMatchingFilters(&myenum,0,TRUE,MERIT_DO_NOT_USE+1,\r
- TRUE,3,mtypesin,NULL,NULL,FALSE,TRUE,0,NULL,NULL,NULL);\r
- if (result != S_OK)\r
- {\r
- filtmap->Release();\r
- Log::getInstance()->log("AudioWin", Log::ERR , "Unable to enum Filters!");\r
- return;\r
- }\r
- ULONG gethowmany;\r
- IMoniker * moni;\r
- while(myenum->Next(1,&moni,&gethowmany)==S_OK)\r
- {\r
- AudioFilterDesc desc;\r
- ZeroMemory(&desc,sizeof(desc));\r
- \r
- LPOLESTR string;\r
- moni->GetDisplayName(0,0,&string);\r
- desc.displayname=new char[wcslen(string)+1];\r
- wcstombs(desc.displayname,string,wcslen(string)+1);\r
- CoTaskMemFree(string);\r
- IPropertyBag *bag;\r
- if (moni->BindToStorage(0,0,IID_IPropertyBag,(void**)&bag) == S_OK)\r
- {\r
- VARIANT vari;\r
- VariantInit(&vari);\r
- result = bag->Read(L"FriendlyName",&vari,NULL);\r
- if (result == S_OK)\r
- {\r
- desc.friendlyname=new char[wcslen(vari.bstrVal)+1];\r
- wcstombs(desc.friendlyname,vari.bstrVal,wcslen(vari.bstrVal)+1);\r
- }\r
- VariantClear(&vari);\r
- bag->Release();\r
-\r
- }\r
- \r
- \r
- mp3audiofilterlist.push_back(desc);\r
- \r
-\r
- \r
- moni->Release();\r
- // bctx->Release();\r
- }\r
- int i;\r
- mp3audiofilterselected=-1;\r
- myenum->Release();\r
- filtmap->Release();\r
-}\r
-\r
-bool AudioWin::loadOptionsfromServer(VDR* vdr)\r
-{\r
- char *name=vdr->configLoad("DirectShow","AudioFilter");\r
- \r
- if (name != NULL) \r
- {\r
- for (int i = 0;i <audiofilterlist.size();i++)\r
- {\r
- if (strcmp(name,audiofilterlist[i].displayname)==0)\r
- {\r
- audiofilterselected = i;\r
- break;\r
- }\r
- }\r
- }\r
- name=vdr->configLoad("DirectShow","Mp3AudioFilter");\r
- \r
- if (name != NULL) \r
- {\r
- for (int i = 0;i <mp3audiofilterlist.size();i++)\r
- {\r
- if (strcmp(name,mp3audiofilterlist[i].displayname)==0)\r
- {\r
- mp3audiofilterselected = i;\r
- break;\r
- }\r
- }\r
- }\r
- return true;\r
-\r
-}\r
-\r
-bool AudioWin::saveOptionstoServer()\r
-{\r
- if (audiofilterselected!=-1) {\r
- VDR::getInstance()->configSave("DirectShow",\r
- "AudioFilter",audiofilterlist[audiofilterselected].displayname);\r
- }\r
- if (mp3audiofilterselected!=-1) {\r
- VDR::getInstance()->configSave("DirectShow",\r
- "Mp3AudioFilter",mp3audiofilterlist[mp3audiofilterselected].displayname);\r
- }\r
- return true;\r
-}\r
-\r
-UINT AudioWin::DeliverMediaSample(UCHAR* buffer, UINT *samplepos)\r
-{\r
- DeliverMediaPacket(mediapacket, buffer, samplepos);\r
- if (*samplepos == mediapacket.length) {\r
- *samplepos = 0;\r
- return 1;\r
- }\r
- else return 0;\r
-}\r
-\r
-UINT AudioWin::DeliverMediaPacket(const MediaPacket packet,\r
- UCHAR* buffer,\r
- UINT *samplepos)\r
-{\r
-\r
- /*First Check, if we have an audio sample*/\r
- VideoWin *vw=(VideoWin*)Video::getInstance();\r
- if (!vw->isdsinited()) return 0;\r
- if (vw->InIframemode()) {\r
- samplepos=0;\r
- MILLISLEEP(10);\r
- return 0; //Not in iframe mode!\r
- }\r
- IMediaSample* ms=NULL;\r
- REFERENCE_TIME reftime1=0;\r
- REFERENCE_TIME reftime2=0;\r
-\r
- UINT headerstrip=0;\r
- if (packet.disconti) {\r
- firstsynched=false;\r
- vw->DeliverAudioMediaSample();\r
- }\r
-\r
- if (packet.type!=vw->lastAType()){//Format Change //Push data out !\r
- firstsynched=false;\r
- vw->DeliverAudioMediaSample();\r
- }\r
-\r
-\r
-\r
- /*Inspect PES-Header */\r
-/* UINT header_length=buffer[(packet.pos_buffer+8)%bufferlength]+8/*is this right*;\r
-*/\r
- if (*samplepos==0 && packet.type!=MPTYPE_MPEG_AUDIO_LAYER3) {//stripheader\r
- headerstrip=buffer[packet.pos_buffer+8]+9;\r
- if (packet.type == MPTYPE_AC3) headerstrip+=4; //skip ac3 bytes\r
- *samplepos+=headerstrip;\r
- if ( packet.synched ) {\r
- vw->DeliverAudioMediaSample();//write out old data\r
- reftime1=packet.presentation_time;\r
- reftime2=reftime1+1;\r
- firstsynched=true;\r
- } else {\r
- if (!firstsynched) {//\r
- *samplepos=packet.length;//if we have not processed at least one\r
- return packet.length;//synched packet ignore it!\r
- }\r
- }\r
- }\r
- BYTE *ms_buf;\r
- UINT ms_length;\r
- UINT ms_pos;\r
- UINT haveToCopy;\r
- if (!vw->getCurrentAudioMediaSample(&ms) || ms==NULL) {// get the current sample\r
- //samplepos=0;\r
- //MILLISLEEP(10);\r
- return *samplepos;\r
- }\r
- ms_pos=ms->GetActualDataLength();\r
- ms_length=ms->GetSize();\r
- haveToCopy=min(ms_length-ms_pos,packet.length-*samplepos);\r
- if ((ms_length-ms_pos)<1) {\r
- vw->DeliverAudioMediaSample(); //we are full!\r
- if (!vw->getCurrentAudioMediaSample(&ms) || ms==NULL) {// get the current sample\r
- //samplepos=0;\r
- //MILLISLEEP(10);\r
- return *samplepos;\r
- }\r
- ms_pos=ms->GetActualDataLength();\r
- ms_length=ms->GetSize();\r
- haveToCopy=min(ms_length-ms_pos,packet.length-*samplepos);\r
- }\r
- ms->GetPointer(&ms_buf);\r
-\r
-\r
- if (ms_pos==0) {//will only be changed on first packet\r
- if (packet.disconti) {\r
- ms->SetDiscontinuity(TRUE);\r
- } else {\r
- ms->SetDiscontinuity(FALSE);\r
- }\r
- if (packet.synched) {\r
- ms->SetSyncPoint(TRUE);\r
- ms->SetTime(&reftime1,&reftime2);\r
-\r
- //ms->SetTime(NULL,NULL);\r
- ms->SetMediaTime(NULL, NULL);\r
- if (reftime1<0) ms->SetPreroll(TRUE);\r
- else ms->SetPreroll(FALSE);\r
- }else {\r
- ms->SetSyncPoint(FALSE);\r
- ms->SetTime(NULL,NULL);\r
- ms->SetMediaTime(NULL, NULL);\r
- ms->SetPreroll(FALSE);\r
- // ms->SetSyncPoint(TRUE);\r
- }\r
- }\r
- if (packet.type!=vw->lastAType()) {\r
- vw->changeAType(packet.type,ms);\r
- ms->SetDiscontinuity(TRUE);\r
- }\r
-\r
-\r
- memcpy(ms_buf+ms_pos,buffer+packet.pos_buffer+*samplepos,haveToCopy);\r
-\r
- ms->SetActualDataLength(haveToCopy+ms_pos);\r
-\r
- *samplepos+=haveToCopy;\r
-\r
- return haveToCopy+headerstrip;\r
-\r
-}\r
-\r
-int AudioWin::dsInitAudioFilter(IGraphBuilder* dsgraphbuilder)\r
-{\r
- HRESULT hres;\r
- IFilterGraph2*fg2=NULL;\r
- VideoWin *vw=(VideoWin*)Video::getInstance();\r
- if (dsgraphbuilder->QueryInterface(IID_IFilterGraph2,(void**)&fg2)!= S_OK)\r
- {\r
- Log::getInstance()->log("AudiooWin", Log::WARN , "Failed querying for FilterGraph2 Interface!");\r
- return 0;\r
- }\r
- IBaseFilter*audiofilter;\r
- if (aud_type!=Audio::MP3) {\r
- audiofilter = getAudioFilter();\r
- } else {\r
- audiofilter = getMp3AudioFilter();\r
- }\r
- if (dsgraphbuilder->AddFilter(audiofilter,NULL) != S_OK)\r
- {\r
- Log::getInstance()->log("AudioWin", Log::WARN , "Failed adding Video Filter!");\r
- return 0;\r
- }\r
- IEnumPins *pinenum=NULL;\r
- bool error=false;\r
- if (audiofilter->EnumPins(&pinenum) == S_OK)\r
- {\r
- IPin *current=NULL;\r
- ULONG fetch=0;\r
- bool firststep=false;\r
- while (pinenum->Next(1,¤t,&fetch)==S_OK)\r
- {\r
- PIN_DIRECTION dir;\r
- if (current->QueryDirection(&dir)==S_OK)\r
- {\r
- if (dir == PINDIR_INPUT)\r
- {\r
- if (vw->getSourceFilter()->GetAudioPin()->Connect(current,NULL)==S_OK)\r
- {\r
- current->Release();\r
- firststep=true;\r
- break;\r
- }\r
- }\r
- }\r
- current->Release();\r
- }\r
- if (firststep==false)\r
- {\r
- Log::getInstance()->log("AudioWin", Log::WARN , "Audio Filter has no suitable input!");\r
- audiofilter->Release();\r
- return 0;\r
- }\r
- bool secondstep=false;\r
- pinenum->Reset();\r
- while (pinenum->Next(1,¤t,&fetch)==S_OK)\r
- {\r
- PIN_DIRECTION dir;\r
- if (current->QueryDirection(&dir)==S_OK)\r
- {\r
- if (dir == PINDIR_OUTPUT)\r
- {\r
- \r
- if (fg2->RenderEx((IPin*)current/*video*/,\r
- 0,NULL) ==S_OK)\r
- {\r
- current->Release();\r
- secondstep=true;\r
- break;\r
- }\r
- }\r
- }\r
- current->Release();\r
- }\r
- if (secondstep==false)\r
- {\r
- Log::getInstance()->log("AudioWin", Log::WARN , "Audio Filter has no suitable output!");\r
- audiofilter->Release();\r
- \r
- return 0;\r
- }\r
- \r
- audiofilter->Release();\r
- pinenum->Release();\r
-\r
- }\r
-\r
-\r
-\r
- fg2->Release();\r
- return 1;\r
-}\r
-\r
-\r
-IBaseFilter *AudioWin::getAudioFilter()\r
-{\r
- IBaseFilter *curfilter= NULL;\r
- bool notset=false;\r
- if (audiofilterselected == -1)\r
- {\r
- int i;\r
- for (i = 0;i <audiofilterlist.size();i++)\r
- {\r
- audiofilterselected = i;\r
- notset=true;\r
- break;\r
- }\r
- }\r
- IBindCtx *bindctx=NULL;\r
- if (CreateBindCtx(0,&bindctx)!=S_OK) return NULL;\r
- IMoniker * moni=NULL;\r
- LPCOLESTR name=new WCHAR[strlen(audiofilterlist[audiofilterselected].displayname)+1];\r
- mbstowcs((wchar_t*)name,audiofilterlist[audiofilterselected].displayname,\r
- strlen(audiofilterlist[audiofilterselected].displayname)+1);\r
- ULONG eater;\r
- if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK)\r
- {\r
- if (moni->BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK)\r
- {\r
- if (curfilter != NULL && notset)\r
- {\r
- VDR *vdr=VDR::getInstance();\r
- if (vdr != NULL)\r
- {\r
- vdr->configSave("DirectShow","AudioFilter",\r
- audiofilterlist[audiofilterselected].displayname);\r
- }\r
- }\r
- \r
- moni->Release();\r
- delete [] name;\r
- bindctx->Release();\r
- return curfilter; \r
- }\r
- bindctx->Release();\r
- delete [] name;\r
- return NULL; \r
- }\r
- return NULL; \r
-}\r
-\r
-IBaseFilter *AudioWin::getMp3AudioFilter()\r
-{\r
- IBaseFilter *curfilter= NULL;\r
- bool notset=false;\r
- if (mp3audiofilterselected == -1)\r
- {\r
- int i;\r
- for (i = 0;i <mp3audiofilterlist.size();i++)\r
- {\r
- mp3audiofilterselected = i;\r
- notset=true;\r
- break;\r
- }\r
- }\r
- IBindCtx *bindctx=NULL;\r
- if (CreateBindCtx(0,&bindctx)!=S_OK) return NULL;\r
- IMoniker * moni=NULL;\r
- LPCOLESTR name=new WCHAR[strlen(mp3audiofilterlist[mp3audiofilterselected].displayname)+1];\r
- mbstowcs((wchar_t*)name,mp3audiofilterlist[mp3audiofilterselected].displayname,\r
- strlen(mp3audiofilterlist[mp3audiofilterselected].displayname)+1);\r
- ULONG eater;\r
- if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK)\r
- {\r
- if (moni->BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK)\r
- {\r
- if (curfilter != NULL && notset)\r
- {\r
- VDR *vdr=VDR::getInstance();\r
- if (vdr != NULL)\r
- {\r
- vdr->configSave("DirectShow","Mp3AudioFilter",\r
- mp3audiofilterlist[mp3audiofilterselected].displayname);\r
- }\r
- }\r
- \r
- moni->Release();\r
- delete [] name;\r
- bindctx->Release();\r
- return curfilter; \r
- }\r
- bindctx->Release();\r
- delete [] name;\r
- return NULL; \r
- }\r
- return NULL; \r
-}\r
-\r
-\r
-bool AudioWin::addOptionPagesToWTB(WTabBar *wtb)\r
-{\r
- Boxx *box=new WWinAudioFilter();\r
- wtb->addTab(tr("Audio Filter"), box);\r
-\r
- \r
- box=new WWinMp3AudioFilter();\r
- wtb->addTab(tr("Mp3 Audio Filter"), box);\r
- \r
-\r
- return true;\r
-}\r
-\r
-const AudioFilterDescList *AudioWin::getAudioFilterList(int &selected)\r
-{\r
- selected=audiofilterselected;\r
- return &audiofilterlist;\r
-}\r
-\r
-const AudioFilterDescList *AudioWin::getMp3AudioFilterList(int &selected)\r
-{\r
- selected=mp3audiofilterselected;\r
- return &mp3audiofilterlist;\r
-}\r
-bool AudioWin::selectMp3AudioFilter(int filter)\r
-{\r
- mp3audiofilterselected=filter;\r
- return true;\r
- \r
-}\r
-\r
-bool AudioWin::selectAudioFilter(int filter)\r
-{\r
- audiofilterselected=filter;\r
- return true;\r
- \r
-}\r
-\r
-long long AudioWin::SetStartOffset(long long curreftime, bool *rsync){\r
- VideoWin *vw=(VideoWin*)Video::getInstance();\r
- return vw->SetStartAudioOffset(curreftime,rsync);\r
-}\r
-\r
-void AudioWin::ResetTimeOffsets() {\r
- VideoWin *vw=(VideoWin*)Video::getInstance();\r
- vw->ResetTimeOffsets();\r
-}\r
-\r
-bool AudioWin::supportsAc3(){\r
- VideoWin *vw=(VideoWin*)Video::getInstance();\r
- return vw->supportsAc3();\r
-}\r
-\r
-#ifdef DEV\r
-int AudioWin::test()\r
-{\r
- return 0;\r
-}\r
-#endif\r
-\r
-\r
-\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 "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;i<audiofilterlist.size();i++)
+ {
+ if (audiofilterlist[i].displayname) delete [] audiofilterlist[i].displayname;
+ if (audiofilterlist[i].friendlyname) delete [] audiofilterlist[i].friendlyname;
+ }
+ audiofilterlist.clear();
+
+ for (i=0;i<mp3audiofilterlist.size();i++)
+ {
+ if (mp3audiofilterlist[i].displayname) delete [] mp3audiofilterlist[i].displayname;
+ if (mp3audiofilterlist[i].friendlyname) delete [] mp3audiofilterlist[i].friendlyname;
+ }
+ mp3audiofilterlist.clear();
+
+}
+
+int AudioWin::init(UCHAR tstreamType)
+{
+ if (initted) return 0;
+ initFilterDatabase();
+ initMp3FilterDatabase();
+ initted = 1;
+ return 1;
+}
+
+int AudioWin::shutdown()
+{
+ if (!initted) return 0;
+ initted = 0;
+ return 1;
+}
+
+int AudioWin::write(char *buf, int len)
+{
+ return 0; //write(fdAudio, buf, len);
+}
+
+int AudioWin::setStreamType(UCHAR type)
+{
+ ((VideoWin*)VideoWin::getInstance())->setAudioStreamType(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 <audiofilterlist.size();i++)
+ {
+ if (strcmp(name,audiofilterlist[i].displayname)==0)
+ {
+ audiofilterselected = i;
+ break;
+ }
+ }
+ }
+ name=vdr->configLoad("DirectShow","Mp3AudioFilter");
+
+ if (name != NULL)
+ {
+ for (int i = 0;i <mp3audiofilterlist.size();i++)
+ {
+ if (strcmp(name,mp3audiofilterlist[i].displayname)==0)
+ {
+ mp3audiofilterselected = i;
+ break;
+ }
+ }
+ }
+ return true;
+
+}
+
+bool AudioWin::saveOptionstoServer()
+{
+ if (audiofilterselected!=-1) {
+ VDR::getInstance()->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 <audiofilterlist.size();i++)
+ {
+ audiofilterselected = i;
+ notset=true;
+ break;
+ }
+ }
+ IBindCtx *bindctx=NULL;
+ if (CreateBindCtx(0,&bindctx)!=S_OK) return NULL;
+ IMoniker * moni=NULL;
+ LPCOLESTR name=new WCHAR[strlen(audiofilterlist[audiofilterselected].displayname)+1];
+ mbstowcs((wchar_t*)name,audiofilterlist[audiofilterselected].displayname,
+ strlen(audiofilterlist[audiofilterselected].displayname)+1);
+ ULONG eater;
+ if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK)
+ {
+ if (moni->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 <mp3audiofilterlist.size();i++)
+ {
+ mp3audiofilterselected = i;
+ notset=true;
+ break;
+ }
+ }
+ IBindCtx *bindctx=NULL;
+ if (CreateBindCtx(0,&bindctx)!=S_OK) return NULL;
+ IMoniker * moni=NULL;
+ LPCOLESTR name=new WCHAR[strlen(mp3audiofilterlist[mp3audiofilterselected].displayname)+1];
+ mbstowcs((wchar_t*)name,mp3audiofilterlist[mp3audiofilterselected].displayname,
+ strlen(mp3audiofilterlist[mp3audiofilterselected].displayname)+1);
+ ULONG eater;
+ if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK)
+ {
+ if (moni->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
+
+
+
-/*\r
- Copyright 2004-2005 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#include "boxstack.h"\r
-\r
-#include "command.h"\r
-#include "remote.h"\r
-#include "log.h"\r
-\r
-BoxStack* BoxStack::instance = NULL;\r
-\r
-BoxStack::BoxStack()\r
-{\r
- if (instance) return;\r
- instance = this;\r
- initted = 0;\r
- numBoxes = 0;\r
-}\r
-\r
-BoxStack::~BoxStack()\r
-{\r
- instance = NULL;\r
-}\r
-\r
-BoxStack* BoxStack::getInstance()\r
-{\r
- return instance;\r
-}\r
-\r
-int BoxStack::init()\r
-{\r
- if (initted) return 0;\r
- initted = 1;\r
- \r
-#ifndef WIN32\r
- pthread_mutex_init(&boxLock, NULL);\r
-#else\r
- boxLock = CreateMutex(NULL,FALSE,NULL);\r
-#endif\r
-\r
- return 1;\r
-}\r
-\r
-int BoxStack::shutdown()\r
-{\r
- if (!initted) return 0;\r
-\r
- // FIXME don't think this can work properly, removeAll leaves the wallpaper there!\r
- removeAll();\r
-\r
- initted = 0;\r
- return 1;\r
-}\r
-\r
-int BoxStack::add(Boxx* v)\r
-{\r
- if (!initted) return 0;\r
- Log::getInstance()->log("BoxStack", Log::DEBUG, "add called"); \r
-#ifndef WIN32\r
- pthread_mutex_lock(&boxLock);\r
-#else\r
- WaitForSingleObject(boxLock, INFINITE);\r
-#endif\r
- Log::getInstance()->log("BoxStack", Log::DEBUG, "Locked for add"); \r
- \r
- if (numBoxes == 16)\r
- { //Error\r
- Log::getInstance()->log("BoxStack", Log::ERR, "More than 16 boxes! Unlocked for add"); \r
-#ifndef WIN32\r
- pthread_mutex_unlock(&boxLock);\r
-#else\r
- ReleaseMutex(boxLock);\r
-#endif\r
- return 0;\r
- }\r
- boxes[numBoxes++] = v;\r
-\r
-#ifndef WIN32\r
- pthread_mutex_unlock(&boxLock);\r
-#else\r
- ReleaseMutex(boxLock);\r
-#endif\r
-\r
- Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for add");\r
- \r
- return 1;\r
-}\r
-\r
-// ---------------------------------------------------- REMOVE CODE\r
-\r
-int BoxStack::remove(Boxx* toDelete)\r
-{\r
- if (!initted) return 0;\r
-\r
- #ifndef WIN32\r
- pthread_mutex_lock(&boxLock);\r
- #else\r
- WaitForSingleObject(boxLock, INFINITE);\r
- #endif\r
- Log::getInstance()->log("BoxStack", Log::DEBUG, "Locked for remove"); \r
- \r
- if (numBoxes == 0)\r
- {\r
- #ifndef WIN32\r
- pthread_mutex_unlock(&boxLock);\r
- #else\r
- ReleaseMutex(boxLock);\r
- #endif\r
- Log::getInstance()->log("BoxStack", Log::ERR, "Unlocked for remove numBoxes == 0"); \r
- return 0;\r
- }\r
-\r
-// Log::getInstance()->log("BoxStack", Log::DEBUG, "entering remove, numBoxes=%i", numBoxes);\r
-\r
- int i;\r
-\r
- if (toDelete == NULL)\r
- {\r
- toDelete = boxes[numBoxes-1];\r
- i = numBoxes - 1;\r
- }\r
- else\r
- {\r
- // to be deleted box is more likely to be at the top\r
- for (i = numBoxes-1; i >= 0; i--)\r
- {\r
-// Log::getInstance()->log("BoxStack", Log::DEBUG, "todel: %p, i=%i, boxes[i]=%p", toDelete, i, boxes[i]);\r
- if (boxes[i] == toDelete) break;\r
- }\r
-\r
- if (i == -1)\r
- {\r
- // not a Box we have!\r
- // FIXME\r
- #ifndef WIN32\r
- pthread_mutex_unlock(&boxLock);\r
- #else\r
- ReleaseMutex(boxLock);\r
- #endif\r
- Log::getInstance()->log("BoxStack", Log::ERR, "Unlocked for remove - no boxx deleted"); \r
- return 0;\r
- }\r
- }\r
-\r
-#ifndef WIN32\r
- pthread_mutex_unlock(&boxLock);\r
-#else\r
- ReleaseMutex(boxLock);\r
-#endif\r
-\r
-toDelete->preDelete();\r
-\r
-#ifndef WIN32\r
- pthread_mutex_lock(&boxLock);\r
-#else\r
- WaitForSingleObject(boxLock, INFINITE);\r
-#endif\r
-\r
-// Log::getInstance()->log("BoxStack", Log::DEBUG, "Starting deleteBox");\r
- deleteBox(i);\r
-// Log::getInstance()->log("BoxStack", Log::DEBUG, "Done deleteBox");\r
-\r
- // Shift the boxes on top down one\r
- --numBoxes;\r
- for(int j = i; j < numBoxes; j++) boxes[j] = boxes[j+1];\r
-\r
- // If there is only the wallpaper left signal command\r
- if (numBoxes == 1)\r
- {\r
- Message* m = new Message();\r
- m->to = Command::getInstance();\r
- m->message = Message::LAST_VIEW_CLOSE;\r
- Command::getInstance()->postMessageNoLock(m);\r
- }\r
-\r
-#ifndef WIN32\r
- pthread_mutex_unlock(&boxLock);\r
-#else\r
- ReleaseMutex(boxLock);\r
-#endif\r
- Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for remove"); \r
- \r
- // Delete the box\r
- //AVO: do this delete outside the lock to allow for recursive calls within the destructor\r
- // as this box is not in the stack any more, there is no chance for a second delete\r
- Log::getInstance()->log("BoxStack", Log::DEBUG, "remove: going to delete boxx %p, num %d", toDelete, numBoxes); \r
- delete toDelete;\r
-\r
- return 1;\r
-}\r
-\r
-/////////////////////////////////////////////////////////////////////////////\r
-// NEW STUFF\r
-/////////////////////////////////////////////////////////////////////////////\r
-\r
-void BoxStack::deleteBox(int z)\r
-{\r
-// Log::getInstance()->log("BoxStack", Log::DEBUG, "Delete box %i of %i", z, numBoxes);\r
- RegionList rl;\r
- boxSplit(boxes[z]->area, z + 1, numBoxes, 1, rl);\r
- while(!rl.empty())\r
- {\r
- repaintRevealed(z, rl.front());\r
- rl.pop_front();\r
- }\r
-}\r
-\r
-void BoxStack::redrawAllBoxes()\r
-{\r
-#ifndef WIN32\r
- pthread_mutex_lock(&boxLock);\r
-#else\r
- WaitForSingleObject(boxLock, INFINITE);\r
-#endif\r
-\r
- for (int z = 0; z < numBoxes; z++)\r
- {\r
- boxes[z]->draw();\r
- }\r
-\r
-\r
-#ifndef WIN32\r
- pthread_mutex_unlock(&boxLock);\r
-#else\r
- ReleaseMutex(boxLock);\r
-#endif\r
- update(NULL,NULL); // should blt all\r
-}\r
-\r
-void BoxStack::update(Boxx* toUpdate, Region* regionToUpdate)\r
-{\r
- Log::getInstance()->log("BoxStack", Log::DEBUG, "Update called");\r
- if (!initted) return; // it is allowed to call this before init\r
-#ifndef WIN32\r
- pthread_mutex_lock(&boxLock);\r
-#else\r
- WaitForSingleObject(boxLock, INFINITE);\r
-#endif\r
- Log::getInstance()->log("BoxStack", Log::DEBUG, "Locked for update");\r
-\r
- // Get the z index of the box\r
- int z = 0;\r
- if (toUpdate)\r
- {\r
- for (z = 0; z < numBoxes; z++)\r
- {\r
- if (boxes[z] == toUpdate) break;\r
- }\r
-\r
- if (z == numBoxes)\r
- {\r
- // not a Box we have!\r
- #ifndef WIN32\r
- pthread_mutex_unlock(&boxLock);\r
- #else\r
- ReleaseMutex(boxLock);\r
- #endif \r
- Log::getInstance()->log("BoxStack", Log::ERR, "Unlocked for update! box not inside boxstack");\r
- return;\r
- }\r
- }\r
- else\r
- {\r
- toUpdate = boxes[0];\r
- }\r
-\r
- if (!toUpdate) {\r
-#ifndef WIN32\r
- pthread_mutex_unlock(&boxLock);\r
-#else\r
- ReleaseMutex(boxLock);\r
-#endif\r
- Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for update, no box present");\r
- return ;\r
- }\r
-\r
- // get the region for the whole box, could be less than that\r
- // for smaller updates\r
-\r
- Region r = toUpdate->area;\r
-\r
- if (regionToUpdate)\r
- {\r
- // Can be null if the whole box should be updated\r
- // If this is given the numbers are relative to the size of the box, not the screen\r
-\r
- r.x += regionToUpdate->x;\r
- r.y += regionToUpdate->y;\r
- r.w = regionToUpdate->w;\r
- r.h = regionToUpdate->h;\r
- }\r
-\r
- RegionList rl;\r
-\r
- Region r2;\r
- boxSplit(r, z+1, numBoxes, 1, rl);\r
- while(!rl.empty())\r
- {\r
- r2 = rl.front();\r
- r2.z = z;\r
- boxes[z]->blt(r2);\r
- rl.pop_front();\r
- }\r
-\r
-#ifndef WIN32\r
- pthread_mutex_unlock(&boxLock);\r
-#else\r
- ReleaseMutex(boxLock);\r
-#endif \r
- Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for update");\r
-}\r
-\r
-void BoxStack::repaintRevealed(int x, Region r)\r
-{\r
- RegionList rl;\r
- boxSplit(r, x - 1, -1, -1, rl);\r
-\r
- Region r2;\r
- while(!rl.empty())\r
- {\r
- r2 = rl.front();\r
- boxes[r2.z]->blt(r2);\r
- rl.pop_front();\r
- }\r
-}\r
-\r
-void BoxStack::boxSplit(Region r, int start, int end, int direction, RegionList& rl)\r
-{\r
-// 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);\r
-\r
- for(int z = start; z != end; z += direction)\r
- {\r
- if (r.overlappedBy(boxes[z]->area))\r
- {\r
-// printf("Z=%i S=%i E=%i D=%i: %i overlaps\n", z, start, end, direction, z);\r
-\r
- int top = r.y;\r
- int btm = r.y2();\r
-\r
- if (boxes[z]->area.y > r.y)\r
- {\r
-// 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);\r
- top = boxes[z]->area.y;\r
- Region newR;\r
- newR.x = r.x;\r
- newR.y = r.y;\r
- newR.w = r.w;\r
- newR.h = boxes[z]->area.y - r.y;\r
- boxSplit(newR, z + direction, end, direction, rl);\r
-\r
- if (direction == -1)\r
- {\r
- Region newR2;\r
- newR2.x = r.x;\r
- newR2.y = boxes[z]->area.y;\r
- newR2.w = r.w;\r
- newR2.h = r.h - newR.h;\r
- boxSplit(newR2, z, end, -1, rl);\r
- return;\r
- }\r
- }\r
-\r
- if (boxes[z]->area.y2() < r.y2())\r
- {\r
-// 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);\r
- btm = boxes[z]->area.y2();\r
- Region newR;\r
- newR.x = r.x;\r
- newR.y = boxes[z]->area.y2() + 1;\r
- newR.w = r.w;\r
- newR.h = r.y2() - newR.y + 1;\r
- boxSplit(newR, z + direction, end, direction, rl);\r
-\r
- if (direction == -1)\r
- {\r
- Region newR2;\r
- newR2.x = r.x;\r
- newR2.y = r.y;\r
- newR2.w = r.w;\r
- newR2.h = r.h - newR.h;\r
- boxSplit(newR2, z, end, -1, rl);\r
- return;\r
- }\r
- }\r
-\r
- if (boxes[z]->area.x > r.x)\r
- {\r
-// 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);\r
- Region newR;\r
- newR.x = r.x;\r
- newR.y = top;\r
- newR.w = boxes[z]->area.x - r.x;\r
- newR.h = btm - top + 1;\r
- boxSplit(newR, z + direction, end, direction, rl);\r
-\r
- if (direction == -1)\r
- {\r
- Region newR2;\r
- newR2.x = r.x + newR.w;\r
- newR2.y = r.y;\r
- newR2.w = r.w - newR.w;\r
- newR2.h = r.h;\r
- boxSplit(newR2, z, end, -1, rl);\r
- return;\r
- }\r
- }\r
-\r
- if (boxes[z]->area.x2() < r.x2())\r
- {\r
-// 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);\r
- Region newR;\r
- newR.x = boxes[z]->area.x2() + 1;\r
- newR.y = top;\r
- newR.w = r.x2() - newR.x + 1;\r
- newR.h = btm - top + 1;\r
- boxSplit(newR, z + direction, end, direction, rl);\r
-\r
- if (direction == -1)\r
- {\r
- Region newR2;\r
- newR2.x = r.x;\r
- newR2.y = r.y;\r
- newR2.w = r.w - newR.w;\r
- newR2.h = r.h;\r
- boxSplit(newR2, z, end, -1, rl);\r
- return;\r
- }\r
- }\r
-\r
- if (direction == -1)\r
- {\r
- // we are going down the stack\r
- // r is underlapped by boxes[z]\r
- // but we have not split\r
- // Therefore this region under test is\r
- // completely covering boxes[z]\r
-\r
- // don't go any further down, generate a region and quit\r
-\r
-// printf("Repaint region: %i %i %i %i\n", r.x, r.y, r.w, r.h);\r
- r.z = z;\r
- rl.push_front(r);\r
- }\r
-\r
-// printf("Returning from Z=%i\n", z);\r
- return;\r
- }\r
- else\r
- {\r
-// printf("Z=%i S=%i E=%i D=%i: %i does not overlap\n", z, start, end, direction, z);\r
- }\r
- }\r
-\r
- // if direction = 1 then we have come to a region that is\r
- // entirely clear of higher boxes and needs to be redrawn\r
-\r
- // if direction = -1 then we have come to a region that is on\r
- // the very bottom with nothing below it to repaint.\r
- // do nothing. stale window data will be left on screen?\r
-\r
- if (direction == 1)\r
- {\r
- rl.push_front(r);\r
- }\r
-}\r
-\r
-/////////////////////////////////////////////////////////////////////////////\r
-// END NEW STUFF\r
-/////////////////////////////////////////////////////////////////////////////\r
-\r
-// ---------------------------------------------------- END OF REMOVE CODE\r
-\r
-\r
-void BoxStack::removeAll()\r
-{\r
- // 1.. Don't delete wallpaper. No point.\r
-\r
- // Need locking on this one??\r
- \r
- // This is pretty silly now that preDelete needs mutex unlocked\r
- \r
- Boxx* toDel = NULL;\r
- \r
- while(numBoxes > 1)\r
- {\r
- #ifndef WIN32\r
- pthread_mutex_lock(&boxLock);\r
- #else\r
- WaitForSingleObject(boxLock, INFINITE);\r
- #endif \r
-\r
- if (numBoxes == 1)\r
- {\r
- #ifndef WIN32\r
- pthread_mutex_unlock(&boxLock);\r
- #else\r
- ReleaseMutex(boxLock);\r
- #endif\r
- break;\r
- } \r
- \r
- toDel = boxes[numBoxes - 1];\r
-\r
- #ifndef WIN32\r
- pthread_mutex_unlock(&boxLock);\r
- #else\r
- ReleaseMutex(boxLock);\r
- #endif\r
-\r
- toDel->preDelete();\r
- \r
- #ifndef WIN32\r
- pthread_mutex_lock(&boxLock);\r
- #else\r
- WaitForSingleObject(boxLock, INFINITE);\r
- #endif \r
-\r
- // If boxes[numBoxes - 1] isn't toDel then there's a problem\r
- if (boxes[numBoxes - 1] == toDel)\r
- {\r
- --numBoxes;\r
- }\r
- else\r
- {\r
- Log::getInstance()->log("BoxStack", Log::ERR, "Can this actually happen? Why?");\r
- toDel = NULL;\r
- }\r
-\r
- #ifndef WIN32\r
- pthread_mutex_unlock(&boxLock);\r
- #else\r
- ReleaseMutex(boxLock);\r
- #endif\r
-\r
- //AVO: do the delete outside the lock to allow for recursive deletes\r
- Log::getInstance()->log("BoxStack", Log::DEBUG, "going to delete boxx %p, num=%d", toDel, numBoxes);\r
- if (toDel) delete toDel;\r
- }\r
-}\r
-\r
-int BoxStack::handleCommand(int command)\r
-{\r
- int retVal;\r
- int retVal2 = 0;\r
- int i;\r
-\r
- if (command != Remote::NA_NONE)\r
- {\r
- // handle command return values\r
- // 0 - drop through to next box\r
- // 1 - dont drop to next box, but not handled\r
- // 2 - handled - stop command here\r
- // 4 - handled - delete this box\r
-\r
- for (i=numBoxes-1; i>=0; i--)\r
- {\r
-// Log::getInstance()->log("BoxStack", Log::DEBUG, "Giving command to i=%i", i);\r
- retVal = boxes[i]->handleCommand(command);\r
- if (retVal == 1)\r
- {\r
- // not handled but don't give to any more boxes\r
- return 0;\r
- }\r
-\r
- if (retVal == 2)\r
- {\r
- // command handled\r
- retVal2 = 1;\r
- break;\r
- }\r
- else if (retVal == 4)\r
- {\r
-// Log::getInstance()->log("BoxStack", Log::DEBUG, "Return 4: i=%i, boxes[i]=%p", i, boxes[i]);\r
- remove(boxes[i]);\r
- retVal2 = 1;\r
- break;\r
- }\r
- }\r
- }\r
- else\r
- {\r
- // fake the return code\r
- retVal2 = 2;\r
- }\r
-\r
- return retVal2;\r
-}\r
-\r
-void BoxStack::processMessage(Message* m)\r
-{\r
- if (m->to != this)\r
- {\r
- for (int i = numBoxes-1; i >= 0; i--)\r
- {\r
- if (boxes[i] == m->to)\r
- {\r
- Log::getInstance()->log("BoxStack", Log::DEBUG, "sending message from box %p to box %p %lu", m->from, m->to, m->message);\r
- boxes[i]->processMessage(m);\r
- return;\r
- }\r
- }\r
- return;\r
- }\r
-\r
- /* Handle mouse events*/\r
- // They come in with m->to = NULL? and just need to be delivered to top box?\r
- if ((numBoxes > 1) && ((m->message == Message::MOUSE_MOVE) || (m->message == Message::MOUSE_LBDOWN)\r
- || (m->message == Message::MOUSE_ANDROID_SCROLL)))\r
- {\r
- boxes[numBoxes-1]->processMessage(m);\r
- return;\r
- }\r
-\r
- Log::getInstance()->log("BoxStack", Log::DEBUG, "it's for meeee!");\r
-\r
- switch(m->message)\r
- {\r
- case Message::CLOSE_ME:\r
- {\r
- remove((Boxx*)m->from);\r
- break;\r
- }\r
- case Message::ADD_VIEW: // currently not used by anything but it might come in useful again\r
- {\r
- Boxx* toAdd = (Boxx*)m->parameter;\r
- add(toAdd);\r
- toAdd->draw();\r
- update(toAdd);\r
- break;\r
- }\r
- }\r
-}\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 "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::redrawAllBoxes()
+{
+#ifndef WIN32
+ pthread_mutex_lock(&boxLock);
+#else
+ WaitForSingleObject(boxLock, INFINITE);
+#endif
+
+ for (int z = 0; z < numBoxes; z++)
+ {
+ boxes[z]->draw();
+ }
+
+
+#ifndef WIN32
+ pthread_mutex_unlock(&boxLock);
+#else
+ ReleaseMutex(boxLock);
+#endif
+ update(NULL,NULL); // should blt all
+}
+
+void BoxStack::update(Boxx* toUpdate, Region* regionToUpdate)
+{
+ Log::getInstance()->log("BoxStack", Log::DEBUG, "Update called");
+ if (!initted) return; // it is allowed to call this before init
+#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];
+ }
+
+ if (!toUpdate) {
+#ifndef WIN32
+ pthread_mutex_unlock(&boxLock);
+#else
+ ReleaseMutex(boxLock);
+#endif
+ Log::getInstance()->log("BoxStack", Log::DEBUG, "Unlocked for update, no box present");
+ return ;
+ }
+
+ // 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;
+ }
+ }
+}
-/*\r
- Copyright 2007 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#include "boxx.h"\r
-#include "bitmap.h"\r
-#include "log.h"\r
-#include "osd.h"\r
-#include <stdlib.h>\r
-\r
-char Boxx::numBoxxes = 0;\r
-\r
-Boxx::Boxx()\r
-{\r
- // I want a parent box or a surface.\r
- parent = NULL;\r
- surface = NULL;\r
-\r
- area.x = 0;\r
- area.y = 0;\r
- area.w = 0;\r
- area.h = 0;\r
-\r
- paraVSpace = 6; // default gap for drawPara\r
-\r
- backgroundColourSet = false;\r
- visible = true;\r
-\r
- numBoxxes++;\r
- Log::getInstance()->log("Boxx", Log::DEBUG, "Construct, now %u", numBoxxes);\r
-}\r
-\r
-Boxx::~Boxx()\r
-{\r
- if (surface) delete surface;\r
- numBoxxes--;\r
- Log::getInstance()->log("Boxx", Log::DEBUG, "Destruct, now %u", numBoxxes);\r
-}\r
-\r
-void Boxx::draw()\r
-{\r
- //Log::getInstance()->log("Boxx", Log::DEBUG, "Draw this %p surface %p", this, surface);\r
- if (backgroundColourSet) fillColour(backgroundColour);\r
-\r
- Boxx* currentBoxx;\r
- vector<Boxx*>::iterator j;\r
- //int count=0;\r
- for (j = children.begin(); j != children.end(); j++)\r
- {\r
- currentBoxx = *j;\r
- // Log::getInstance()->log("Boxx", Log::DEBUG, "Draw child %d %d", count,currentBoxx);\r
- if (currentBoxx->getVisible()) currentBoxx->draw();\r
- // count++;\r
- } \r
- // Log::getInstance()->log("Boxx", Log::DEBUG, "Draw this %p surface %p End", this, surface);\r
-}\r
-\r
-void Boxx::setSize(UINT w, UINT h)\r
-{\r
- area.w = w;\r
- area.h = h;\r
-}\r
-\r
-void Boxx::setPosition(UINT x, UINT y)\r
-{\r
- area.x = x;\r
- area.y = y;\r
-}\r
-\r
-void Boxx::createBuffer()\r
-{\r
- surface = Osd::getInstance()->createNewSurface();\r
- surface->create(area.w, area.h);\r
-}\r
-\r
-void Boxx::add(Boxx* newChild)\r
-{\r
- newChild->setParent(this);\r
- children.push_back(newChild);\r
-}\r
-\r
-void Boxx::remove(Boxx* oldChild)\r
-{\r
- for(vector<Boxx*>::iterator i = children.begin(); i != children.end(); i++)\r
- {\r
- if (*i == oldChild)\r
- {\r
- children.erase(i);\r
- return;\r
- }\r
- }\r
- Log::getInstance()->log("Boxx", Log::ERR, "Remove child box called, child %p not found", oldChild);\r
-}\r
-\r
-void Boxx::setParent(Boxx* newParent)\r
-{\r
- parent = newParent;\r
-}\r
-\r
-void Boxx::setBackgroundColour(const DrawStyle& Tcolour)\r
-{\r
- backgroundColour = Tcolour;\r
- backgroundColourSet = true;\r
-}\r
-\r
-void Boxx::setVisible(bool isVisible)\r
-{\r
- visible = isVisible;\r
-}\r
-\r
-bool Boxx::getVisible()\r
-{\r
- return visible;\r
-}\r
-\r
-void Boxx::setGap(UINT gap)\r
-{\r
- paraVSpace = gap;\r
-}\r
-\r
-void Boxx::blt(Region& r)\r
-{\r
- /* surface update to screen needs:\r
- source x distance into this surface\r
- source y distance into this surface\r
- width of update\r
- height of update\r
- destination x on screen\r
- destination y on screen\r
- */\r
-\r
- if (parent) abort(); // if (parent) then this is a child boxx. It can not blt.\r
-\r
- // this shouldn't be here\r
- r.x -= area.x;\r
- r.y -= area.y;\r
-\r
- surface->updateToScreen(r.x, r.y, r.w, r.h, area.x + r.x, area.y + r.y);\r
-\r
-}\r
-\r
-int Boxx::getScreenX()\r
-{\r
- if (parent) return area.x + parent->getScreenX();\r
- return area.x;\r
-}\r
-\r
-int Boxx::getScreenY()\r
-{\r
- if (parent) return area.y + parent->getScreenY();\r
- return area.y;\r
-}\r
-\r
-int Boxx::getRootBoxOffsetX() // convert this to be getX and silently do the parent/not thing? same for Y below?\r
-{\r
- if (parent) return area.x + parent->getRootBoxOffsetX();\r
- return 0;\r
-}\r
-\r
-int Boxx::getRootBoxOffsetY()\r
-{\r
- if (parent) return area.y + parent->getRootBoxOffsetY();\r
- return 0;\r
-}\r
-\r
-int Boxx::getX()\r
-{\r
- return area.x;\r
-}\r
-\r
-int Boxx::getY()\r
-{\r
- return area.y;\r
-}\r
-\r
-int Boxx::getX2()\r
-{\r
- return area.x + area.w;\r
-}\r
-\r
-int Boxx::getY2()\r
-{\r
- return area.y + area.h;\r
-}\r
-\r
-UINT Boxx::getWidth()\r
-{\r
- return area.w;\r
-}\r
-\r
-UINT Boxx::getHeight()\r
-{\r
- return area.h;\r
-}\r
-\r
-// FIXME Clean up the code to use just one of the following\r
-\r
-Region* Boxx::getRegion()\r
-{\r
- return &area;\r
-}\r
-\r
-Region Boxx::getRegionR()\r
-{\r
- return area;\r
-}\r
-\r
-void Boxx::getRootBoxRegion(Region* r)\r
-{\r
- // Returns a region that describes the position of this box on the box with the surface\r
- // To be used for boxstack->update calls\r
-\r
- r->x = getRootBoxOffsetX();\r
- r->y = getRootBoxOffsetY();\r
- r->w = area.w;\r
- r->h = area.h; \r
-}\r
-\r
-// Level 1 drawing functions\r
-\r
-void Boxx::fillColour(const DrawStyle& colour)\r
-{\r
- rectangle(0, 0, area.w, area.h, colour);\r
-}\r
-\r
-void Boxx::drawPara(const char* text, int x, int y, const DrawStyle& colour)\r
-{\r
- char line[256];\r
- int lineHeight = getFontHeight() + paraVSpace;\r
-\r
- float lineWidth;\r
- float thisCharWidth;\r
- int textPos;\r
- int linePos;\r
- int ypos;\r
- int printLine;\r
-\r
- textPos = 0;\r
- ypos = y;\r
-\r
- while(1)\r
- {\r
- linePos = 0;\r
- lineWidth = 0;\r
- while(1)\r
- {\r
- printLine = 0;\r
- unsigned int cur_length=1;\r
- wchar_t cur_char=getWChar(text+textPos,&cur_length);\r
-\r
- if (cur_char == '\0') break;\r
-\r
- if (cur_char == '\n')\r
- {\r
- textPos+=cur_length; // ignore the \n\r
- printLine = 1;\r
- break;\r
- }\r
- thisCharWidth = charWidth(cur_char);\r
- if ((lineWidth + thisCharWidth) > (int)(area.w - (2 * paraMargin)))\r
- {\r
- // this character would break the right margin\r
- if (cur_char == ' ')\r
- {\r
- // this char is a space, ignore and break\r
- textPos+=cur_length;\r
- break;\r
- }\r
- else\r
- {\r
- // Need to go back to the last space in the line\r
- while ((cur_char != ' ') && (linePos >= 0))\r
- {\r
- textPos-=cur_length;\r
- cur_char=getWChar(text+textPos,&cur_length);\r
- linePos--;\r
- }\r
- // Now take the space we just found\r
- textPos+=cur_length;\r
- break;\r
- }\r
- }\r
- for (int n=0;n<cur_length;n++) line[linePos++] = text[textPos+n];\r
- lineWidth += thisCharWidth;\r
- textPos+=cur_length;\r
- }\r
-\r
-// line[linePos++] = '\0';\r
- if (linePos>=0) line[linePos++] = '\0'; //Here is the change\r
-\r
- if (printLine || (linePos > 1)) // if some text was put in line\r
- {\r
- drawText(line, x, ypos, colour);\r
- ypos += lineHeight;\r
- if (ypos > (int)(area.h - lineHeight)) break;\r
- }\r
- else\r
- {\r
- break;\r
- }\r
- }\r
-}\r
-\r
-void Boxx::rectangle(Region& region, const DrawStyle& colour)\r
-{\r
- rectangle(region.x, region.y, region.w, region.h, colour);\r
-}\r
-\r
-// Level 0 drawing functions\r
-\r
-void Boxx::rectangle(UINT x, UINT y, UINT w, UINT h, const DrawStyle& colour)\r
-{\r
- if (parent) parent->rectangle(area.x + x, area.y + y, w, h, colour);\r
- else surface->fillblt(x, y, w, h, colour);\r
-}\r
-\r
-void Boxx::drawText(const char* text, int x, int y, const DrawStyle& colour)\r
-{\r
- if (parent) parent->drawText(text, area.x + x, area.y + y, colour);\r
- else surface->drawText(text, x, y, colour);\r
-}\r
-\r
-void Boxx::drawText(const char* text, int x, int y, int width, const DrawStyle& colour)\r
-{\r
- if (parent) parent->drawText(text, area.x + x, area.y + y, width, colour);\r
- else surface->drawText(text, x, y, width, colour);\r
-}\r
-\r
-void Boxx::drawTextRJ(const char* text, int x, int y, const DrawStyle& colour)\r
-{\r
- if (parent) parent->drawTextRJ(text, area.x + x, area.y + y, colour);\r
- else surface->drawTextRJ(text, x, y, colour);\r
-}\r
-\r
-void Boxx::drawTextCentre(const char* text, int x, int y, const DrawStyle& colour)\r
-{\r
- if (parent) parent->drawTextCentre(text, area.x + x, area.y + y, colour);\r
- else surface->drawTextCentre(text, x, y, colour);\r
-}\r
-// Now deprecated\r
-/*\r
-void Boxx::drawPixelAlpha(UINT x, UINT y, const Colour& colour,bool fastdraw)\r
-{\r
- if (parent) parent->drawPixelAlpha(area.x + x, area.y + y, colour,fastdraw);\r
- else\r
- {\r
- int c = ( (colour.alpha << 24 )\r
- | (colour.red << 16)\r
- | (colour.green << 8)\r
- | (colour.blue ) );\r
-\r
- surface->drawPixel(x, y, c,fastdraw);\r
- }\r
-}\r
-\r
-void Boxx::drawPixel(UINT x, UINT y, const Colour& colour, bool fastdraw)\r
-{\r
- if (parent) parent->drawPixel(area.x + x, area.y + y, colour,fastdraw);\r
- else\r
- {\r
- int c = ( (0xFF000000 )\r
- | (colour.red << 16)\r
- | (colour.green << 8)\r
- | (colour.blue ) );\r
-\r
- surface->drawPixel(x, y, c,fastdraw);\r
- }\r
-}\r
-*/\r
-\r
-void Boxx::drawTTChar(int ox, int oy,int x, int y, cTeletextChar c)\r
-{\r
- if (parent) parent->drawTTChar(area.x + ox, area.y + oy, x,y,c);\r
- else if (surface) surface->drawTTChar(ox, oy,x,y,c);\r
-\r
-}\r
-\r
-void Boxx::drawBitmap(UINT x, UINT y, const Bitmap& bm, const DisplayRegion & region)\r
-{\r
- if (parent) parent->drawBitmap(area.x + x, area.y + y, bm, region);\r
- else if (surface) surface->drawBitmap(x, y, bm, region);\r
-}\r
-\r
-void Boxx::drawJpeg(const char *fileName,int x, int y,int *width, int *height)\r
-{\r
- if (parent) parent->drawJpeg(fileName,area.x +x,area.y +y,width,height);\r
- else if (surface) surface->drawJpeg(fileName,x,y,width,height);\r
-}\r
-\r
-void Boxx::drawMonoBitmap(UCHAR*base, int dx, int dy, unsigned int height,unsigned int width, Colour& nextColour)\r
-{\r
- if (parent) parent->drawMonoBitmap(base, area.x +dx,area.y +dy, height,width, nextColour);\r
- else if (surface) surface->drawMonoBitmap(base, dx,dy, height,width, nextColour);\r
-}\r
-\r
-int Boxx::getFontHeight()\r
-{\r
- if (parent) return parent->getFontHeight();\r
- else if (surface) return surface->getFontHeight();\r
- else return 18;\r
-}\r
-\r
-void Boxx::startFastDraw()\r
-{\r
- if (parent) parent->startFastDraw();\r
- else\r
- {\r
- if (surface) surface->startFastDraw();\r
- }\r
-}\r
-\r
-void Boxx::endFastDraw()\r
-{\r
- if (parent) parent->endFastDraw();\r
- else\r
- {\r
- if (surface) surface->endFastDraw();\r
- }\r
-}\r
- \r
-\r
-float Boxx::charWidth(wchar_t c)\r
-{\r
- if (parent) return parent->charWidth(c);\r
- else if (surface) return surface->getCharWidth(c);\r
- else return 16.; //?\r
-}\r
-\r
-wchar_t Boxx::getWChar(const char* str, unsigned int *length)\r
-{\r
- if (parent) return parent->getWChar(str,length);\r
- else if (surface) return surface->getWChar(str,length);\r
- else return '?'; //?\r
-}\r
-\r
-Surface * Boxx::getSurface() {\r
- if (parent) return parent->getSurface();\r
- return surface;\r
-}\r
-\r
+/*
+ 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 <stdlib.h>
+
+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<Boxx*>::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<Boxx*>::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 DrawStyle& 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 DrawStyle& colour)
+{
+ rectangle(0, 0, area.w, area.h, colour);
+}
+
+void Boxx::drawPara(const char* text, int x, int y, const DrawStyle& colour)
+{
+ char line[256];
+ int lineHeight = getFontHeight() + paraVSpace;
+
+ float lineWidth;
+ float thisCharWidth;
+ int textPos;
+ int linePos;
+ int ypos;
+ int printLine;
+
+ textPos = 0;
+ ypos = y;
+
+ while(1)
+ {
+ linePos = 0;
+ lineWidth = 0;
+ while(1)
+ {
+ printLine = 0;
+ unsigned int cur_length=1;
+ wchar_t cur_char=getWChar(text+textPos,&cur_length);
+
+ if (cur_char == '\0') break;
+
+ if (cur_char == '\n')
+ {
+ textPos+=cur_length; // ignore the \n
+ printLine = 1;
+ break;
+ }
+ thisCharWidth = charWidth(cur_char);
+ if ((lineWidth + thisCharWidth) > (int)(area.w - (2 * paraMargin)))
+ {
+ // this character would break the right margin
+ if (cur_char == ' ')
+ {
+ // this char is a space, ignore and break
+ textPos+=cur_length;
+ break;
+ }
+ else
+ {
+ // Need to go back to the last space in the line
+ while ((cur_char != ' ') && (linePos >= 0))
+ {
+ textPos-=cur_length;
+ cur_char=getWChar(text+textPos,&cur_length);
+ linePos--;
+ }
+ // Now take the space we just found
+ textPos+=cur_length;
+ break;
+ }
+ }
+ for (int n=0;n<cur_length;n++) line[linePos++] = text[textPos+n];
+ lineWidth += thisCharWidth;
+ textPos+=cur_length;
+ }
+
+// 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 DrawStyle& 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 DrawStyle& colour)
+{
+ if (parent) parent->rectangle(area.x + x, area.y + y, w, h, colour);
+ else surface->fillblt(x, y, w, h, colour);
+}
+
+void Boxx::drawText(const char* text, int x, int y, const DrawStyle& colour)
+{
+ if (parent) parent->drawText(text, area.x + x, area.y + y, colour);
+ else surface->drawText(text, x, y, colour);
+}
+
+void Boxx::drawText(const char* text, int x, int y, int width, const DrawStyle& colour)
+{
+ if (parent) parent->drawText(text, area.x + x, area.y + y, width, colour);
+ else surface->drawText(text, x, y, width, colour);
+}
+
+void Boxx::drawTextRJ(const char* text, int x, int y, const DrawStyle& colour)
+{
+ if (parent) parent->drawTextRJ(text, area.x + x, area.y + y, colour);
+ else surface->drawTextRJ(text, x, y, colour);
+}
+
+void Boxx::drawTextCentre(const char* text, int x, int y, const DrawStyle& colour)
+{
+ if (parent) parent->drawTextCentre(text, area.x + x, area.y + y, colour);
+ else surface->drawTextCentre(text, x, y, colour);
+}
+// Now deprecated
+/*
+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, const DisplayRegion & region)
+{
+ if (parent) parent->drawBitmap(area.x + x, area.y + y, bm, region);
+ else if (surface) surface->drawBitmap(x, y, bm, region);
+}
+
+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);
+}
+
+void Boxx::drawMonoBitmap(UCHAR*base, int dx, int dy, unsigned int height,unsigned int width, Colour& nextColour)
+{
+ if (parent) parent->drawMonoBitmap(base, area.x +dx,area.y +dy, height,width, nextColour);
+ else if (surface) surface->drawMonoBitmap(base, dx,dy, height,width, nextColour);
+}
+
+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();
+ }
+}
+
+
+float Boxx::charWidth(wchar_t c)
+{
+ if (parent) return parent->charWidth(c);
+ else if (surface) return surface->getCharWidth(c);
+ else return 16.; //?
+}
+
+wchar_t Boxx::getWChar(const char* str, unsigned int *length)
+{
+ if (parent) return parent->getWChar(str,length);
+ else if (surface) return surface->getWChar(str,length);
+ else return '?'; //?
+}
+
+Surface * Boxx::getSurface() {
+ if (parent) return parent->getSurface();
+ return surface;
+}
+
-/*\r
- Copyright 2004-2005 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef BOXX_H\r
-#define BOXX_H\r
-\r
-#include <stdio.h>\r
-#include <vector>\r
-\r
-using namespace std;\r
-\r
-#include "colour.h"\r
-#include "region.h"\r
-#include "message.h"\r
-\r
-\r
-#include "surface.h"\r
-\r
-class Bitmap;\r
-\r
-class Boxx\r
-{\r
- friend class Wwss;\r
- public:\r
- Boxx();\r
- virtual ~Boxx();\r
-\r
- virtual void setSize(UINT w, UINT h); // virtual? really?\r
- void setPosition(UINT x, UINT y); // Set position on parent. Even numbers only!!!\r
- void createBuffer(); // Make this a root view that goes in the BoxStack\r
- virtual void draw();\r
- \r
- \r
- void setGap(UINT gap);\r
- void setBackgroundColour(const DrawStyle& colour);\r
- void setVisible(bool isVisible);\r
-\r
-\r
- // The following are supposed to be abstract functions\r
- // However, it is useful to be able to make instances of Boxx\r
- // Therefore the following stubs are provided.\r
- virtual void preDelete() {}\r
- virtual int handleCommand(int x) { return 0; }\r
- virtual void processMessage(Message* m) {}\r
- virtual bool mouseMove(int x, int y) { return false; }\r
- virtual bool mouseLBDOWN(int x, int y) { return false; }\r
- virtual bool mouseAndroidScroll(int x, int y,int sx, int sy) { return false; }\r
- virtual void deactivateAllControls() {}\r
-\r
- /* preDelete \r
- \r
- I think it's functionally equivalent to e.g. delete timers in Boxx::preDelete\r
- because the only place where a Boxx is deleted is in \r
- BoxStack::remove. There is now a call in BoxStack::remove to Boxx::preDelete\r
- The reason for this is to stop timercalls calling BoxStack::update at the\r
- same time BoxStack::remove is locked trying to delete the Boxx\r
- */\r
-\r
- // Get functions\r
- int getScreenX(); // where is it on screen\r
- int getScreenY();\r
- int getRootBoxOffsetX(); // where is it relative to the top-parent in the boxstack\r
- int getRootBoxOffsetY();\r
- int getX(); // where is it relative to its parent\r
- int getX2(); // .. and the right edge\r
- int getY();\r
- int getY2();\r
- UINT getWidth();\r
- UINT getHeight();\r
- bool getVisible();\r
- Region* getRegion(); // Not to be used for changing the region\r
- Region getRegionR(); // Same but as an object\r
- void getRootBoxRegion(Region*);\r
- \r
- // Drawing functions level 1\r
- void fillColour(const DrawStyle & colour);\r
- void drawPara(const char* text, int x, int y, const DrawStyle& colour);\r
-\r
- // Drawing functions level 0\r
- void rectangle(UINT x, UINT y, UINT w, UINT h, const DrawStyle& colour);\r
- void rectangle(Region& region, const DrawStyle& colour);\r
-\r
- void drawText(const char* text, int x, int y, const DrawStyle& colour);\r
- void drawText(const char* text, int x, int y, int width, const DrawStyle& colour);\r
- void drawTextRJ(const char* text, int x, int y, const DrawStyle& colour);\r
- void drawTextCentre(const char* text, int x, int y, const DrawStyle& colour);\r
- //Now deprecated\r
- //void drawPixel(UINT x, UINT y, const Colour& colour, bool fastdraw=false);\r
- void drawBitmap(UINT x, UINT y, const Bitmap& bm, const DisplayRegion & region);\r
- //Now deprecated\r
- // void drawPixelAlpha(UINT x, UINT y, const Colour& colour,bool fastdraw=false);\r
- int getFontHeight();\r
-\r
- void drawJpeg(const char *fileName,int x, int y,int *width, int *height);\r
-\r
- void drawTTChar(int ox, int oy,int x, int y, cTeletextChar c);\r
- void drawMonoBitmap(UCHAR*base, int dx, int dy, unsigned int height,unsigned int width, Colour& nextColour);\r
-\r
- /* This is for system which need a locking of the drawing surface to speed up drawing */\r
- void startFastDraw();\r
- void endFastDraw();\r
-\r
- float charWidth(wchar_t c);\r
- wchar_t getWChar(const char* str, unsigned int *length);\r
-\r
- void add(Boxx*); // a boxx has a set of child boxxs\r
- void remove(Boxx*);\r
-\r
- /*\r
- The following function sets the child's parent pointer without adding the child to the children vector.\r
- It's a hack (that should be deprecated?) to allow things like WSymbol to be created, do some drawing on\r
- the surface and then be deleted again, leaving the drawing present.\r
- A better design would be to create many WSymbols - one per symbol and leave them created - then the\r
- automatic draw code will be able to redraw the symbols without all that code needing\r
- to be in the derived boxx's draw method.\r
- */ \r
- void TEMPADD(Boxx* child) { child->setParent(this); }\r
-\r
- friend class BoxStack;\r
- protected:\r
- //get the surface this box is drawing to\r
- Surface *getSurface();\r
- Boxx* parent;\r
- Region area;\r
- vector<Boxx*> children;\r
-\r
- void setParent(Boxx*); \r
- void blt(Region& r);\r
-\r
- static const int paraMargin = 10;\r
- UINT paraVSpace;\r
-\r
- DrawStyle backgroundColour;\r
- bool backgroundColourSet;\r
- bool visible;\r
-\r
- static char numBoxxes;\r
- Surface* surface;\r
-};\r
-\r
-#endif\r
-\r
-\r
-/*\r
-\r
-New Boxx design\r
-\r
-It's "Boxx" because "Box" was already taken.\r
-\r
-BoxStack replaces Viewman and handles displaying a stack of root boxxs (boxxs with surfaces) on the screen.\r
-\r
-Boxx relaces Box, Widget and parts of View and represents a box with or without a surface.\r
-Let's call a Boxx with a surface a root box, and a boxx without a surface a child box.\r
-\r
-A boxx with a surface (root) is like an old View and is handled by BoxStack.\r
-A boxx without a surface (child) is like and old Widget and needs to be a child box of another boxx (root or not).\r
-\r
-Don't add a boxx with a surface to another boxx. That isn't the design.\r
-\r
-So, when you create a boxx, either that boxx calls createBuffer() on itself,\r
-or some other box add()s the new box to itself.\r
-\r
-Root boxxs can overlap each other - this is managed by boxstack.\r
-Child boxxs within a parent boxx do not overlap each other.\r
-However, a grandchild box can overlap a child box (but must be entirely contained within the child box).\r
-\r
-TBBoxx replaces View but is now only a convenience class for the window dressing stuff. It isn't required anymore since\r
-all the real work that view used to do is done in Boxx now.\r
-\r
-Obseleted classes: Box, View, Viewman, Widget, others?\r
-No code outside boxx should talk about surfaces anymore. Hopefully.\r
-\r
-*/\r
-\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.
+*/
+
+#ifndef BOXX_H
+#define BOXX_H
+
+#include <stdio.h>
+#include <vector>
+
+using namespace std;
+
+#include "colour.h"
+#include "region.h"
+#include "message.h"
+
+
+#include "surface.h"
+
+class Bitmap;
+
+class Boxx
+{
+ friend class Wwss;
+ 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 DrawStyle& 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 DrawStyle & colour);
+ void drawPara(const char* text, int x, int y, const DrawStyle& colour);
+
+ // Drawing functions level 0
+ void rectangle(UINT x, UINT y, UINT w, UINT h, const DrawStyle& colour);
+ void rectangle(Region& region, const DrawStyle& colour);
+
+ void drawText(const char* text, int x, int y, const DrawStyle& colour);
+ void drawText(const char* text, int x, int y, int width, const DrawStyle& colour);
+ void drawTextRJ(const char* text, int x, int y, const DrawStyle& colour);
+ void drawTextCentre(const char* text, int x, int y, const DrawStyle& colour);
+ //Now deprecated
+ //void drawPixel(UINT x, UINT y, const Colour& colour, bool fastdraw=false);
+ void drawBitmap(UINT x, UINT y, const Bitmap& bm, const DisplayRegion & region);
+ //Now deprecated
+ // 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);
+ void drawMonoBitmap(UCHAR*base, int dx, int dy, unsigned int height,unsigned int width, Colour& nextColour);
+
+ /* This is for system which need a locking of the drawing surface to speed up drawing */
+ void startFastDraw();
+ void endFastDraw();
+
+ float charWidth(wchar_t c);
+ wchar_t getWChar(const char* str, unsigned int *length);
+
+ 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<Boxx*> children;
+
+ void setParent(Boxx*);
+ void blt(Region& r);
+
+ static const int paraMargin = 10;
+ UINT paraVSpace;
+
+ DrawStyle 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.
+
+*/
+
-/*\r
- Copyright 2004-2005 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef WIN32\r
-#include <linux/errno.h>\r
-#endif\r
-\r
-#include "command.h"\r
-\r
-#ifdef WIN32\r
-#include "remotewin.h"\r
-#endif\r
-\r
-#ifdef __ANDROID__\r
-#include "remoteandroid.h"\r
-#endif\r
-\r
-#include "led.h"\r
-#include "video.h"\r
-#include "audio.h"\r
-#include "mtd.h"\r
-#include "vdr.h"\r
-#include "vvolume.h"\r
-#include "vserverselect.h"\r
-#include "vwelcome.h"\r
-#include "vmute.h"\r
-#include "colour.h"\r
-#include "osd.h"\r
-#include "i18n.h"\r
-#include "timerreceiver.h"\r
-#include "timers.h"\r
-#include "wol.h"\r
-#include "vconnect.h"\r
-#include "message.h"\r
-#include "remote.h"\r
-#include "vinfo.h"\r
-#include "boxx.h"\r
-#include "boxstack.h"\r
-#include "log.h"\r
-#include "vsleeptimer.h"\r
-\r
-\r
-Command* Command::instance = NULL;\r
-\r
-Command::Command()\r
-{\r
- if (instance) return;\r
- instance = this;\r
- initted = 0;\r
- isStandby = 0;\r
- firstBoot = 1;\r
- connLost = NULL;\r
- crashed = false;\r
- server = NULL;\r
-}\r
-\r
-Command::~Command()\r
-{\r
- instance = NULL;\r
-}\r
-\r
-Command* Command::getInstance()\r
-{\r
- return instance;\r
-}\r
-\r
-int Command::init(bool tcrashed, char* tServer)\r
-{\r
- if (initted) return 0;\r
- initted = 1;\r
- crashed = tcrashed;\r
- server = tServer;\r
-\r
- logger = Log::getInstance();\r
- boxstack = BoxStack::getInstance();\r
- remote = Remote::getInstance();\r
- \r
- remote->InitHWCListwithDefaults();\r
- \r
- if (!logger || !boxstack || !remote)\r
- {\r
- initted = 0;\r
- return 0;\r
- }\r
-#ifndef WIN32\r
- pthread_mutex_init(&masterLock, NULL);\r
-#else\r
- masterLock=CreateMutex(NULL,FALSE,NULL);\r
-#endif\r
-\r
- return 1;\r
-}\r
-\r
-int Command::shutdown()\r
-{\r
- if (!initted) return 0;\r
- initted = 0;\r
- return 1;\r
-}\r
-\r
-void Command::stop()\r
-{\r
-// VDR::getInstance()->cancelFindingServer();\r
- logger->log("Command", Log::NOTICE, "Command stop1...");\r
- \r
- udp.shutdown();\r
- logger->log("Command", Log::NOTICE, "Command stop2...");\r
- irun = 0;\r
-}\r
-\r
-void Command::doWallpaper()\r
-{\r
- Video* video = Video::getInstance();\r
-\r
- // Blue background\r
- Boxx* bbg = new Boxx();\r
- bbg->setSize(video->getScreenWidth(), video->getScreenHeight());\r
- bbg->createBuffer();\r
- bbg->fillColour(DrawStyle::VIDEOBLUE);\r
- boxstack->add(bbg);\r
- boxstack->update(bbg);\r
- boxstack->remove(bbg);\r
-\r
- // Wallpaper\r
- WJpeg* wallpaperj = new WJpegTYPE();\r
- wallpaperj->setSize(video->getScreenWidth(), video->getScreenHeight());\r
- wallpaperj->createBuffer();\r
-\r
- if (video->getFormat() == Video::PAL)\r
- {\r
- logger->log("Command", Log::DEBUG, "PAL wallpaper selected");\r
-#ifndef _MIPS_ARCH \r
- wallpaperj->init("/wallpaperPAL.jpg");\r
-#else\r
- wallpaperj->init("wallpaperPAL.jpg");\r
-#endif\r
- }\r
- else\r
- {\r
- logger->log("Command", Log::DEBUG, "NTSC wallpaper selected");\r
- wallpaperj->init("/wallpaperNTSC.jpg");\r
- }\r
- wallpaperj->draw();\r
-\r
- boxstack->add(wallpaperj);\r
- boxstack->update(wallpaperj);\r
-\r
- wallpaper = wallpaperj;\r
-}\r
-\r
-void Command::run()\r
-{\r
- if (!initted) return;\r
- irun = 1;\r
-#ifndef WIN32\r
- mainPid = getpid();\r
-#endif\r
-\r
- // just in case\r
- Video::getInstance()->signalOn();\r
- Led::getInstance()->on();\r
-\r
- doWallpaper();\r
-\r
- // End of startup. Lock the mutex and put the first view up\r
-// logger->log("Command", Log::DEBUG, "WANT LOCK");\r
-#ifndef WIN32\r
- pthread_mutex_lock(&masterLock);\r
-#else\r
- WaitForSingleObject(masterLock, INFINITE );\r
-#endif\r
- //logger->log("Command", Log::DEBUG, "LOCKED");\r
-\r
- if (crashed)\r
- {\r
- buildCrashedBox();\r
- }\r
- else\r
- {\r
- VConnect* vconnect = new VConnect(server);\r
- boxstack->add(vconnect);\r
- vconnect->run();\r
- }\r
-\r
- // Start method 2 of getting commands in...\r
- udp.run(this);\r
-\r
- UCHAR button = 0;\r
- while(irun)\r
- {\r
- // unlock and wait\r
- //logger->log("Command", Log::DEBUG, "UNLOCK");\r
-#ifndef WIN32\r
- pthread_mutex_unlock(&masterLock);\r
-#else\r
- ReleaseMutex(masterLock);\r
-#endif\r
- \r
- button = remote->getButtonPress(2); // FIXME why is this set to 2 and not 0? so it can quit \r
- // something happened, lock and process\r
- \r
- // logger->log("Command", Log::DEBUG, "WANT LOCK");\r
-#ifndef WIN32\r
- pthread_mutex_lock(&masterLock);\r
-#else\r
- WaitForSingleObject(masterLock, INFINITE );\r
-#endif\r
- // logger->log("Command", Log::DEBUG, "LOCK");\r
-\r
- if ((button == Remote::NA_NONE) /*|| (button == Remote::NA_UNKNOWN)*/) continue;\r
-\r
- if (button != Remote::NA_SIGNAL) handleCommand(button);\r
- processMessageQueue();\r
-\r
- }\r
-\r
- //logger->log("Command", Log::DEBUG, "UNLOCK");\r
-#ifndef WIN32\r
- pthread_mutex_unlock(&masterLock);\r
-#else\r
- ReleaseMutex(masterLock);\r
-#endif\r
-\r
-\r
-}\r
-\r
-void Command::postMessage(Message* m)\r
-{\r
- // This is locked here in case the main loop is not waiting for an event, but is processing one\r
- // it could be killed but then not react to it because the signal wouldn't cause\r
- // remote->getButtonPress to break\r
- // locking the mutex ensures that the master thread is waiting on getButtonPress\r
-\r
-\r
- //logger->log("Command", Log::DEBUG, "WANT LOCK");\r
-#ifndef WIN32\r
- pthread_mutex_lock(&masterLock);\r
-#else\r
- WaitForSingleObject(masterLock, INFINITE );\r
-#endif\r
- //logger->log("Command", Log::DEBUG, "LOCK");\r
- MessageQueue::postMessage(m);\r
-\r
-#ifndef WIN32\r
-#ifndef __ANDROID__\r
- kill(mainPid, SIGURG);\r
-#else\r
- ((RemoteAndroid*)Remote::getInstance())->Signal();\r
-#endif\r
- pthread_mutex_unlock(&masterLock);\r
-#else\r
- ((RemoteWin*)Remote::getInstance())->Signal();\r
- ReleaseMutex(masterLock);\r
-#endif\r
- //logger->log("Command", Log::DEBUG, "UNLOCK");\r
-}\r
-\r
-void Command::postMessageNoLock(Message* m)\r
-{\r
- // As above but use this one if this message is being posted because of a button press\r
- // the mutex is already locked, locking around postMessage is not needed as the\r
- // queue is guaranteed to be run when the button has been processed\r
- MessageQueue::postMessage(m);\r
-}\r
-\r
-bool Command::postMessageIfNotBusy(Message* m)\r
-{\r
- // Used for Windows mouse events\r
-\r
- //logger->log("Command", Log::DEBUG, "TRY LOCK");\r
-#ifndef WIN32\r
- if (pthread_mutex_trylock(&masterLock) != EBUSY)\r
- {\r
- //logger->log("Command", Log::DEBUG, "LOCK");\r
- MessageQueue::postMessage(m);\r
-#ifndef __ANDROID__\r
- kill(mainPid, SIGURG);\r
-#else\r
- ((RemoteAndroid*)Remote::getInstance())->Signal();\r
-#endif\r
- pthread_mutex_unlock(&masterLock);\r
- //logger->log("Command", Log::DEBUG, "UNLOCK");\r
- return true;\r
- }\r
- else\r
- {\r
- return false;\r
- }\r
-#else\r
- switch (WaitForSingleObject(masterLock, 0 ))\r
- { //FIXME this is not "if not busy" check\r
- case WAIT_OBJECT_0: //but with proper argument 0 this did not work\r
- // case WAIT_ABANDONED:\r
- MessageQueue::postMessage(m);\r
- ((RemoteWin*)Remote::getInstance())->Signal();\r
- ReleaseMutex(masterLock);\r
- return true;\r
-\r
- case WAIT_ABANDONED: return false;\r
- case WAIT_TIMEOUT: return false;\r
- }\r
- return false;\r
-#endif\r
-}\r
-\r
-void Command::postMessageFromOuterSpace(Message* m)\r
-{\r
- /*\r
- Yet another way of getting messages into Command. This one is for events that\r
- are not standard button presses (or UDP generated buttons). It is also not for\r
- events that are generated as a result of other events (events that can safely\r
- call postMessageNoLock and be guaranteed that the message will be processed\r
- because it is known that the queue is currently being processed).\r
- This is for events that come from outer space and can occur when the master\r
- mutex is locked or not, they need to be queued and executed but it doesn't\r
- matter when.\r
- Actually so far it is for events caused by the video stream - aspect ratio\r
- changes. These can occur when the master mutex is locked and so postMessage\r
- doesn't work. postMessageNoLock doesn't work because if the mutex *isn't*\r
- locked at the time then the message could be sat around a while before\r
- being noticed.\r
- The whole message system was at first supposed to prevent the problem of\r
- calling a function on an object that had just been deleted, by ordering\r
- messages such that all calls are done before object deletion. However,\r
- because of the new centralised messaging system and the fact that BoxStack\r
- locates the destination object before calling it, the messaging system now\r
- allows the kind of sloppy calls it was supposed to stop. Weird huh. This\r
- is mentioned here because the video stream might generate an event just as\r
- the user hits stop. The mutex is locked, and by the time the message\r
- is examined the vvideorec/live has been deleted. This doesn't matter because\r
- boxstack will drop the message if it can't find the matching object to\r
- deliver it to.\r
- Finally, all this is fine and dandy, except that I'm not 100% sure that\r
- this sloppy postMessage and hope a queued signal will force it to be processed\r
- thingy will actually work. Hmmm.\r
- Lastly <g>, I will consider making the naming system a little more sane\r
- if this works.\r
- */\r
-\r
- logger->log("Command", Log::DEBUG, "PMFOS called");\r
- MessageQueue::postMessage(m);\r
-\r
-#ifndef WIN32\r
-#ifndef __ANDROID__\r
- kill(mainPid, SIGURG);\r
-#else\r
- ((RemoteAndroid*)Remote::getInstance())->Signal();\r
-#endif\r
-#else\r
- ((RemoteWin*)Remote::getInstance())->Signal();\r
-#endif\r
-}\r
-\r
-void Command::processMessage(Message* m)\r
-{\r
- // FIXME - a slight modification - how if messagereceivers were to register\r
- // themselves as receivers to avoid the calling-a-deleted-object problem\r
- // then only deliver/register/unregister would have to be protected\r
-\r
- logger->log("Command", Log::DEBUG, "processing message %i", m->message);\r
-\r
-\r
- if (m->to == this)\r
- {\r
- switch(m->message)\r
- {\r
- // << FIXME OBSELETE\r
- case Message::STOP_PLAYBACK:\r
- {\r
- handleCommand(Remote::STOP); // an odd way of doing it, but so simple\r
- break;\r
- }\r
- // Also connection_lost comes from player - anywhere else?\r
- // FIXME OBSELETE >>\r
-\r
-\r
- case Message::VDR_CONNECTED:\r
- {\r
- doJustConnected((VConnect*)m->from);\r
- break;\r
- }\r
- case Message::SCREENSHOT:\r
- {\r
- Osd::getInstance()->screenShot("/out.jpg");\r
- break;\r
- }\r
- case Message::CONNECTION_LOST:\r
- {\r
- doFromTheTop(true);\r
- break;\r
- }\r
- case Message::UDP_BUTTON:\r
- {\r
- handleCommand(m->parameter);\r
- break;\r
- }\r
- case Message::CHANGE_LANGUAGE:\r
- {\r
- boxstack->removeAll();\r
- boxstack->update(wallpaper);\r
- I18n::initialize();\r
- if (!VDR::getInstance()->isConnected()) { connectionLost(); break; }\r
- VWelcome* vw = new VWelcome();\r
- vw->draw();\r
- boxstack->add(vw);\r
- boxstack->update(vw);\r
- break;\r
- }\r
- case Message::LAST_VIEW_CLOSE:\r
- {\r
- // Shouldn't be done like this. Some generic message pass back from vinfo perhaps\r
- if (crashed)\r
- {\r
- crashed = false;\r
- doFromTheTop(false); \r
- }\r
- \r
-// VWelcome* vw = new VWelcome();\r
-// vw->draw();\r
-// boxstack->add(vw);\r
-// boxstack->update(vw);\r
-\r
- break;\r
- }\r
- }\r
- }\r
- else\r
- {\r
- /* FIXME\r
- \r
- Instead of sending through the boxstack, implement a more generic MessageReceiver interface\r
- and have potential receivers register with something\r
- When a message needs to be delivered, check if the receiver is still registered, if so, deliver the message\r
- This could all be done using the existing big command mutex to keep it simple\r
- */\r
- \r
- logger->log("Command", Log::DEBUG, "Sending message to boxstack");\r
- boxstack->processMessage(m);\r
- }\r
-}\r
-\r
-void Command::handleCommand(int button)\r
-{\r
- if (isStandby && (button != Remote::POWER)) return;\r
- if (!connLost && boxstack->handleCommand(button)) return; // don't send to boxstack if connLost\r
-\r
- // command was not handled\r
-\r
- switch(button)\r
- {\r
- case Remote::DF_LEFT:\r
- case Remote::DF_RIGHT:\r
- case Remote::VOLUMEUP:\r
- case Remote::VOLUMEDOWN:\r
- {\r
- if (remote->handlesVolume()) {\r
- if (button==Remote::DF_LEFT || button==Remote::VOLUMEDOWN)\r
- remote->volumeDown();\r
- else remote->volumeUp();\r
- } else {\r
- VVolume* v = new VVolume();\r
- boxstack->add(v);\r
- v->handleCommand(button); // this will draw+show\r
- }\r
- return;\r
- }\r
- case Remote::MUTE:\r
- {\r
- if (remote->handlesVolume()) {\r
- remote->volumeMute();\r
- } else {\r
- VMute* v = new VMute();\r
- v->draw();\r
- boxstack->add(v);\r
- boxstack->update(v);\r
- }\r
- return;\r
- }\r
- case Remote::POWER:\r
- {\r
- doStandby();\r
- return;\r
- }\r
- case Remote::OK:\r
- {\r
- // FIXME\r
- if (!connLost) return; // if connLost, handle Remote::OK\r
- doFromTheTop(false);\r
- return;\r
- }\r
- case Remote::GO:\r
- {\r
- VSleeptimer* sleep = new VSleeptimer();\r
- boxstack->add(sleep);\r
- sleep->handleCommand(button); // this will draw+show\r
- return;\r
- }\r
- }\r
-}\r
-\r
-void Command::sig1()\r
-{\r
-#ifdef DEV\r
- Message* m = new Message(); // break into master mutex\r
- m->message = Message::SCREENSHOT;\r
- m->to = this;\r
- postMessage(m);\r
-#endif\r
-}\r
-\r
-void Command::doStandby()\r
-{\r
- if (isStandby)\r
- {\r
- Video::getInstance()->signalOn();\r
- Led::getInstance()->on();\r
- isStandby = 0;\r
-\r
-\r
- VConnect* vconnect = new VConnect(server);\r
- boxstack->add(vconnect);\r
- vconnect->run();\r
- }\r
- else\r
- {\r
- boxstack->removeAll();\r
- Video::getInstance()->signalOff();\r
- boxstack->update(wallpaper);\r
-\r
- VDR::getInstance()->configSave("General", "Last Power State", "Off");\r
- logger->unsetExternLogger();\r
- VDR::getInstance()->disconnect();\r
- Led::getInstance()->off();\r
- isStandby = 1;\r
- Sleeptimer::getInstance()->shutdown();\r
-#ifdef WIN32\r
- stop(); //different behavoiur on windows, we exit\r
-#endif\r
- }\r
-}\r
-\r
-void Command::doFromTheTop(bool which)\r
-{\r
- if (which)\r
- {\r
- if (connLost)\r
- {\r
- logger->log("Command", Log::NOTICE, "Connection lost dialog already present");\r
- return;\r
- }\r
- \r
- logger->log("Command", Log::NOTICE, "Doing connection lost dialog");\r
- connLost = new VInfo();\r
- connLost->setSize(360, 200);\r
- connLost->createBuffer();\r
- if (Video::getInstance()->getFormat() == Video::PAL)\r
- connLost->setPosition(190, 170);\r
- else\r
- connLost->setPosition(180, 120);\r
- connLost->setOneLiner(tr("Connection lost"));\r
- connLost->setDropThrough();\r
- connLost->setBorderOn(1);\r
- connLost->setTitleBarColour(DrawStyle::DANGER);\r
- connLost->okButton();\r
- connLost->draw();\r
- boxstack->add(connLost);\r
- boxstack->update(connLost);\r
- remote->clearBuffer();\r
- }\r
- else\r
- {\r
- logger->unsetExternLogger();\r
- VDR::getInstance()->disconnect();\r
- boxstack->removeAll();\r
- boxstack->update(wallpaper);\r
- connLost = NULL;\r
- \r
- flushMessageQueue();\r
- remote->clearBuffer();\r
- \r
- // at this point, everything should be reset to first-go\r
- \r
- VConnect* vconnect = new VConnect(server);\r
- boxstack->add(vconnect);\r
- vconnect->run();\r
- }\r
-}\r
-\r
-void Command::doReboot()\r
-{\r
-\r
- logger->unsetExternLogger();\r
- VDR::getInstance()->disconnect();\r
- // just kill it...\r
- logger->log("Command", Log::NOTICE, "Reboot");\r
-#ifndef WIN32\r
-#ifndef VOMP_HAS_EXIT\r
- // some plattforms, want a proper deinitialisation of their hardware before reboot\r
- Osd::getInstance()->shutdown();\r
- Audio::getInstance()->shutdown();\r
- Video::getInstance()->shutdown();\r
- Remote::getInstance()->shutdown();\r
-\r
- reboot(LINUX_REBOOT_CMD_RESTART);\r
- // if reboot is not allowed -> stop\r
- stop();\r
-\r
-\r
-#else\r
- stop();\r
-\r
-#ifdef __ANDROID__\r
- exit(0);\r
-#endif\r
-\r
-#endif\r
-#endif //Would we support this on windows?\r
-}\r
-\r
-void Command::connectionLost()\r
-{\r
- logger->unsetExternLogger();\r
- Message* m = new Message(); // break into master mutex\r
- m->message = Message::CONNECTION_LOST;\r
- m->to = this;\r
- postMessageFromOuterSpace(m);\r
-}\r
-\r
-void Command::buildCrashedBox()\r
-{\r
- VInfo* crash = new VInfo();\r
- crash->setSize(360, 250);\r
- crash->createBuffer();\r
- if (Video::getInstance()->getFormat() == Video::PAL)\r
- crash->setPosition(190, 146);\r
- else\r
- crash->setPosition(180, 96);\r
- 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.");\r
- crash->setBorderOn(1);\r
- crash->setTitleBarColour(DrawStyle::DANGER);\r
- crash->okButton();\r
- crash->setExitable();\r
- crash->draw();\r
- boxstack->add(crash);\r
- boxstack->update(crash);\r
-}\r
-\r
-void Command::doJustConnected(VConnect* vconnect)\r
-{\r
- I18n::initialize();\r
- if (!VDR::getInstance()->isConnected()) { connectionLost(); return; }\r
- logger->log("Command", Log::INFO, "Entering doJustConnected");\r
- \r
- Video* video = Video::getInstance();\r
- Audio* audio = Audio::getInstance(); \r
- boxstack->remove(vconnect);\r
-\r
- VInfo* vi = new VInfo();\r
- vi->setSize(400, 200);\r
- vi->createBuffer();\r
- if (video->getFormat() == Video::PAL)\r
- vi->setPosition(170, 200);\r
- else\r
- vi->setPosition(160, 150);\r
- vi->setOneLiner(tr("Connected, loading config"));\r
- vi->draw();\r
- boxstack->add(vi);\r
- boxstack->update(vi);\r
-\r
- VDR* vdr = VDR::getInstance();\r
- char* config;\r
-\r
- // See if we're supposed to do network logging\r
- config = vdr->configLoad("Advanced", "Network logging");\r
- if (config && !STRCASECMP(config, "On"))\r
- {\r
- logger->log("Command", Log::INFO, "Turning on network logging");\r
- logger->setExternLogger(vdr);\r
- } \r
- else\r
- {\r
- logger->unsetExternLogger();\r
- logger->log("Command", Log::INFO, "Turned off network logging");\r
- }\r
- if (config) delete[] config;\r
-\r
- // See if config says to override video format (PAL/NTSC)\r
- config = vdr->configLoad("General", "Override Video Format");\r
- if (config)\r
- {\r
- logger->log("Command", Log::DEBUG, "Override Video Format is present");\r
-\r
- if ( (!strcmp(config, "PAL") && (video->getFormat() != Video::PAL))\r
- || (!strcmp(config, "NTSC") && (video->getFormat() != Video::NTSC))\r
- || (!strcmp(config, "PAL_M") && (video->getFormat() != Video::PAL_M))\r
- || (!strcmp(config, "NTSC_J") && (video->getFormat() != Video::NTSC_J))\r
- )\r
- {\r
- // Oh sheesh, need to switch format. Bye bye TV...\r
-\r
- // Take everything down\r
- boxstack->removeAll();\r
- boxstack->remove(wallpaper);\r
- Osd* osd = Osd::getInstance();\r
-#ifndef __ANDROID__\r
- osd->shutdown();\r
-#endif\r
- video->shutdown();\r
-\r
- remote->shutdown(); // need on raspberry shut not do any harm, hopefully\r
- remote->init(RemoteStartDev);\r
-\r
- // Get video and osd back up with the new mode\r
- if (!strcmp(config, "PAL"))\r
- {\r
- logger->log("Command", Log::DEBUG, "Switching to PAL");\r
- video->init(Video::PAL);\r
- }\r
- else if (!strcmp(config, "NTSC"))\r
- {\r
- logger->log("Command", Log::DEBUG, "Switching to NTSC");\r
- video->init(Video::NTSC);\r
- } else if (!strcmp(config, "PAL_M"))\r
- {\r
- logger->log("Command", Log::DEBUG, "Switching to PAL_M");\r
- video->init(Video::PAL_M);\r
- } else if (!strcmp(config, "NTSC_J"))\r
- {\r
- logger->log("Command", Log::DEBUG, "Switching to NTSC_J");\r
- video->init(Video::NTSC_J);\r
- }\r
-#ifndef __ANDROID__\r
- //we do not init twice\r
- osd->init((char*)("/dev/stbgfx"));\r
-#endif\r
-\r
- // Put the wallpaper back\r
- doWallpaper();\r
-\r
- // Re add the vinfo\r
- vi = new VInfo();\r
- vi->setSize(400, 200);\r
- vi->createBuffer();\r
- if (video->getFormat() == Video::PAL)\r
- vi->setPosition(170, 200);\r
- else\r
- vi->setPosition(160, 150);\r
-\r
- vi->setOneLiner(tr("Connected, loading config"));\r
- vi->draw();\r
- boxstack->add(vi);\r
- boxstack->update(vi);\r
- }\r
- else\r
- {\r
- logger->log("Command", Log::DEBUG, "Already in requested mode, or request was not 'PAL' or 'NTSC'");\r
- }\r
- }\r
- else\r
- {\r
- logger->log("Command", Log::DEBUG, "Phew, no dangerous on-the-fly mode switching to do!");\r
- }\r
-\r
- // Power off if first boot and config says so\r
- if (firstBoot)\r
- {\r
- firstBoot = 0;\r
-\r
- logger->log("Command", Log::DEBUG, "Load power after boot");\r
-\r
- config = vdr->configLoad("General", "Power After Boot");\r
-\r
- if (config)\r
- {\r
- if (!STRCASECMP(config, "On"))\r
- {\r
- logger->log("Command", Log::INFO, "Config says Power After Boot = On");\r
- }\r
- else if (!STRCASECMP(config, "Off"))\r
- {\r
- logger->log("Command", Log::INFO, "Config says Power After Boot = Off");\r
- doStandby();\r
- delete[] config;\r
- return; // quit here\r
- }\r
- else if (!STRCASECMP(config, "Last state"))\r
- {\r
- char* lastPowerState = vdr->configLoad("General", "Last Power State");\r
- if (lastPowerState)\r
- {\r
- if (!STRCASECMP(lastPowerState, "On"))\r
- {\r
- logger->log("Command", Log::INFO, "Config says Last Power State = On");\r
- }\r
- else if (!STRCASECMP(lastPowerState, "Off"))\r
- {\r
- logger->log("Command", Log::INFO, "Config says Last Power State = Off");\r
- doStandby();\r
- delete[] config;\r
- return; // quit here\r
- }\r
- else\r
- {\r
- logger->log("Command", Log::INFO, "Config General/Last Power State not understood");\r
- }\r
- }\r
- else\r
- {\r
- logger->log("Command", Log::INFO, "Config General/Last Power State not found");\r
- }\r
- }\r
- else\r
- {\r
- logger->log("Command", Log::INFO, "Config/Power After Boot not understood");\r
- }\r
- delete[] config;\r
- }\r
- else\r
- {\r
- logger->log("Command", Log::INFO, "Config General/Power After Boot not found");\r
- }\r
- }\r
-\r
-\r
- // Go S-Video if config says so\r
-\r
- config = vdr->configLoad("TV", "Connection");\r
-\r
- if (config)\r
- {\r
- if (!STRCASECMP(config, "S-Video"))\r
- {\r
- logger->log("Command", Log::INFO, "Switching to S-Video as Connection=%s", config);\r
- video->setConnection(Video::SVIDEO);\r
- } else if (!STRCASECMP(config, "HDMI"))\r
- {\r
- logger->log("Command", Log::INFO, "Switching to HDMI as Connection=%s", config);\r
- video->setConnection(Video::HDMI);\r
- } else if (!STRCASECMP(config, "HDMI3D"))\r
- {\r
- logger->log("Command", Log::INFO, "Switching to HDMI3D as Connection=%s", config);\r
- video->setConnection(Video::HDMI3D);\r
- }\r
- else\r
- {\r
- logger->log("Command", Log::INFO, "Switching to RGB/Composite as Connection=%s", config);\r
- video->setConnection(Video::COMPOSITERGB);\r
- }\r
- delete[] config;\r
- }\r
- else\r
- {\r
- logger->log("Command", Log::INFO, "Config TV/S-Video not found");\r
- }\r
-\r
- // Set remote type\r
-\r
- config = vdr->configLoad("General", "Remote type");\r
-\r
- if (config)\r
- {\r
- if (!STRCASECMP(config, "New"))\r
- {\r
- logger->log("Command", Log::INFO, "Switching to New remote type");\r
- remote->setRemoteType(Remote::NEWREMOTE);\r
- }\r
- else\r
- {\r
- logger->log("Command", Log::INFO, "Switching to Old remote type");\r
- remote->setRemoteType(Remote::OLDREMOTE);\r
- }\r
- delete[] config;\r
- }\r
- else\r
- {\r
- logger->log("Command", Log::INFO, "Config General/Remote type not found");\r
- remote->setRemoteType(Remote::OLDREMOTE);\r
- }\r
-\r
-\r
-\r
-\r
- // Get TV aspect ratio\r
-\r
- config = vdr->configLoad("TV", "Aspect");\r
- if (config)\r
- {\r
- if (!STRCASECMP(config, "16:9"))\r
- {\r
- logger->log("Command", Log::INFO, "/// Switching to TV aspect 16:9");\r
- video->setTVsize(Video::ASPECT16X9);\r
- }\r
- else\r
- {\r
- logger->log("Command", Log::INFO, "/// Switching to TV aspect 4:3");\r
- video->setTVsize(Video::ASPECT4X3);\r
- }\r
- delete[] config;\r
- }\r
- else\r
- {\r
- logger->log("Command", Log::INFO, "Config TV/Aspect type not found, going 4:3");\r
- video->setTVsize(Video::ASPECT4X3);\r
- }\r
-\r
- config = vdr->configLoad("TV", "Widemode");\r
- if (config)\r
- {\r
- if (!STRCASECMP(config, "Letterbox"))\r
- {\r
- logger->log("Command", Log::INFO, "Setting letterbox mode");\r
- video->setMode(Video::LETTERBOX);\r
- }\r
- else\r
- {\r
- logger->log("Command", Log::INFO, "Setting chop-sides mode");\r
- video->setMode(Video::NORMAL);\r
- }\r
- delete[] config;\r
- }\r
- else\r
- {\r
-#ifdef __ANDROID__\r
- logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting letterbox mode");\r
- video->setMode(Video::LETTERBOX);\r
-#else\r
- logger->log("Command", Log::INFO, "Config TV/Widemode not found, Setting chop-sides mode");\r
- video->setMode(Video::NORMAL);\r
-#endif\r
- }\r
-\r
- config = vdr->configLoad("Advanced", "TCP receive window");\r
- if (config)\r
- {\r
- size_t newTCPsize = atoi(config);\r
- delete[] config;\r
-\r
- logger->log("Command", Log::INFO, "Setting TCP window size %i", newTCPsize);\r
- vdr->setReceiveWindow(newTCPsize);\r
- }\r
- else\r
- {\r
- logger->log("Command", Log::INFO, "TCP window size not found, setting 2048");\r
- vdr->setReceiveWindow(2048); // Default\r
- }\r
-\r
- config = vdr->configLoad("Advanced", "Disable WOL");\r
- if (config)\r
- {\r
- if (!STRCASECMP(config, "Yes"))\r
- {\r
- logger->log("Command", Log::INFO, "Config says disable WOL");\r
- Wol::getInstance()->setEnabled(false);\r
- }\r
- else\r
- {\r
- logger->log("Command", Log::INFO, "Config says enable WOL");\r
- Wol::getInstance()->setEnabled(true);\r
- }\r
-\r
- delete[] config;\r
- }\r
- else\r
- {\r
- logger->log("Command", Log::INFO, "By default, enable WOL");\r
- Wol::getInstance()->setEnabled(true);\r
- }\r
- /* device dependend config */\r
- audio->loadOptionsfromServer(vdr);\r
- video->loadOptionsfromServer(vdr);\r
- remote->loadOptionsfromServer(vdr);\r
-\r
- video->executePendingModeChanges();\r
- // config done\r
-\r
- // Save power state = on\r
-\r
- vdr->configSave("General", "Last Power State", "On");\r
-\r
- // Make sure connection didn't die\r
- if (!vdr->isConnected())\r
- {\r
- Command::getInstance()->connectionLost();\r
- }\r
- else\r
- {\r
- boxstack->remove(vi);\r
-\r
- VWelcome* vw = new VWelcome();\r
- vw->draw();\r
- boxstack->add(vw);\r
- boxstack->update(vw);\r
-\r
- // Enter pre-keys here\r
-// handleCommand(Remote::OK);\r
-// handleCommand(Remote::THREE);\r
-// handleCommand(Remote::SIX);\r
-// handleCommand(Remote::OK);\r
-// handleCommand(Remote::UP);\r
-// handleCommand(Remote::PLAY);\r
-// handleCommand(Remote::DOWN);\r
-// handleCommand(Remote::DOWN);\r
-// handleCommand(Remote::DOWN);\r
- // handleCommand(Remote::OK);\r
-// handleCommand(Remote::RED);\r
- }\r
-}\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.
+*/
+
+#ifndef WIN32
+#include <linux/errno.h>
+#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 "mtd.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(DrawStyle::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 <g>, 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:
+ {
+ if (remote->handlesVolume()) {
+ if (button==Remote::DF_LEFT || button==Remote::VOLUMEDOWN)
+ remote->volumeDown();
+ else remote->volumeUp();
+ } else {
+ VVolume* v = new VVolume();
+ boxstack->add(v);
+ v->handleCommand(button); // this will draw+show
+ }
+ return;
+ }
+ case Remote::MUTE:
+ {
+ if (remote->handlesVolume()) {
+ remote->volumeMute();
+ } else {
+ 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(DrawStyle::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
+#ifndef VOMP_HAS_EXIT
+ // some plattforms, want a proper deinitialisation of their hardware before reboot
+ Osd::getInstance()->shutdown();
+ Audio::getInstance()->shutdown();
+ Video::getInstance()->shutdown();
+ Remote::getInstance()->shutdown();
+
+ reboot(LINUX_REBOOT_CMD_RESTART);
+ // if reboot is not allowed -> stop
+ stop();
+
+
+#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(DrawStyle::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; }
+ logger->log("Command", Log::INFO, "Entering doJustConnected");
+
+ 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::PAL))
+ || (!strcmp(config, "NTSC") && (video->getFormat() != Video::NTSC))
+ || (!strcmp(config, "PAL_M") && (video->getFormat() != Video::PAL_M))
+ || (!strcmp(config, "NTSC_J") && (video->getFormat() != Video::NTSC_J))
+ )
+ {
+ // 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();
+
+ remote->shutdown(); // need on raspberry shut not do any harm, hopefully
+ remote->init(RemoteStartDev);
+
+ // 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);
+ } else if (!strcmp(config, "PAL_M"))
+ {
+ logger->log("Command", Log::DEBUG, "Switching to PAL_M");
+ video->init(Video::PAL_M);
+ } else if (!strcmp(config, "NTSC_J"))
+ {
+ logger->log("Command", Log::DEBUG, "Switching to NTSC_J");
+ video->init(Video::NTSC_J);
+ }
+#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 if (!STRCASECMP(config, "HDMI"))
+ {
+ logger->log("Command", Log::INFO, "Switching to HDMI as Connection=%s", config);
+ video->setConnection(Video::HDMI);
+ } else if (!STRCASECMP(config, "HDMI3D"))
+ {
+ logger->log("Command", Log::INFO, "Switching to HDMI3D as Connection=%s", config);
+ video->setConnection(Video::HDMI3D);
+ }
+ 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);
+
+ video->executePendingModeChanges();
+ // 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);
+ }
+}
-/*\r
- Copyright 2004-2005 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef DEFINES_H\r
-#define DEFINES_H\r
-\r
-typedef unsigned char UCHAR;\r
-typedef unsigned short USHORT;\r
-typedef unsigned int UINT;\r
-typedef unsigned long ULONG;\r
-typedef unsigned long long ULLONG;\r
-\r
-#define OPTIONTYPE_TEXT 1\r
-#define OPTIONTYPE_INT 2\r
-\r
-#define BENCHMARK_FPS\r
-\r
-//ULLONG htonll(ULLONG a);\r
-//ULLONG ntohll(ULLONG a);\r
-void MILLISLEEP(ULONG a);\r
-long long getTimeMS();\r
-\r
-#ifdef WIN32\r
-\r
- #define Surface_TYPE SurfaceWin\r
- #define Thread_TYPE ThreadWin\r
- #define ThreadID_TYPE unsigned int\r
-\r
- #define SNPRINTF _snprintf\r
- #define VSNPRINTF _vsnprintf\r
- #define STRCASECMP _stricmp\r
- #define STRCASESTR StrStrI\r
-/* #define STRTOULL _strtoui64 */\r
- #define STRTOUL strtoul\r
- #define CLOSESOCKET closesocket\r
- #define DEFAULT_TCP_WINDOWSIZENR 1 /*=2048*/\r
- #define PLAYER_MAX_STREAMING_BUFFERS 120 // for video in uints of 50000 KB\r
-\r
- #define VOMP_HAS_EXIT\r
-\r
-#else\r
-\r
- int max(int, int);\r
- int min(UINT, int);\r
-/*#ifdef _MIPS_ARCH\r
- #define Surface_TYPE SurfaceDirectFB\r
-#else\r
- #define Surface_TYPE SurfaceMVP\r
-#endif*/\r
-#ifdef __ANDROID__\r
- #define Thread_TYPE ThreadPAndroid\r
-\r
-#else\r
- #define Thread_TYPE ThreadP\r
-\r
-#endif\r
- #include <pthread.h>\r
- #define ThreadID_TYPE pthread_t\r
-\r
- #define SNPRINTF snprintf\r
- #define VSNPRINTF vsnprintf\r
- #define STRCASECMP strcasecmp\r
- #define STRCASESTR strcasestr\r
- #define STRTOUL strtoul\r
- #define CLOSESOCKET close\r
-\r
-// add here defines for plattform specific objects\r
-#ifdef VOMP_PLATTFORM_RASPBERRY\r
- #define Remote_TYPE RemoteLinux // Generic Remote under Linux (Konsole!, not X) will support in the end:\r
- #define RemoteStartDev ""//No devices passed\r
-\r
- // Keyboard\r
- // remotes under /dev/event\r
- // HDMI CEC\r
- //lirc?\r
- #define Mtd_TYPE MtdRaspberry //this is device dependent\r
- #define Led_TYPE LedRaspberry //this is device dependent\r
- #define Osd_TYPE OsdOpenVG // This OpenGL ES 2.0, in the moment only for raspberry, but might be splitted for other devices\r
- #define OsdStartDev ""\r
- #define Audio_TYPE AudioOMX // This is Audio based on OpenMax and libav for decoding\r
- #define Video_TYPE VideoOMX // This is Video based on OpenMax\r
-\r
-\r
-\r
- #define VPE_OMX_SUPPORT // Activate support for hardware codec using openmax il\r
- #define VPE_OMX_H264_DECODER "OMX.broadcom.video_decode"\r
- #define VPE_OMX_MPEG2_DECODER "OMX.broadcom.video_decode"\r
- #define VPE_OMX_VIDEO_SCHED "OMX.broadcom.video_scheduler"\r
- #define VPE_OMX_VIDEO_REND "OMX.broadcom.video_render"\r
- #define VPE_OMX_VIDEO_DEINTERLACE "OMX.broadcom.image_fx"\r
- #define VPE_OMX_CLOCK "OMX.broadcom.clock"\r
- #define VPE_OMX_AUDIO_DECODER "OMX.broadcom.audio_decode"\r
- #define VPE_OMX_AUDIO_REND "OMX.broadcom.audio_render"\r
-\r
- //#define VPE_LIBAV_SUPPORT\r
- // #define VPE_LIBAV_MPEG2_TRANSCODING\r
-\r
- #define DEFAULT_TCP_WINDOWSIZENR 6 /*=2048*/\r
- #define PLAYER_MAX_STREAMING_BUFFERS 120 // for video in uints of 50000 KB\r
- #define TV_NORM_SWITCHABLE\r
- #define HANDLE_VT_SWITCHING\r
-\r
- #define VOMP_LINUX_CLOCK CLOCK_MONOTONIC\r
-\r
-#endif\r
-#ifdef VOMP_PLATTFORM_MVP\r
- #define Remote_TYPE RemoteMVP\r
- #define RemoteStartDev "/dev/rawir"\r
- #define Mtd_TYPE MtdMVP\r
- #define Led_TYPE LedMVP\r
- #define Osd_TYPE OsdMVP\r
- #define OsdStartDev "/dev/stbgfx"\r
- #define Audio_TYPE AudioMVP\r
- #define Video_TYPE VideoMVP\r
- #define Surface_TYPE SurfaceMVP //deprecated\r
- #define DEFAULT_TCP_WINDOWSIZENR 1 /*=2048*/\r
- #define PLAYER_MAX_STREAMING_BUFFERS 11 // for video in uints of 50000 KB\r
- #define PAL_WSS\r
- #define MVP_REMOTE_TYPES\r
-\r
- #define VOMP_MEDIAPLAYER\r
- #define VOMP_LINUX_CLOCK CLOCK_REALTIME\r
-#endif\r
-\r
-#ifdef VOMP_PLATTFORM_NMT // This was the attempt to port vomp to nmt, it failed but maybe the code is useful at some time\r
- #define Remote_TYPE RemoteLirc\r
- #define RemoteStartDev "/dev/lircd"\r
- #define Mtd_TYPE MtdNMT\r
- #define Led_TYPE LedMVP\r
- #define Osd_TYPE OsdDirectFB\r
- #define OsdStartDev ""\r
- #define Audio_TYPE AudioNMT\r
- #define Video_TYPE VideoNMT\r
- #define Surface_TYPE SurfaceDirectFB //deprecated\r
- #define DEFAULT_TCP_WINDOWSIZENR 1 /*=2048*/\r
- #define PLAYER_MAX_STREAMING_BUFFERS 11 // for video in uints of 50000 KB\r
-\r
- #define VOMP_LINUX_CLOCK CLOCK_REALTIME\r
-\r
-#endif\r
-\r
-#endif\r
-\r
-/*\r
-typedef struct\r
-{\r
- UINT id; // Used for working out what has changed at the end\r
- char *title; // Name of the option\r
- char *configSection; // Which section of the config file\r
- char *configParam; // Parameter name in the config file\r
- UINT optionType; // 1 for text, 2 for int\r
- UINT optionCount; // How many choices?\r
- UINT defaultOption; // Serial of the default choice (base 0), or actual option in int mode\r
- int startInt; // Starting int for int mode\r
- const char * const * options; // Text for the options (null for int mode)\r
-} OPTIONDATA;\r
-*/\r
-\r
-#endif\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.
+*/
+
+#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
+
+#define BENCHMARK_FPS
+
+//ULLONG htonll(ULLONG a);
+//ULLONG ntohll(ULLONG a);
+void MILLISLEEP(ULONG a);
+long long getTimeMS();
+
+#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
+ #define DEFAULT_TCP_WINDOWSIZENR 1 /*=2048*/
+ #define PLAYER_MAX_STREAMING_BUFFERS 120 // for video in uints of 50000 KB
+
+ #define VOMP_HAS_EXIT
+
+#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 <pthread.h>
+ #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 OsdOpenVG // This OpenGL ES 2.0, in the moment only for raspberry, but might be splitted for other devices
+ #define OsdStartDev ""
+ #define Audio_TYPE AudioOMX // This is Audio based on OpenMax and libav for decoding
+ #define Video_TYPE VideoOMX // This is Video based on OpenMax
+
+
+
+ #define VPE_OMX_SUPPORT // Activate support for hardware codec using openmax il
+ #define VPE_OMX_H264_DECODER "OMX.broadcom.video_decode"
+ #define VPE_OMX_MPEG2_DECODER "OMX.broadcom.video_decode"
+ #define VPE_OMX_VIDEO_SCHED "OMX.broadcom.video_scheduler"
+ #define VPE_OMX_VIDEO_REND "OMX.broadcom.video_render"
+ #define VPE_OMX_VIDEO_DEINTERLACE "OMX.broadcom.image_fx"
+ #define VPE_OMX_CLOCK "OMX.broadcom.clock"
+ #define VPE_OMX_AUDIO_DECODER "OMX.broadcom.audio_decode"
+ #define VPE_OMX_AUDIO_REND "OMX.broadcom.audio_render"
+
+ //#define VPE_LIBAV_SUPPORT
+ // #define VPE_LIBAV_MPEG2_TRANSCODING
+
+ #define DEFAULT_TCP_WINDOWSIZENR 6 /*=2048*/
+ #define PLAYER_MAX_STREAMING_BUFFERS 120 // for video in uints of 50000 KB
+ #define TV_NORM_SWITCHABLE
+ #define HANDLE_VT_SWITCHING
+
+ #define VOMP_LINUX_CLOCK CLOCK_MONOTONIC
+
+#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
+ #define Surface_TYPE SurfaceMVP //deprecated
+ #define DEFAULT_TCP_WINDOWSIZENR 1 /*=2048*/
+ #define PLAYER_MAX_STREAMING_BUFFERS 11 // for video in uints of 50000 KB
+ #define PAL_WSS
+ #define MVP_REMOTE_TYPES
+
+ #define VOMP_MEDIAPLAYER
+ #define VOMP_LINUX_CLOCK CLOCK_REALTIME
+#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
+ #define Surface_TYPE SurfaceDirectFB //deprecated
+ #define DEFAULT_TCP_WINDOWSIZENR 1 /*=2048*/
+ #define PLAYER_MAX_STREAMING_BUFFERS 11 // for video in uints of 50000 KB
+
+ #define VOMP_LINUX_CLOCK CLOCK_REALTIME
+
+#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
-/*\r
- Copyright 2005-2008 Mark Calderbank\r
- Copyright 2007 Marten Richter (AC3 support)\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software Foundation, Inc.,\r
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#include "demuxer.h"\r
-\r
-#include "callback.h"\r
-#include "dvbsubtitles.h"\r
-#include "log.h"\r
-\r
-#include <cstdlib>\r
-\r
-#include <math.h>\r
-\r
-#define DEMUXER_SEQ_HEAD 0x000001B3\r
-#define DEMUXER_PIC_HEAD 0x00000101\r
-#define DEMUXER_SEQ_EXT_HEAD 0x000001B5\r
-\r
-#define DEMUXER_H264_ACCESS_UNIT 0x00000109\r
-#define DEMUXER_H264_SEQ_PARAMETER_SET 0x00000107\r
-#define DEMUXER_H264_SUB_ENHANCEMENT_INF 0x00000106\r
-\r
-\r
-#define SEEK_THRESHOLD 150000 // About 1.5 seconds\r
-\r
-// Statics\r
-const int Demuxer::FrameRates[9] = { 0, 23, 24, 25, 29, 30, 50, 59, 60 };\r
-Demuxer* Demuxer::instance = NULL;\r
-\r
-class NALUUnit {\r
-public:\r
- NALUUnit(const UCHAR* buf,UINT length_buf);\r
- ~NALUUnit();\r
-\r
-inline UINT getBits(UINT num_bits);\r
- UINT getUe();\r
- int getSe();\r
- bool isEonalu() {return eonalu;};\r
-\r
-protected:\r
- UCHAR* nalu_buf;\r
- UINT nalu_length;\r
- UINT pos;\r
- UCHAR bit_pos;\r
- UCHAR working_byte;\r
- UINT last_bytes;\r
- bool eonalu;\r
-};\r
-\r
-NALUUnit::NALUUnit(const UCHAR *buf, UINT length_buf)\r
-{\r
- nalu_length=0;\r
- nalu_buf=NULL;\r
- pos=0;\r
- bit_pos=0;\r
- working_byte=0;\r
- last_bytes=0;\r
- eonalu=false;\r
-\r
- UINT nalu_start=0;\r
- UINT nalu_end=0;\r
- UINT pattern =(((UINT)buf[ 0] << 16) |\r
- ((UINT)buf[1] << 8) |\r
- (UINT)buf[2] );\r
- nalu_start=3;\r
- while (pattern != 0x000001)\r
- {\r
- if (++nalu_start >= length_buf) return;\r
- pattern = ((pattern << 8) | buf[nalu_start])&0x00FFFFFF;\r
- }\r
- nalu_end=nalu_start+1;\r
- pattern = ((pattern << 8) | buf[nalu_end])&0x00FFFFFF;\r
-\r
- while (pattern != 0x000001 && pattern != 0x000000)\r
- {\r
- if (++nalu_end >= length_buf) { nalu_end+=3;break;};\r
- pattern = ((pattern << 8) | buf[nalu_end])&0x00FFFFFF;\r
- }\r
- nalu_end-=3;\r
- nalu_end=min(length_buf-1,nalu_end);\r
- nalu_length=nalu_end-nalu_start;\r
- nalu_buf=(UCHAR*)malloc(nalu_length);\r
- memcpy(nalu_buf,buf+nalu_start,nalu_length);\r
- pos=1;\r
-}\r
-\r
-NALUUnit::~NALUUnit()\r
-{\r
- if (nalu_buf) free(nalu_buf);\r
-}\r
-\r
-inline UINT NALUUnit::getBits(UINT num_bits)\r
-{\r
- if (num_bits==0) return 0; //???\r
- UINT remain_bits=num_bits;\r
- UINT work=0;\r
- //May be slow, but should work!\r
- while (remain_bits>0) {\r
- if (bit_pos==0) {\r
- if (pos<nalu_length)\r
- {\r
- last_bytes=(last_bytes<<8) & nalu_buf[pos];\r
- if ((last_bytes & 0x00FFFFFF) == 0x000003) pos++; //emulation prevention byte\r
- if (pos<nalu_length)\r
- {\r
- working_byte=nalu_buf[pos];\r
- pos++;\r
- } \r
- else\r
- {\r
- working_byte=0;\r
- eonalu=true;\r
- }\r
- } \r
- else\r
- {\r
- working_byte=0;\r
- eonalu=true;\r
- }\r
-\r
- }\r
- UINT fetch_bits=min(remain_bits,8-bit_pos);\r
- work=work <<fetch_bits;\r
- //work|=((working_byte>>bit_pos) & (0xFF>>(8-fetch_bits)));\r
- work|=(working_byte &(0xFF>>(bit_pos)))>>(8-fetch_bits-bit_pos);\r
- remain_bits-=fetch_bits;\r
- bit_pos=(bit_pos+fetch_bits)%8;\r
- }\r
- return work;\r
-}\r
-\r
-UINT NALUUnit::getUe()\r
-{\r
- int leadbits=-1;\r
- bool bit;\r
- for( bit = 0; !bit && !eonalu; leadbits++ )\r
- bit = getBits(1);\r
- if (eonalu) return true;\r
- return ((1 << leadbits)-1)+getBits(leadbits);\r
-}\r
-\r
-int NALUUnit::getSe()\r
-{\r
- UINT input=getUe();\r
- if (input==0) return 0;\r
- int output=((input+1)>>1);\r
- if (input & 0x1) output*=-1;\r
- return output;\r
-}\r
-\r
-\r
-\r
-static const int PESPacket_initial_size = 2000;\r
-\r
-// PESPacket methods\r
-PESPacket::PESPacket()\r
-{\r
- data_size = PESPacket_initial_size;\r
- data = (UCHAR*)malloc(data_size);\r
- data[0] = 0x00;\r
- data[1] = 0x00;\r
- data[2] = 0x01;\r
- init(0);\r
-}\r
-\r
-PESPacket::PESPacket(const PESPacket& packet)\r
-{\r
- copyFrom(packet);\r
-}\r
-\r
-PESPacket& PESPacket::operator=(const PESPacket& packet)\r
-{\r
- if (this != &packet)\r
- {\r
- if (data) free(data);\r
- copyFrom(packet);\r
- }\r
- return *this;\r
-}\r
-\r
-PESPacket::~PESPacket()\r
-{\r
- if (data) free(data);\r
-}\r
-\r
-void PESPacket::copyFrom(const PESPacket& packet)\r
-{\r
- length = packet.length;\r
- size = packet.size;\r
- packetType = packet.packetType;\r
- substream = packet.substream;\r
- seq_header = packet.seq_header;\r
- data_size = size;\r
- data = (UCHAR*)malloc(data_size);\r
- memcpy(data, packet.data, data_size);\r
-}\r
-\r
-void PESPacket::init(UCHAR type, UCHAR sub)\r
-{\r
- length = 0; \r
- size = 6;\r
- data[4] = data[5] = 0;\r
- data[3] = type;\r
- packetType = type;\r
- substream = sub;\r
- seq_header = 1; // Unknown seq_header status\r
-\r
-}\r
-\r
-void PESPacket::truncate()\r
-{\r
- init(packetType,substream);\r
-}\r
-\r
-\r
-\r
-int PESPacket::write(const UCHAR *buf, int len)\r
-{\r
-\r
-\r
- if (size + len > 0x10000) return 0;\r
- if (size + len > data_size)\r
- { // Reallocate\r
- UINT new_data_size = max(data_size + data_size / 2, data_size + len);\r
- if (new_data_size > 0x10000) new_data_size = 0x10000;\r
- data_size = new_data_size;\r
- data = (UCHAR*)realloc(data, data_size);\r
- }\r
- memcpy(data + size, buf, len);\r
- length += len;\r
- size += len;\r
- data[4] = (length >> 8);\r
- data[5] = (length & 0xFF);\r
- // We have added data - reset seq_header indicator if necessary\r
- if (seq_header == 0) seq_header = 1; // Reset to 'unknown'\r
- return 1;\r
-}\r
-\r
-ULLONG PESPacket::getPTS() const\r
-{\r
- if ( ( (packetType >= Demuxer::PESTYPE_AUD0 &&\r
- packetType <= Demuxer::PESTYPE_AUDMAX)\r
- ||\r
- (packetType >= Demuxer::PESTYPE_VID0 &&\r
- packetType <= Demuxer::PESTYPE_VIDMAX)\r
- ||\r
- packetType == Demuxer::PESTYPE_PRIVATE_1\r
- )\r
- && size >= 14 && data[7] & 0x80)\r
- {\r
- return ( (ULLONG)(data[ 9] & 0x0E) << 29) |\r
- ( (ULLONG)(data[10]) << 22 ) |\r
- ( (ULLONG)(data[11] & 0xFE) << 14 ) |\r
- ( (ULLONG)(data[12]) << 7 ) |\r
- ( (ULLONG)(data[13] & 0xFE) >> 1 );\r
- }\r
- else return PTS_INVALID;\r
-}\r
-\r
-UCHAR PESPacket::operator[] (UINT index) const\r
-{\r
- if (index >= size)\r
- return 0;\r
- else\r
- return data[index];\r
-}\r
-\r
-UINT PESPacket::findPictureHeader(bool h264) const\r
-{\r
- if (size < 12) return 0;\r
- UINT pattern = ( ((UINT)data[ 8] << 24) |\r
- ((UINT)data[ 9] << 16) |\r
- ((UINT)data[10] << 8) |\r
- (UINT)data[11] );\r
- UINT pos = 11;\r
- if (h264) {\r
- \r
- while (pattern != DEMUXER_H264_ACCESS_UNIT)\r
- {\r
- if (++pos >= size) return 0;\r
- pattern = (pattern << 8) | data[pos];\r
- }\r
- return pos-3;\r
- } else {\r
- while (pattern != DEMUXER_PIC_HEAD)\r
- {\r
- if (++pos >= size) return 0;\r
- pattern = (pattern << 8) | data[pos];\r
- }\r
- return pos-3;\r
- }\r
-}\r
-\r
-UINT PESPacket::countPictureHeaders(bool h264) const\r
-{\r
- if (size < 12) return 0;\r
- UINT pattern = ( ((UINT)data[ 8] << 24) |\r
- ((UINT)data[ 9] << 16) |\r
- ((UINT)data[10] << 8) |\r
- (UINT)data[11] );\r
- UINT pos = 11;\r
- UINT count=0;\r
- if (h264) {\r
- \r
- while (pos<size)\r
- {\r
- pos++;\r
- pattern = (pattern << 8) | data[pos];\r
- if (pattern==DEMUXER_H264_ACCESS_UNIT) count++;\r
- }\r
- return count;\r
- } else {\r
- while (pos<size)\r
- {\r
- pos++;\r
- pattern = (pattern << 8) | data[pos];\r
- if (pattern==DEMUXER_PIC_HEAD) count++;\r
- }\r
- return count;\r
- }\r
-}\r
-\r
-UINT PESPacket::findSeqHeader(bool h264) const\r
-{\r
- if (seq_header != 1) return seq_header;\r
- if (size < 12) return 0;\r
- UINT pattern = ( ((UINT)data[ 8] << 24) |\r
- ((UINT)data[ 9] << 16) |\r
- ((UINT)data[10] << 8) |\r
- (UINT)data[11] );\r
- UINT pos = 11;\r
- if (h264) {\r
- while ((pattern & 0xFFFFFF1F) != DEMUXER_H264_SEQ_PARAMETER_SET)\r
- {\r
- if (++pos >= size)\r
- {\r
- seq_header = 0;\r
- return 0;\r
- }\r
- pattern = (pattern << 8) | data[pos];\r
- }\r
- seq_header = pos - 3;\r
- } \r
- else \r
- {\r
- while (pattern != DEMUXER_SEQ_HEAD)\r
- {\r
- if (++pos >= size)\r
- {\r
- seq_header = 0;\r
- return 0;\r
- }\r
- pattern = (pattern << 8) | data[pos];\r
- }\r
- seq_header = pos - 3;\r
- }\r
- return seq_header;\r
-}\r
-\r
-UINT PESPacket::findSeqExtHeader(bool h264) const\r
-{\r
- if (seq_header != 1) return seq_header;\r
- if (size < 12) return 0;\r
- UINT pattern = ( ((UINT)data[ 8] << 24) |\r
- ((UINT)data[ 9] << 16) |\r
- ((UINT)data[10] << 8) |\r
- (UINT)data[11] );\r
- UINT pos = 11;\r
- if (h264) {\r
- while ((pattern & 0xFFFFFF1F) != DEMUXER_H264_SUB_ENHANCEMENT_INF)\r
- {\r
- if (++pos >= size)\r
- {\r
- seq_header = 0;\r
- return 0;\r
- }\r
- pattern = (pattern << 8) | data[pos];\r
- }\r
- seq_header = pos - 3;\r
- }\r
- else\r
- {\r
- while (pattern != DEMUXER_SEQ_EXT_HEAD && (data[pos+1]&0xf0)!=0x10)\r
- {\r
- if (++pos >= (size-1))\r
- {\r
- seq_header = 0;\r
- return 0;\r
- }\r
- pattern = (pattern << 8) | data[pos];\r
- }\r
- seq_header = pos - 3;\r
- }\r
- return seq_header;\r
-}\r
-\r
-// Demuxer methods\r
-Demuxer::Demuxer()\r
-{\r
- if (instance) return;\r
- instance = this;\r
- initted = false;\r
- callback = NULL;\r
- arcnt = 0;\r
- vid_seeking = aud_seeking = false;\r
- video_pts = audio_pts = 0;\r
- ispre_1_3_19 = false;\r
- packetnum=0;\r
- h264 = false;\r
- fps = 25.0;\r
- astreamtype=4;\r
-}\r
-\r
-Demuxer::~Demuxer()\r
-{\r
- shutdown();\r
- instance = NULL;\r
-}\r
-\r
-Demuxer* Demuxer::getInstance()\r
-{\r
- return instance;\r
-}\r
-\r
-int Demuxer::init(Callback* tcallback, DrainTarget* audio, DrainTarget* video, DrainTarget* teletext,\r
- ULONG demuxMemoryV, ULONG demuxMemoryA, ULONG demuxMemoryT,double infps, DVBSubtitles* tsubtitles)\r
-{\r
- if (!initted)\r
- {\r
- if ( !videostream.init(video, demuxMemoryV) ||\r
- !audiostream.init(audio, demuxMemoryA) ||\r
- !teletextstream.init(teletext, demuxMemoryT))\r
- {\r
- Log::getInstance()->log("Demuxer", Log::CRIT,\r
- "Failed to initialize demuxer");\r
- shutdown();\r
- return 0;\r
- }\r
- }\r
- if (teletext) {\r
- isteletextdecoded = true;\r
- } else {\r
- isteletextdecoded = false;\r
- }\r
- fps=infps;\r
- reset();\r
- initted = true;\r
- subtitles = tsubtitles;\r
- callback = tcallback;\r
- return 1;\r
-}\r
-\r
-void Demuxer::reset()\r
-{\r
- Log::getInstance()->log("Demuxer", Log::DEBUG, "Reset called");\r
- flush();\r
- video_current = audio_current = teletext_current = subtitle_current = -1;\r
- horizontal_size = vertical_size = 0;\r
- interlaced=true; // default is true\r
- aspect_ratio = (enum AspectRatio) 0;\r
- frame_rate = bit_rate = 0;\r
- ispre_1_3_19 = false;\r
- h264 = false;\r
- packetnum=0;\r
- astreamtype=4;\r
-\r
- for (int i = 0; i <= (PESTYPE_AUDMAX - PESTYPE_AUD0); i++)\r
- {\r
- avail_mpaudchan[i] = false;\r
- }\r
- for (int i = 0; i <= (PESTYPE_SUBSTREAM_AC3MAX - PESTYPE_SUBSTREAM_AC30); i++)\r
- {\r
- avail_ac3audchan[i] = false;\r
- }\r
- for (int i = 0; i <= (PESTYPE_SUBSTREAM_DVBSUBTITLEMAX - PESTYPE_SUBSTREAM_DVBSUBTITLE0); i++)\r
- {\r
- avail_dvbsubtitlechan[i] = false;\r
- }\r
-}\r
-\r
-int Demuxer::shutdown()\r
-{\r
- videostream.shutdown();\r
- audiostream.shutdown();\r
- teletextstream.shutdown();\r
- initted = false;\r
- return 1;\r
-}\r
-\r
-void Demuxer::flush()\r
-{\r
- Log::getInstance()->log("Demuxer", Log::DEBUG, "Flush called");\r
-\r
- videostream.flush();\r
- audiostream.flush();\r
- teletextstream.flush();\r
-}\r
-\r
-void Demuxer::flushAudio()\r
-{\r
- audiostream.flush();\r
-}\r
-\r
-void Demuxer::seek()\r
-{\r
- vid_seeking = aud_seeking = true;\r
- video_pts = audio_pts = teletext_pts = 0;\r
-}\r
-\r
-void Demuxer::setAudioStream(int id)\r
-{\r
- audio_current = id;\r
-}\r
-\r
-void Demuxer::setVideoStream(int id)\r
-{\r
- video_current = id;\r
-}\r
-\r
-void Demuxer::setTeletextStream(int id)\r
-{\r
- teletext_current = id;\r
-}\r
-\r
-void Demuxer::setDVBSubtitleStream(int id)\r
-{\r
- subtitle_current = id;\r
-}\r
-\r
-void Demuxer::setAspectRatio(enum AspectRatio ar)\r
-{\r
- if (aspect_ratio != ar)\r
- {\r
- Log::getInstance()->log("Demux", Log::DEBUG,\r
- "Aspect ratio difference signalled");\r
- if (++arcnt > 3) // avoid changing aspect ratio if glitch in signal\r
- {\r
- arcnt = 0;\r
- aspect_ratio = ar;\r
- if (callback) callback->call(this);\r
- }\r
- }\r
- else\r
- arcnt = 0;\r
-}\r
-\r
-bool Demuxer::writeAudio(bool * dataavail)\r
-{\r
- return audiostream.drain(dataavail);\r
-}\r
-\r
-bool Demuxer::writeVideo(bool * dataavail)\r
-{\r
- return videostream.drain(dataavail);\r
-}\r
-\r
-bool Demuxer::writeTeletext(bool * dataavail)\r
-{\r
- return teletextstream.drain(dataavail);\r
-}\r
-\r
-bool Demuxer::submitPacket(PESPacket& packet)\r
-{\r
- UINT sent = 0;\r
- UCHAR packet_type = packet.getPacketType();\r
- const UCHAR* packetdata = packet.getData();\r
- if (packet_type >= PESTYPE_VID0 && packet_type <= PESTYPE_VIDMAX)\r
- {\r
- if (video_current == -1) video_current = packet_type;\r
- if (video_current == packet_type && !vid_seeking)\r
- {\r
- sent = videostream.put(&packetdata[0], packet.getSize(), h264?MPTYPE_VIDEO_H264:MPTYPE_VIDEO_MPEG2,packetnum);\r
- if (sent) packetnum++;\r
- }\r
- else\r
- sent = packet.getSize();\r
- }\r
- else if (packet_type >= PESTYPE_AUD0 && packet_type <= PESTYPE_AUDMAX)\r
- {\r
-\r
- if (audio_current == -1) audio_current = packet_type;\r
- avail_mpaudchan[packet_type - PESTYPE_AUD0] = true;\r
- if (audio_current == packet_type && !aud_seeking)\r
- {\r
- UCHAR type=MPTYPE_MPEG_AUDIO;\r
- switch (astreamtype)\r
- {\r
- case 3:\r
- case 4:\r
- type=MPTYPE_MPEG_AUDIO; break;\r
- case 0x11:\r
- type=MPTYPE_AAC_LATM; break;\r
- };\r
- sent = audiostream.put(&packetdata[0], packet.getSize(), type,packetnum);\r
- if (sent) packetnum++;\r
- }\r
- else\r
- sent = packet.getSize();\r
- }\r
- else if (packet_type == PESTYPE_PRIVATE_1 &&\r
- packet.getSubstream() >= PESTYPE_SUBSTREAM_AC30 &&\r
- packet.getSubstream() <= PESTYPE_SUBSTREAM_AC3MAX)\r
- {\r
- avail_ac3audchan[packet.getSubstream() - PESTYPE_SUBSTREAM_AC30] = true;\r
- if (packet.getSubstream() == audio_current)\r
- {\r
- sent = audiostream.put(&packetdata[0], packet.getSize(), (ispre_1_3_19)? MPTYPE_AC3_PRE13 : MPTYPE_AC3,packetnum);\r
- if (sent) packetnum++;\r
- }\r
- else\r
- {\r
- sent = packet.getSize();\r
- }\r
- }\r
- else if (packet_type == PESTYPE_PRIVATE_1 &&\r
- packet.getSubstream() >= PESTYPE_SUBSTREAM_DVBSUBTITLE0 &&\r
- packet.getSubstream() <= PESTYPE_SUBSTREAM_DVBSUBTITLEMAX)\r
- {\r
- avail_dvbsubtitlechan[packet.getSubstream()-PESTYPE_SUBSTREAM_DVBSUBTITLE0]=true;\r
- if (subtitle_current == -1) subtitle_current = packet.getSubstream();\r
- if (subtitles && packet.getSubstream()==subtitle_current)\r
- {\r
- subtitles->put(packet);\r
- }\r
- sent = packet.getSize();\r
- }\r
- else if (isteletextdecoded && packet_type == PESTYPE_PRIVATE_1 &&\r
- packet.getSubstream() >= PESTYPE_SUBSTREAM_TELETEXT0 &&\r
- packet.getSubstream() <= PESTYPE_SUBSTREAM_TELETEXTMAX)\r
- {\r
-\r
- if (teletext_current == -1) teletext_current = packet.getSubstream();\r
- if (teletext_current == packet.getSubstream())\r
- {\r
- sent = teletextstream.put(&packetdata[0], packet.getSize(), MPTYPE_TELETEXT,packetnum);\r
- }\r
- else \r
- {\r
- sent = packet.getSize();\r
- }\r
- }\r
- else\r
- {\r
- sent = packet.getSize();\r
- }\r
-\r
- if (sent < packet.getSize()) // Stream is full.\r
- return false;\r
- else\r
- return true;\r
-}\r
-\r
-void Demuxer::parsePacketDetails(PESPacket& packet)\r
-{\r
- if (packet.getPacketType() >= PESTYPE_AUD0 &&\r
- packet.getPacketType() <= PESTYPE_AUDMAX)\r
- {\r
- // Extract audio PTS if it exists\r
- if (packet.hasPTS())\r
- {\r
- audio_pts = packet.getPTS();\r
- // We continue to seek on the audio if the video PTS that we\r
- // are trying to match is ahead of the audio PTS by at most\r
- // SEEK_THRESHOLD. We consider the possibility of PTS wrap.\r
- if (aud_seeking && !vid_seeking &&\r
- !( (video_pts_seek > audio_pts &&\r
- video_pts_seek - audio_pts < SEEK_THRESHOLD)\r
- ||\r
- (video_pts_seek < audio_pts &&\r
- video_pts_seek + (1LL<<33) - audio_pts < SEEK_THRESHOLD) ))\r
- {\r
- aud_seeking = 0;\r
- Log::getInstance()->log("Demuxer", Log::DEBUG,\r
- "Leaving audio sync: Audio PTS = %llu", audio_pts);\r
- }\r
- }\r
- }\r
- else if (packet.getPacketType() == PESTYPE_PRIVATE_1) // Private stream\r
- {\r
- //Inspired by vdr's device.c\r
- int payload_begin = packet[8]+9;\r
- unsigned char substream_id = packet[payload_begin];\r
- unsigned char substream_type = substream_id & 0xF0;\r
- unsigned char substream_index = substream_id & 0x1F;\r
-pre_1_3_19_Recording: //This is for old recordings stuff and live TV\r
- if (ispre_1_3_19)\r
- {\r
- int old_substream=packet.getSubstream();\r
- if (old_substream){ //someone else already set it, this is live tv\r
- substream_id = old_substream;\r
- substream_type = substream_id & 0xF0;\r
- substream_index = substream_id & 0x1F;\r
- } else {\r
- substream_id = PESTYPE_PRIVATE_1;\r
- substream_type = 0x80;\r
- substream_index = 0;\r
- }\r
-\r
- }\r
- switch (substream_type)\r
- {\r
- case 0x20://SPU\r
- case 0x30://SPU\r
- packet.setSubstream(substream_id);\r
- break;\r
- case 0xA0: //LPCM //not supported yet, is there any LPCM transmissio out there?\r
- break;\r
- case 0x80: //ac3, currently only one ac3 track per recording supported\r
- packet.setSubstream(substream_type+substream_index);\r
-\r
- // Extract audio PTS if it exists\r
- if (packet.hasPTS())\r
- {\r
- audio_pts = packet.getPTS();\r
- // We continue to seek on the audio if the video PTS that we\r
- // are trying to match is ahead of the audio PTS by at most\r
- // SEEK_THRESHOLD. We consider the possibility of PTS wrap.\r
- if (aud_seeking && !vid_seeking &&\r
- !( (video_pts_seek > audio_pts &&\r
- video_pts_seek - audio_pts < SEEK_THRESHOLD)\r
- ||\r
- (video_pts_seek < audio_pts &&\r
- video_pts_seek + (1LL<<33) - audio_pts < SEEK_THRESHOLD) ))\r
- {\r
- aud_seeking = 0;\r
- Log::getInstance()->log("Demuxer", Log::DEBUG, "Leaving audio sync: Audio PTS = %llu", audio_pts);\r
- }\r
- }\r
- break;\r
- case 0x10: //Teletext Is this correct?\r
- packet.setSubstream(substream_id);\r
- // Extract teletxt PTS if it exists\r
- if (packet.hasPTS())\r
- {\r
- teletext_pts = packet.getPTS();\r
- }\r
- break;\r
- default:\r
- if (!ispre_1_3_19)\r
- {\r
- ispre_1_3_19=true; //switching to compat mode and live tv mode\r
- goto pre_1_3_19_Recording;\r
- }\r
- else\r
- {\r
- packet.setSubstream(0);\r
- }\r
- break;\r
- }\r
- }\r
- else if (packet.getPacketType() >= PESTYPE_VID0 &&\r
- packet.getPacketType() <= PESTYPE_VIDMAX)\r
- {\r
- // Extract video PTS if it exists\r
- if (packet.hasPTS()) video_pts = packet.getPTS();\r
-\r
- // If there is a sequence header, extract information\r
- UINT pos = packet.findSeqHeader(h264);\r
- if (pos > 1)\r
- {\r
- if (!h264) {\r
- pos += 4;\r
- if (pos+6 >= packet.getSize()) return;\r
- horizontal_size = ((int)packet[pos] << 4) | ((int)packet[pos+1] >> 4);\r
- \r
- vertical_size = (((int)packet[pos+1] & 0xf) << 8) | (int)packet[pos+2];\r
-\r
- setAspectRatio((enum AspectRatio)(packet[pos+3] >> 4));\r
- frame_rate = packet[pos+3] & 0x0f;\r
- if (frame_rate >= 1 && frame_rate <= 8)\r
- frame_rate = FrameRates[frame_rate];\r
- else\r
- frame_rate = 0;\r
- bit_rate = ((int)packet[pos+4] << 10) |\r
- ((int)packet[pos+5] << 2) |\r
- ((int)packet[pos+6] >> 6);\r
- } \r
- else\r
- {\r
- /* Chris and Mark I know this is ugly, should we move this to a method of PESPacket or to NALUUnit what would be better?\r
- 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*/\r
- NALUUnit nalu(packet.getData()+pos,packet.getSize()-pos);\r
- profile=nalu.getBits(8);\r
- nalu.getBits(8); //constraints\r
- nalu.getBits(8); //level_idc\r
- nalu.getUe(); //seq_parameter_set_id\r
- int chroma=1;\r
- if (profile==100 || profile==110 || profile==122 || profile==144)\r
- {\r
- chroma=nalu.getUe();\r
- if (chroma==3)\r
- {\r
- nalu.getBits(1);\r
- }\r
- nalu.getUe(); //bit depth lume\r
- nalu.getUe(); //bit depth chrome\r
- nalu.getBits(1);\r
- if (nalu.getBits(1))\r
- {\r
- for (int i=0;i<8;i++){\r
- if (nalu.getBits(1))\r
- {\r
- if (i<6)\r
- {\r
- UINT lastscale=8;\r
- UINT nextscale=8;\r
- for (int j=0;j<16;j++) {\r
- if (nextscale!=0) {\r
- UINT delta=nalu.getSe();\r
- nextscale=(lastscale+delta+256)%256;\r
- }\r
- lastscale=(nextscale==0)?lastscale:nextscale;\r
- }\r
- }\r
- else\r
- {\r
- UINT lastscale=8;\r
- UINT nextscale=8;\r
- for (int j=0;j<64;j++) {\r
- if (nextscale!=0) {\r
- UINT delta=nalu.getSe();\r
- nextscale=(lastscale+delta+256)%256;\r
- }\r
- lastscale=(nextscale==0)?lastscale:nextscale;\r
- }\r
- }\r
- }\r
- }\r
- }\r
- }\r
- int chromunitx=1;\r
- int chromunity=1;\r
- switch (chroma) {\r
- case 0:\r
- chromunitx=chromunity=1; break;\r
- case 1:\r
- chromunitx=chromunity=2; break;\r
- case 2:\r
- chromunitx=2;chromunity=1; break;\r
- case 3:\r
- chromunitx=chromunity=1; break;\r
- };\r
-\r
- nalu.getUe(); //log2framenum\r
- UINT temp=nalu.getUe();\r
- if (temp==0) //pict order\r
- nalu.getUe();\r
- else if (temp==1) {\r
- nalu.getBits(1);\r
- nalu.getSe();\r
- nalu.getSe();\r
- UINT temp2=nalu.getUe();\r
- for (int i=0;i<temp2;i++)\r
- nalu.getSe();\r
- }\r
- nalu.getUe(); //Num refframes\r
- nalu.getBits(1);\r
- horizontal_size=(nalu.getUe()+1)*16;\r
- \r
- vertical_size=(nalu.getUe()+1)*16;\r
- int tinterlaced=nalu.getBits(1);\r
- vertical_size*=(2-tinterlaced);\r
- interlaced=!tinterlaced;\r
- \r
- if (!tinterlaced) nalu.getBits(1);\r
- nalu.getBits(1);\r
- if (nalu.getBits(1))\r
- {\r
- horizontal_size-=nalu.getUe()*chromunitx;\r
- horizontal_size-=nalu.getUe()*chromunitx;\r
- vertical_size-=nalu.getUe()*(2-tinterlaced)*chromunity;\r
- vertical_size-=nalu.getUe()*(2-tinterlaced)*chromunity;\r
- }\r
- if (nalu.getBits(1))\r
- {\r
- if (nalu.getBits(1))\r
- {\r
- UINT aspectratioidc=nalu.getBits(8);\r
- bool hasaspect=false;\r
- const float aspects[]={1., 1./1.,12./11.,10./11.,16./11.,40./33.,\r
- 24./11.,20./11.,32./11.,80./33.,18./11.,15./11.,64./33.,160./99.,4./3.,3./2.,2./1.};\r
- \r
- float aspectratio=((float) horizontal_size)/((float) vertical_size);\r
- if (aspectratioidc<=16) \r
- {\r
- hasaspect=true;\r
- aspectratio*=aspects[aspectratioidc];\r
- \r
- }\r
- else if (aspectratioidc==255)\r
- {\r
- int t_sar_width=nalu.getBits(16);\r
- int t_sar_height=nalu.getBits(16);\r
- if (t_sar_width!=0 && t_sar_height!=0)\r
- {\r
- hasaspect=true;\r
- aspectratio*=((float)t_sar_width)/((float)t_sar_height);\r
- }\r
- }\r
- if (hasaspect)\r
- {\r
- if (fabs(aspectratio-16./9.)<0.1) setAspectRatio(ASPECT_16_9);\r
- else if (fabs(aspectratio-4./3.)<0.1) setAspectRatio(ASPECT_4_3);\r
- }\r
- }\r
-\r
- }\r
-\r
- }\r
- UINT posext = packet.findSeqExtHeader(h264);\r
- if (posext>1) {\r
- if (!h264) {\r
- interlaced=!(packet[pos+1] & 0x08); //really simple\r
- // if more than full hd is coming we need to add additional parsers here\r
- } else {\r
- /* NALUUnit nalu(packet.getData()+posext,packet.getSize()-posext);\r
- while (!nalu.isEonalu()) {\r
- unsigned int payloadtype=0;\r
- unsigned int payloadadd=0xFF;\r
- while (payloadadd==0xFF && !nalu.isEonalu()) {\r
- payloadadd=nalu.getBits(8);\r
- payloadtype+=payloadadd;\r
- }\r
- unsigned int payloadsize=0;\r
- payloadadd=0xff;\r
- while (payloadadd==0xFF && !nalu.isEonalu()) {\r
- payloadadd=nalu.getBits(8);\r
- payloadsize+=payloadadd;\r
- }\r
- switch (payloadtype) {\r
- case 1: { // picture timing SEI\r
-\r
- } break;\r
- default: {\r
- while (payloadsize) { // not handled skip\r
- nalu.getBits(8);\r
- payloadsize--;\r
- }\r
- } break;\r
- }\r
-\r
-\r
-\r
- }*/\r
-\r
-\r
- }\r
- }\r
- \r
- if (vid_seeking)\r
- {\r
- vid_seeking = 0;\r
- video_pts_seek = video_pts;\r
- Log::getInstance()->log("Demuxer", Log::DEBUG,\r
- "Entering audio sync: Video PTS = %llu", video_pts);\r
- Log::getInstance()->log("Demuxer", Log::DEBUG,\r
- "Entering audio sync: Audio PTS = %llu", audio_pts);\r
- }\r
- return;\r
- } \r
- }\r
-}\r
-\r
-UINT Demuxer::stripAudio(UCHAR* buf, UINT len)\r
-{\r
- UINT read_pos = 0, write_pos = 0;\r
- UINT pattern, packet_length;\r
- if (len < 4) return 0;\r
- pattern = (buf[0] << 16) | (buf[1] << 8) | (buf[2]);\r
- while (read_pos + 7 <= len)\r
- {\r
- pattern = ((pattern & 0xFFFFFF) << 8) | buf[read_pos+3];\r
- if (pattern < (0x100|PESTYPE_VID0) || pattern > (0x100|PESTYPE_VIDMAX))\r
- read_pos++;\r
- else\r
- {\r
- packet_length = ((buf[read_pos+4] << 8) | (buf[read_pos+5])) + 6;\r
- if (read_pos + packet_length > len)\r
- read_pos = len;\r
- else\r
- {\r
- if (read_pos != write_pos)\r
- memmove(buf+write_pos, buf+read_pos, packet_length);\r
- read_pos += packet_length;\r
- write_pos += packet_length;\r
- pattern = (buf[read_pos] << 16) | (buf[read_pos+1] << 8)\r
- | (buf[read_pos+2]);\r
- }\r
- }\r
- }\r
- return write_pos;\r
-}\r
-\r
-void Demuxer::changeTimes(UCHAR* buf, UINT len,UINT playtime)\r
-{\r
- UINT pattern, packet_length;\r
- UINT read_pos = 0;\r
- if (len < 4) return;\r
- pattern = (buf[0] << 16) | (buf[1] << 8) | (buf[2]);\r
- while (read_pos + 7 <= len)\r
- {\r
- pattern = ((pattern & 0xFFFFFF) << 8) | buf[read_pos+3];\r
- if (pattern < (0x100|PESTYPE_VID0) || pattern > (0x100|PESTYPE_VIDMAX))\r
- read_pos++;\r
- else\r
- {\r
- packet_length = ((buf[read_pos+4] << 8) | (buf[read_pos+5])) + 6;\r
- // ok we have a packet figure out if pts and dts are present and replace them\r
- if (read_pos + 19 > len) return;\r
- ULLONG new_ts=playtime*90; //play time is on ms so multiply it by 90\r
- if (buf[read_pos+7] & 0x80) { // pts is here, replace it\r
- buf[read_pos+9]=0x21 | (( new_ts>>29)& 0xde );\r
- buf[read_pos+10]=0x00 |(( new_ts>>22)& 0xff );\r
- buf[read_pos+11]=0x01 | (( new_ts>>14)& 0xfe );\r
- buf[read_pos+12]=0x00 | (( new_ts>>7)& 0xff );\r
- buf[read_pos+13]=0x01 | (( new_ts<<1)& 0xfe );\r
- }\r
-\r
- if (buf[read_pos+7] & 0x40) { // pts is here, replace it\r
- buf[read_pos+14]=0x21 | (( new_ts>>29)& 0xde );\r
- buf[read_pos+15]=0x00 | (( new_ts>>22)& 0xff );\r
- buf[read_pos+16]=0x01 | (( new_ts>>14)& 0xfe );\r
- buf[read_pos+17]=0x00 | (( new_ts>>7)& 0xff );\r
- buf[read_pos+18]=0x01 | (( new_ts<<1)& 0xfe );\r
- }\r
- read_pos += packet_length;\r
- pattern = (buf[read_pos] << 16) | (buf[read_pos+1] << 8)\r
- | (buf[read_pos+2]);\r
- }\r
- }\r
-\r
-}\r
-\r
-bool Demuxer::scanForVideo(UCHAR* buf, UINT len, bool &ish264)\r
-{\r
- UINT pos = 3;\r
- UINT pattern;\r
- ish264=false;\r
- if (len < 4) return false;\r
- pattern = (buf[0] << 16) | (buf[1] << 8) | (buf[2]);\r
- while (pos < len)\r
- {\r
- pattern = ((pattern & 0xFFFFFF) << 8) | buf[pos++];\r
- if (pattern >= (0x100|PESTYPE_VID0) && pattern <= (0x100|PESTYPE_VIDMAX))\r
- return true;\r
- }\r
- return false;\r
-}\r
-\r
-bool* Demuxer::getmpAudioChannels()\r
-{\r
- return avail_mpaudchan;\r
-}\r
-\r
-bool* Demuxer::getac3AudioChannels()\r
-{\r
- return avail_ac3audchan;\r
-}\r
-\r
-bool* Demuxer::getSubtitleChannels()\r
-{\r
- return avail_dvbsubtitlechan;\r
-}\r
-\r
-int Demuxer::getselSubtitleChannel()\r
-{\r
- return subtitle_current;\r
-}\r
-\r
-int Demuxer::getselAudioChannel()\r
-{\r
- return audio_current;\r
-}\r
-\r
-\r
+/*
+ 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 <cstdlib>
+
+#include <math.h>
+
+#define DEMUXER_SEQ_HEAD 0x000001B3
+#define DEMUXER_PIC_HEAD 0x00000101
+#define DEMUXER_SEQ_EXT_HEAD 0x000001B5
+
+#define DEMUXER_H264_ACCESS_UNIT 0x00000109
+#define DEMUXER_H264_SEQ_PARAMETER_SET 0x00000107
+#define DEMUXER_H264_SUB_ENHANCEMENT_INF 0x00000106
+
+
+#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<nalu_length)
+ {
+ last_bytes=(last_bytes<<8) & nalu_buf[pos];
+ if ((last_bytes & 0x00FFFFFF) == 0x000003) pos++; //emulation prevention byte
+ if (pos<nalu_length)
+ {
+ working_byte=nalu_buf[pos];
+ pos++;
+ }
+ else
+ {
+ working_byte=0;
+ eonalu=true;
+ }
+ }
+ else
+ {
+ working_byte=0;
+ eonalu=true;
+ }
+
+ }
+ UINT fetch_bits=min(remain_bits,8-bit_pos);
+ work=work <<fetch_bits;
+ //work|=((working_byte>>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)
+ {
+ pos++;
+ pattern = (pattern << 8) | data[pos];
+ if (pattern==DEMUXER_H264_ACCESS_UNIT) count++;
+ }
+ return count;
+ } else {
+ while (pos<size)
+ {
+ pos++;
+ pattern = (pattern << 8) | data[pos];
+ if (pattern==DEMUXER_PIC_HEAD) count++;
+ }
+ return count;
+ }
+}
+
+UINT PESPacket::findSeqHeader(bool h264) const
+{
+ if (seq_header != 1) return seq_header;
+ 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 & 0xFFFFFF1F) != DEMUXER_H264_SEQ_PARAMETER_SET)
+ {
+ if (++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;
+}
+
+UINT PESPacket::findSeqExtHeader(bool h264) const
+{
+ if (seq_header != 1) return seq_header;
+ 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 & 0xFFFFFF1F) != DEMUXER_H264_SUB_ENHANCEMENT_INF)
+ {
+ if (++pos >= size)
+ {
+ seq_header = 0;
+ return 0;
+ }
+ pattern = (pattern << 8) | data[pos];
+ }
+ seq_header = pos - 3;
+ }
+ else
+ {
+ while (pattern != DEMUXER_SEQ_EXT_HEAD && (data[pos+1]&0xf0)!=0x10)
+ {
+ if (++pos >= (size-1))
+ {
+ 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;
+ astreamtype=4;
+}
+
+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;
+ interlaced=true; // default is true
+ aspect_ratio = (enum AspectRatio) 0;
+ frame_rate = bit_rate = 0;
+ ispre_1_3_19 = false;
+ h264 = false;
+ packetnum=0;
+ astreamtype=4;
+
+ 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(bool * dataavail)
+{
+ return audiostream.drain(dataavail);
+}
+
+bool Demuxer::writeVideo(bool * dataavail)
+{
+ return videostream.drain(dataavail);
+}
+
+bool Demuxer::writeTeletext(bool * dataavail)
+{
+ return teletextstream.drain(dataavail);
+}
+
+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)
+ {
+ UCHAR type=MPTYPE_MPEG_AUDIO;
+ switch (astreamtype)
+ {
+ case 3:
+ case 4:
+ type=MPTYPE_MPEG_AUDIO; break;
+ case 0x11:
+ type=MPTYPE_AAC_LATM; break;
+ };
+ sent = audiostream.put(&packetdata[0], packet.getSize(), type,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)
+ {
+ int old_substream=packet.getSubstream();
+ if (old_substream){ //someone else already set it, this is live tv
+ substream_id = old_substream;
+ substream_type = substream_id & 0xF0;
+ substream_index = substream_id & 0x1F;
+ } else {
+ 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;i<temp2;i++)
+ nalu.getSe();
+ }
+ nalu.getUe(); //Num refframes
+ nalu.getBits(1);
+ horizontal_size=(nalu.getUe()+1)*16;
+
+ vertical_size=(nalu.getUe()+1)*16;
+ int tinterlaced=nalu.getBits(1);
+ vertical_size*=(2-tinterlaced);
+ interlaced=!tinterlaced;
+
+ if (!tinterlaced) nalu.getBits(1);
+ nalu.getBits(1);
+ if (nalu.getBits(1))
+ {
+ horizontal_size-=nalu.getUe()*chromunitx;
+ horizontal_size-=nalu.getUe()*chromunitx;
+ vertical_size-=nalu.getUe()*(2-tinterlaced)*chromunity;
+ vertical_size-=nalu.getUe()*(2-tinterlaced)*chromunity;
+ }
+ if (nalu.getBits(1))
+ {
+ if (nalu.getBits(1))
+ {
+ UINT aspectratioidc=nalu.getBits(8);
+ bool hasaspect=false;
+ const float aspects[]={1., 1./1.,12./11.,10./11.,16./11.,40./33.,
+ 24./11.,20./11.,32./11.,80./33.,18./11.,15./11.,64./33.,160./99.,4./3.,3./2.,2./1.};
+
+ float aspectratio=((float) horizontal_size)/((float) vertical_size);
+ if (aspectratioidc<=16)
+ {
+ hasaspect=true;
+ aspectratio*=aspects[aspectratioidc];
+
+ }
+ else if (aspectratioidc==255)
+ {
+ int t_sar_width=nalu.getBits(16);
+ int t_sar_height=nalu.getBits(16);
+ if (t_sar_width!=0 && t_sar_height!=0)
+ {
+ hasaspect=true;
+ aspectratio*=((float)t_sar_width)/((float)t_sar_height);
+ }
+ }
+ if (hasaspect)
+ {
+ if (fabs(aspectratio-16./9.)<0.1) setAspectRatio(ASPECT_16_9);
+ else if (fabs(aspectratio-4./3.)<0.1) setAspectRatio(ASPECT_4_3);
+ }
+ }
+
+ }
+
+ }
+ UINT posext = packet.findSeqExtHeader(h264);
+ if (posext>1) {
+ if (!h264) {
+ interlaced=!(packet[pos+1] & 0x08); //really simple
+ // if more than full hd is coming we need to add additional parsers here
+ } else {
+ /* NALUUnit nalu(packet.getData()+posext,packet.getSize()-posext);
+ while (!nalu.isEonalu()) {
+ unsigned int payloadtype=0;
+ unsigned int payloadadd=0xFF;
+ while (payloadadd==0xFF && !nalu.isEonalu()) {
+ payloadadd=nalu.getBits(8);
+ payloadtype+=payloadadd;
+ }
+ unsigned int payloadsize=0;
+ payloadadd=0xff;
+ while (payloadadd==0xFF && !nalu.isEonalu()) {
+ payloadadd=nalu.getBits(8);
+ payloadsize+=payloadadd;
+ }
+ switch (payloadtype) {
+ case 1: { // picture timing SEI
+
+ } break;
+ default: {
+ while (payloadsize) { // not handled skip
+ nalu.getBits(8);
+ payloadsize--;
+ }
+ } break;
+ }
+
+
+
+ }*/
+
+
+ }
+ }
+
+ if (vid_seeking)
+ {
+ vid_seeking = 0;
+ video_pts_seek = video_pts;
+ Log::getInstance()->log("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;
+}
+
+
-/*\r
- Copyright 2005-2008 Mark Calderbank\r
- Copyright 2007 Marten Richter (AC3 support)\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software Foundation, Inc.,\r
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-/*\r
-\r
-Thanks go to Jon Gettler of the MVPMC project and Stephen Rice for\r
-the demuxer in mvpmc. It was used in the creation of this demuxer;\r
-however, no code was copied verbatim.\r
-\r
-*/\r
-\r
-#ifndef DEMUXER_H\r
-#define DEMUXER_H\r
-\r
-#include "stream.h"\r
-#include "defines.h"\r
-\r
-class DVBSubtitles;\r
-class Callback;\r
-class DrainTarget;\r
-\r
-class PESPacket\r
-{\r
- public:\r
- PESPacket();\r
- PESPacket(const PESPacket& packet);\r
- PESPacket& operator=(const PESPacket& packet);\r
- ~PESPacket();\r
- void init(UCHAR type, UCHAR sub=0);\r
- void truncate();\r
- int write(const UCHAR* buf, int len);\r
-\r
- UCHAR operator[] (UINT index) const;\r
- // return data[index] if in bounds, else 0\r
- // so no proper error condition but never mind for now\r
- const UCHAR* getData() const { return data; }\r
- UINT getLength() const { return length; }\r
- UINT getSize() const { return size; }\r
- UCHAR getPacketType() const { return packetType; }\r
- void setSubstream(UCHAR s) { substream = s; }\r
- UCHAR getSubstream() const { return substream; }\r
- ULLONG getPTS() const;\r
- bool hasPTS() const { return (getPTS() != PTS_INVALID); }\r
-\r
- UINT findPictureHeader(bool h264) const;\r
- UINT findSeqHeader(bool h264) const;\r
- UINT findSeqExtHeader(bool h264) const;\r
- UINT countPictureHeaders(bool h264) const;\r
- static const ULLONG PTS_INVALID = (1LL << 33);\r
-\r
-\r
-\r
- protected:\r
- void copyFrom(const PESPacket& packet);\r
- UCHAR * data;\r
- UINT length, size;\r
- UINT data_size;\r
- UCHAR packetType;\r
- UCHAR substream;\r
- UINT mutable seq_header; // 0 = no, 1 = unknown, else = header offset\r
-};\r
-\r
-class Demuxer\r
-{\r
- public:\r
- Demuxer();\r
- virtual ~Demuxer();\r
- static Demuxer* getInstance();\r
- int init(Callback* tcallback, DrainTarget* audio, DrainTarget* video,\r
- DrainTarget* teletext,\r
- ULONG demuxMemoryV, ULONG demuxMemoryA, ULONG demuxMemoryT, double fps=25.,\r
- DVBSubtitles* tsubtitles=NULL);\r
- virtual void reset();\r
- virtual void flush();\r
- void flushAudio();\r
- void seek();\r
- void setVideoStream(int id);\r
- //TODO HANS next virtual necessary?\r
- //virtual void setAudioStream(int id);\r
- void setAudioStream(int id);\r
- void setTeletextStream(int id);\r
- void setDVBSubtitleStream(int id);\r
- bool writeAudio(bool * dataavail=NULL);\r
- bool writeVideo(bool * dataavail=NULL);\r
- bool writeTeletext(bool * dataavail=NULL);\r
-\r
- virtual int scan(UCHAR* buf, int len) = 0;\r
- virtual int findPTS(UCHAR* buf, int len, ULLONG* dest) = 0;\r
- virtual int put(UCHAR* buf, int len) = 0;\r
- virtual void setFrameNum(ULONG frame) {}\r
- virtual void setPacketNum(ULONG packet) {}\r
- virtual ULONG getFrameNumFromPTS(ULLONG pts) {return 0;}\r
- virtual ULONG getPacketNum() {return 0;}\r
-\r
- bool* getmpAudioChannels(); //Maybe virtual ?\r
- bool* getac3AudioChannels(); //Maybe virtual ?\r
- bool* getSubtitleChannels();\r
- int getselAudioChannel();\r
- int getselSubtitleChannel();\r
- bool ish264() {return h264;}\r
- void seth264(bool newh264){h264=newh264;}\r
-\r
- int getHorizontalSize() { return horizontal_size; }\r
- int getVerticalSize() { return vertical_size; }\r
- int getAspectRatio() { return aspect_ratio; }\r
- int getFrameRate() { return frame_rate; }\r
- int getBitRate() { return bit_rate; }\r
- bool getInterlaced() { return interlaced;}\r
- ULLONG getVideoPTS() { return video_pts; }\r
- ULLONG getAudioPTS() { return audio_pts; }\r
-\r
- enum AspectRatio\r
- {\r
- ASPECT_4_3 = 2,\r
- ASPECT_16_9 = 3\r
- };\r
-\r
- // Remove all data from a buffer apart from video PES packets.\r
- // Returns the length of the reduced data.\r
- // removed *static function*, due to DemuxerTS\r
- virtual UINT stripAudio(UCHAR* buf, UINT len);\r
- void changeTimes(UCHAR* buf, UINT len,UINT playtime);\r
-\r
- // Scan a buffer to see if video packets are present.\r
- // Returns true if video exists; false if not.\r
- // removed *static function* for h264 detection\r
- static bool scanForVideo(UCHAR* buf, UINT len, bool &ish264);\r
-\r
- enum PESTYPE\r
- {\r
- PESTYPE_PRIVATE_1 = 0xBD,\r
-\r
- PESTYPE_AUD0 = 0xC0,\r
- PESTYPE_AUD1, PESTYPE_AUD2, PESTYPE_AUD3, PESTYPE_AUD4,\r
- PESTYPE_AUD5, PESTYPE_AUD6, PESTYPE_AUD7, PESTYPE_AUD8,\r
- PESTYPE_AUD9, PESTYPE_AUD10, PESTYPE_AUD11, PESTYPE_AUD12,\r
- PESTYPE_AUD13, PESTYPE_AUD14, PESTYPE_AUD15, PESTYPE_AUD16,\r
- PESTYPE_AUD17, PESTYPE_AUD18, PESTYPE_AUD19, PESTYPE_AUD20,\r
- PESTYPE_AUD21, PESTYPE_AUD22, PESTYPE_AUD23, PESTYPE_AUD24,\r
- PESTYPE_AUD25, PESTYPE_AUD26, PESTYPE_AUD27, PESTYPE_AUD28,\r
- PESTYPE_AUD29, PESTYPE_AUD30, PESTYPE_AUD31,\r
- PESTYPE_AUDMAX = PESTYPE_AUD31,\r
-\r
- PESTYPE_VID0 = 0xE0,\r
- PESTYPE_VID1, PESTYPE_VID2, PESTYPE_VID3, PESTYPE_VID4,\r
- PESTYPE_VID5, PESTYPE_VID6, PESTYPE_VID7, PESTYPE_VID8,\r
- PESTYPE_VID9, PESTYPE_VID10, PESTYPE_VID11, PESTYPE_VID12,\r
- PESTYPE_VID13, PESTYPE_VID14, PESTYPE_VID15,\r
- PESTYPE_VIDMAX = PESTYPE_VID15\r
- };\r
- enum PESTYPE_SUBSTREAM\r
- {\r
- PESTYPE_SUBSTREAM_TELETEXT0 = 0x10,\r
- PESTYPE_SUBSTREAM_TELETEXT1,PESTYPE_SUBSTREAM_TELETEXT2, PESTYPE_SUBSTREAM_TELETEXT3,\r
- PESTYPE_SUBSTREAM_TELETEXT4,PESTYPE_SUBSTREAM_TELETEXT5,PESTYPE_SUBSTREAM_TELETEXT6,\r
- PESTYPE_SUBSTREAM_TELETEXT7, PESTYPE_SUBSTREAM_TELETEXT8, PESTYPE_SUBSTREAM_TELETEXT9,\r
- PESTYPE_SUBSTREAM_TELETEXT10,PESTYPE_SUBSTREAM_TELETEXT11,PESTYPE_SUBSTREAM_TELETEXT12,\r
- PESTYPE_SUBSTREAM_TELETEXT13,PESTYPE_SUBSTREAM_TELETEXT14,PESTYPE_SUBSTREAM_TELETEXT15,\r
- PESTYPE_SUBSTREAM_TELETEXTMAX=PESTYPE_SUBSTREAM_TELETEXT15,\r
- PESTYPE_SUBSTREAM_DVBSUBTITLE0=0x20,\r
- PESTYPE_SUBSTREAM_DVBSUBTITLE1,PESTYPE_SUBSTREAM_DVBSUBTITLE2,PESTYPE_SUBSTREAM_DVBSUBTITLE3,\r
- PESTYPE_SUBSTREAM_DVBSUBTITLE4,PESTYPE_SUBSTREAM_DVBSUBTITLE5,PESTYPE_SUBSTREAM_DVBSUBTITLE6,\r
- PESTYPE_SUBSTREAM_DVBSUBTITLE7,\r
- PESTYPE_SUBSTREAM_DVBSUBTITLEMAX=PESTYPE_SUBSTREAM_DVBSUBTITLE7,\r
- PESTYPE_SUBSTREAM_AC30 = 0x80,\r
- PESTYPE_SUBSTREAM_AC31,PESTYPE_SUBSTREAM_AC32, PESTYPE_SUBSTREAM_AC33,\r
- PESTYPE_SUBSTREAM_AC34,PESTYPE_SUBSTREAM_AC35,PESTYPE_SUBSTREAM_AC36,\r
- PESTYPE_SUBSTREAM_AC37,\r
- PESTYPE_SUBSTREAM_AC3MAX = PESTYPE_SUBSTREAM_AC37\r
- };\r
-\r
- protected:\r
- // Operations on PES packets\r
- bool submitPacket(PESPacket&);\r
- void parsePacketDetails(PESPacket&);\r
-\r
- // General demuxer objects and status indicators\r
- static Demuxer* instance;\r
- Stream videostream;\r
- Stream audiostream;\r
- DVBSubtitles* subtitles;\r
- Stream teletextstream;\r
- int shutdown();\r
- bool initted;\r
- bool vid_seeking;\r
- bool aud_seeking;\r
- bool h264;\r
- int video_current, audio_current, teletext_current, subtitle_current;\r
- int astreamtype;\r
-\r
- // Video stream information\r
- void setAspectRatio(enum AspectRatio);\r
- Callback* callback;\r
-\r
- int horizontal_size;\r
- int vertical_size;\r
- bool interlaced;\r
- int profile;\r
- enum AspectRatio aspect_ratio;\r
- int arcnt;\r
- int frame_rate;\r
- int bit_rate;\r
- ULLONG video_pts;\r
- ULLONG video_pts_seek;\r
- ULLONG audio_pts;\r
- ULLONG teletext_pts;\r
- bool isteletextdecoded;\r
-\r
-\r
- unsigned int packetnum;\r
-\r
- // Constants\r
- static const int FrameRates[9];\r
-\r
- double fps;\r
-\r
- bool ispre_1_3_19;\r
- bool avail_mpaudchan[PESTYPE_AUDMAX-PESTYPE_AUD0+1];\r
- bool avail_ac3audchan[PESTYPE_SUBSTREAM_AC3MAX-PESTYPE_SUBSTREAM_AC30+1];\r
- bool avail_dvbsubtitlechan[PESTYPE_SUBSTREAM_DVBSUBTITLEMAX-PESTYPE_SUBSTREAM_DVBSUBTITLE0+1];\r
-};\r
-\r
-#endif\r
+/*
+ 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 findSeqExtHeader(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 * dataavail=NULL);
+ bool writeVideo(bool * dataavail=NULL);
+ bool writeTeletext(bool * dataavail=NULL);
+
+ 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; }
+ bool getInterlaced() { return interlaced;}
+ 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;
+ int astreamtype;
+
+ // Video stream information
+ void setAspectRatio(enum AspectRatio);
+ Callback* callback;
+
+ int horizontal_size;
+ int vertical_size;
+ bool interlaced;
+ 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
-/*\r
- Copyright 2006 Mark Calderbank, Andreas Vogel\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#include "demuxeraudio.h"\r
-#include "audio.h"\r
-#include "i18n.h"\r
-#include "log.h"\r
-\r
-#define HDRBYTE1 0xff\r
-#define HDRBYTE2 0xe0\r
-#define HDRBYTE2MASK 0xe0\r
-\r
-\r
-\r
-class PacketBuffer {\r
- \r
- public:\r
- PacketBuffer(Stream *as,UCHAR strtype) {\r
- log=Log::getInstance();\r
- audio=as;\r
- streamtype=strtype;\r
- newStream();\r
- }\r
- //just handle the data (do not deal with headers)\r
- int putInternal(UCHAR* buf,int len,unsigned int &packetnum);\r
- void reset(){\r
- partPacket=0;\r
- bytesWritten=0;\r
- framelen=DemuxerAudio::PACKET_SIZE;\r
- }\r
- void newStream() {\r
- reset();\r
- numpackets=0;\r
- numbytes=0;\r
- skipfactor=0;\r
- numskip=0;\r
- }\r
- bool bufferFull() {\r
- return (partPacket>=framelen);\r
- }\r
- //can we write a new packet?\r
- bool bufferEmpty() {\r
- return partPacket==0;\r
- }\r
- //only set this, if buffer empty\r
- //otherwise ignored!\r
- bool setFramelen(int len) {\r
- if (! bufferEmpty() ) return false;\r
- if (len > (int)DemuxerAudio::PACKET_SIZE) return false;\r
- framelen=len;\r
- return true;\r
- }\r
- //how much bytes do we need to fill the packet?\r
- int bytesMissing() {\r
- return framelen-partPacket;\r
- }\r
- int getFramelen() {\r
- return framelen;\r
- }\r
- void setSkipFactor(int factor) {\r
- skipfactor=factor;\r
- numskip=0;\r
- }\r
- private:\r
- void packetWritten() {\r
- numbytes+=framelen;\r
- //log->log("DemuxerAudio::PacketBuffer",Log::DEBUG,"written packet %ld l=%d, bytes %ld",numpackets,framelen,numbytes);\r
- numpackets++;\r
- reset();\r
- }\r
- bool doSkip();\r
- UCHAR store[DemuxerAudio::PACKET_SIZE]; // Storage for partial packets\r
- int partPacket; // Length of partial packet stored from previous put()\r
- int bytesWritten; //if they are !=0 and != framelength the stream is full...\r
- int framelen;\r
- Log * log;\r
- Stream * audio;\r
- UCHAR streamtype;\r
- //global counters\r
- ULONG numpackets;\r
- ULONG numbytes;\r
- int skipfactor;\r
- int numskip;\r
-};\r
-\r
-\r
-DemuxerAudio::DemuxerAudio(int p_vID, int p_aID)\r
-{\r
- inSync=false;\r
- isStarting=true;\r
- log=Log::getInstance();\r
- readHeaders=0;\r
- streamtype=Audio::MP3;\r
- buffer=new PacketBuffer(&audiostream,streamtype);\r
-// buffer=new PacketBuffer(&teststream,streamtype);\r
- globalBytesWritten=0;\r
- packetnum=0;\r
- id3=NULL;\r
- info=NULL;\r
- vbr=NULL;\r
- reset();\r
-}\r
-\r
-DemuxerAudio::~DemuxerAudio() {\r
- delete buffer;\r
- if(info) delete info;\r
- if(id3) delete id3;\r
- if (vbr) delete vbr;\r
-}\r
-\r
-void DemuxerAudio::flush()\r
-{\r
- Demuxer::flushAudio();\r
- buffer->newStream();\r
- tmpFill=0;\r
-}\r
-\r
-void DemuxerAudio::reset() {\r
- buffer->newStream();\r
- tmpFill=0;\r
- readHeaders=0;\r
- packetnum=0;\r
- outOfSync=0;\r
- globalBytesWritten=0;\r
- if (id3) delete id3;\r
- id3=NULL;\r
- if (info) delete info;\r
- info=NULL;\r
- if (vbr) delete vbr;\r
- vbr=NULL;\r
- inSync=false;\r
- hasHdrInfo=false;\r
- hasVBRInfo=false;\r
- isStarting=true;\r
- hdrBitrate=128000;\r
- hdrSamplingRate=44100;\r
- avrBitrate=0;\r
- hdrFramelen=0;\r
- isStarting=true;\r
-}\r
-\r
-int DemuxerAudio::scan(UCHAR *buf, int len)\r
-{\r
- //no differend pids here\r
- return 0;\r
-}\r
-\r
-void DemuxerAudio::setVID(int p_vID)\r
-{\r
-}\r
-\r
-void DemuxerAudio::setAID(int p_aID,int type)\r
-{\r
-}\r
-\r
-static const char * id3_1_genre[] = {\r
- "Blueshhh",\r
- "Classic Rock",\r
- "Country",\r
- "Dance",\r
- "Disco",\r
- "Funk",\r
- "Grunge",\r
- "Hip-Hop",\r
- "Jazz",\r
- "Metal",\r
- "New Age",\r
- "Oldies",\r
- "Other",\r
- "Pop",\r
- "R&B",\r
- "Rap",\r
- "Reggae",\r
- "Rock",\r
- "Techno",\r
- "Industrial",\r
- "Alternative",\r
- "Ska",\r
- "Death Metal",\r
- "Pranks",\r
- "Soundtrack",\r
- "Euro-Techno",\r
- "Ambient",\r
- "Trip-Hop",\r
- "Vocal",\r
- "Jazz+Funk",\r
- "Fusion",\r
- "Trance",\r
- "Classical",\r
- "Instrumental",\r
- "Acid",\r
- "House",\r
- "Game",\r
- "Sound Clip",\r
- "Gospel",\r
- "Noise",\r
- "AlternRock",\r
- "Bass",\r
- "Soul",\r
- "Punk",\r
- "Space",\r
- "Meditative",\r
- "Instrumental Pop",\r
- "Instrumental Rock",\r
- "Ethnic",\r
- "Gothic",\r
- "Darkwave",\r
- "Techno-Industrial",\r
- "Electronic",\r
- "Pop-Folk",\r
- "Eurodance",\r
- "Dream",\r
- "Southern Rock",\r
- "Comedy",\r
- "Cult",\r
- "Gangsta",\r
- "Top 40",\r
- "Christian Rap",\r
- "Pop/Funk",\r
- "Jungle",\r
- "Native American",\r
- "Cabaret",\r
- "New Wave",\r
- "Psychadelic",\r
- "Rave",\r
- "Showtunes",\r
- "Trailer",\r
- "Lo-Fi",\r
- "Tribal",\r
- "Acid Punk",\r
- "Acid Jazz",\r
- "Polka",\r
- "Retro",\r
- "Musical",\r
- "Rock & Roll",\r
- "Hard Rock"\r
-};\r
-\r
-\r
-\r
-static int bitrateTable[16][5]={\r
-/* L1,L2,L3,2L1,2L2 */\r
-/*0000*/ {-1,-1,-1,-1,-1},\r
-/*0001*/ {32,32,32,32,8},\r
-/*0010*/ {64,48,40,48,16},\r
-/*0011*/ {96,56,48,56,24},\r
-/*0100*/ {128,64,56,64,32},\r
-/*0101*/ {160,80,64,80,40},\r
-/*0110*/ {192,96,80,96,48},\r
-/*0111*/ {224,112,96,112,56},\r
-/*1000*/ {256,128,112,128,64},\r
-/*1001*/ {288,160,128,144,80},\r
-/*1010*/ {320,192,160,160,96},\r
-/*1011*/ {352,224,192,176,112},\r
-/*1100*/ {384,256,224,192,128},\r
-/*1101*/ {416,320,256,224,144},\r
-/*1110*/ {448,384,320,256,160},\r
-/*1111*/ {-1,-1,-1,-1,-1} };\r
-\r
-static int samplingRateTable[4][3]={\r
-/*00*/ {44100,22050,11025},\r
-/*01*/ {48000,24000,12000},\r
-/*10*/ {32000,16000,8000},\r
-/*11*/ {-1,-1,-1}};\r
-\r
-//max 7 char!\r
-static const char * mpegString(UCHAR code) {\r
- switch(code) {\r
- case 0:\r
- return "MPEG2.5";\r
- case 1:\r
- return "RESERV";\r
- case 2:\r
- return "MPEG 2";\r
- case 3:\r
- return "MPEG 1";\r
- }\r
- return "UNKNOWN";\r
-}\r
-\r
-static const char * layerString(UCHAR code) {\r
- switch(code) {\r
- case 0:\r
- return "Layer reserved";\r
- case 1:\r
- return "Layer III";\r
- case 2:\r
- return "Layer II";\r
- case 3:\r
- return "Layer I";\r
- }\r
- return "Layer UNKNOWN";\r
-}\r
-/**\r
- * parse an id3 Header\r
- * provided by Brian Walton\r
- * @returns -1 of nothing found\r
- */\r
- \r
-int DemuxerAudio::id3_2_3_FrameParse(unsigned char buf[], id3_frame *frame)\r
-{\r
- if (buf[0] < 0x20 || buf[1] < 0x20 || buf [2] < 0x20 ) return -1;\r
- frame->size = (buf[4] & 0x7F) << 21 | (buf[5] & 0x7F) << 14 | (buf[6] & 0x7F) << 7 | (buf[7] & 0x7F);\r
- if (frame->size == 0) return -1;\r
- //TODO. clearify flags against:\r
- //http://id3.org/id3v2.3.0#head-697d09c50ed7fa96fb66c6b0a9d93585e2652b0b\r
- frame->flags.tagAlterPreserv = (buf[8] & 0x80) >> 7;\r
- frame->flags.filelterPreserv = (buf[8] & 0x40) >> 6;\r
- frame->flags.readOnly = (buf[8] & 0x20) >> 5;\r
- frame->flags.groupId = (buf[9] & 0x20) >> 5;\r
- frame->flags.compression = (buf[9] & 0x80) >> 7;\r
- frame->flags.encryption = (buf[9] & 0x40) >> 6;\r
- frame->flags.unsync = 0;\r
- frame->flags.dataLen = 0;\r
- return 0;\r
-}\r
-\r
- /**\r
- * parse an id3 Header\r
- * provided by Brian Walton\r
- * @returns -1 of nothing found\r
- */\r
- \r
-int DemuxerAudio::id3_2_2_FrameParse(unsigned char buf[], id3_frame *frame)\r
-{\r
- if (buf[0] < 0x20 || buf[1] < 0x20 || buf[2] < 0x20) return -1;\r
- frame->size = (buf[3] & 0x7F) << 14 | (buf[4] & 0x7F) << 7 | (buf[5] & 0x7F);\r
- if (frame->size == 0) return -1;\r
- return 0;\r
-}\r
-\r
-\r
-//fill an id3tag from a frame payload\r
-//http://id3.org/id3v2.3.0#head-697d09c50ed7fa96fb66c6b0a9d93585e2652b0b\r
-//http://id3.org/id3v2-00\r
-static struct tagid {\r
- const char * bytes;\r
- int index;\r
-} knownFrames[]= {\r
- //ID3V2.3\r
- {"TIT2",1}, //title\r
- {"TPE1",2}, //artist\r
- {"TCON",3}, //genre\r
- {"TRCK",6}, //track\r
- {"TYER",4}, //year\r
- {"TALB",5}, //album\r
- {"TCOM",7}, //composer\r
- {"COMM",8}, //comment\r
- //Text encoding $xx\r
- //Language $xx xx xx\r
- //Short content descrip. <text string according to encoding> $00 (00)\r
- //The actual text <full text string according to encoding>\r
- //ID3V2.0\r
- {"TT2",1 },\r
- {"TP1",2 },\r
- {"TCM",7 },\r
- {"TCO",3 }, //(genreNumber)\r
- {"TAL",5 },\r
- {"TRK",6 },\r
- {"TYE",4 },\r
- {"COM",8 }\r
-};\r
-#define NUMKNOWN (sizeof(knownFrames)/sizeof(knownFrames[0]))\r
-\r
-/*fill in infos\r
- from an ID3 V2.x, V2.3 frame into the tags structure\r
- frameData must point to the header\r
- framelen is the len without header (10 Bytes for V23, 6 Bytes for v2x)\r
- */\r
-\r
-#define MAXLEN(tagtype) ((UINT)frameLen<sizeof(tag->tagtype)-1?(UINT)frameLen:sizeof(tag->tagtype)-1)\r
-bool DemuxerAudio::fillId3Tag(id3_tag * tag,UCHAR * frameData, int frameLen, int dataOffset, bool v23) {\r
- int tl=v23?4:3;\r
- int tagIndex=-1;\r
- if (tag == NULL) return false;\r
- if (frameLen < 2) return false;\r
- for (UINT i=0;i< NUMKNOWN;i++) {\r
- if(strncmp((char *)frameData,knownFrames[i].bytes,tl) == 0) {\r
- tagIndex=knownFrames[i].index;\r
- break;\r
- }\r
- }\r
- if (tagIndex < 0) return false;\r
- UCHAR encoding=*(frameData+dataOffset);\r
- dataOffset++;\r
- frameLen--;\r
- if (encoding != 0) {\r
- log->log("DemuxerAudio",Log::DEBUG,"unknown encoding for tag %d, tagid %s",encoding,\r
- knownFrames[tagIndex].bytes);\r
- return false;\r
- }\r
- switch(tagIndex) {\r
- case 1: //title\r
- strncpy(tag->title,(char*)(frameData+dataOffset),MAXLEN(title));\r
- tag->title[MAXLEN(title)]=0;\r
- break;\r
- case 2: //artist\r
- strncpy(tag->artist,(char*)(frameData+dataOffset),MAXLEN(artist));\r
- tag->artist[MAXLEN(artist)]=0;\r
- break;\r
- case 3: //genre\r
- {\r
- UCHAR * st=frameData+dataOffset;\r
- int genre=0;\r
- if (*st=='(') {\r
- genre=atoi((const char *)(st+1)) && 31;\r
- st=(UCHAR *)id3_1_genre[genre];\r
- }\r
- strncpy(tag->genre,(char*)st,MAXLEN(genre));\r
- tag->genre[MAXLEN(genre)]=0;\r
- break;\r
- }\r
- case 4: //year\r
- strncpy(tag->year,(char *)(frameData+dataOffset),MAXLEN(year));\r
- tag->year[MAXLEN(year)]=0;\r
- break;\r
- case 5: //album\r
- strncpy(tag->album,(char *)(frameData+dataOffset),MAXLEN(album));\r
- tag->album[MAXLEN(album)]=0;\r
- break;\r
- case 6: //track\r
- strncpy(tag->track,(char *)(frameData+dataOffset),MAXLEN(track));\r
- tag->track[MAXLEN(track)]=0;\r
- break;\r
- case 7: //composer\r
- strncpy(tag->composer,(char *)(frameData+dataOffset),MAXLEN(composer));\r
- tag->composer[MAXLEN(composer)]=0;\r
- break;\r
- case 8: //comment\r
- strncpy(tag->comment,(char *)(frameData+dataOffset),MAXLEN(comment));\r
- tag->comment[MAXLEN(comment)]=0;\r
- break;\r
- default:\r
- return false;\r
- }\r
-\r
- return true;\r
-}\r
-\r
-/**\r
- * parse an id3 Header\r
- * based on code provided by Brian Walton\r
- * @returns -1 of nothing found\r
- * otherwise the id3 info is filled\r
- */\r
-\r
-int DemuxerAudio::parseID3V2(UCHAR *data, int len) {\r
- int debug=0;\r
- UCHAR * start=data;\r
- id3_header id3header;\r
- id3_frame id3frame;\r
- id3_tag * id3tag=NULL;\r
- //len = read(fd, data, 10);\r
- if (len < 10) {\r
- delete id3tag;\r
- return -1;\r
- }\r
- len-=10;\r
- if(data[0]=='I' && data[1]=='D' && data[2]=='3')\r
- {\r
- id3tag=new id3_tag();\r
- id3header.major = data[3];\r
- id3header.minor = data[4];\r
- if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"ID3 V2.%d.%d found\n", id3header.major, id3header.minor);\r
- id3header.flags.unsynchronisation = (data[5] & 0x80)>>7;\r
- id3header.flags.extended_header = (data[5] & 0x40)>>6;\r
- id3header.flags.experimental = (data[5] & 0x20)>>5;\r
- id3header.flags.footer = (data[5] & 0x10)>>4;\r
- if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Unsynchronisation flag: %d\n", id3header.flags.unsynchronisation);\r
- if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Extended header flag: %d\n", id3header.flags.extended_header);\r
- if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Experimental indicator flag: %d\n", id3header.flags.experimental);\r
- if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Footer present flag: %d\n", id3header.flags.footer);\r
- id3header.size = (data[6] & 0x7F) << 21 | (data[7] & 0x7F) << 14 | (data[8] & 0x7F) << 7 | (data[9] & 0x7F);\r
- if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"ID3 Size: %d\n", id3header.size);\r
- data=start+10;\r
- if (len <= id3header.size) {\r
- if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"header size to big %d, only %d bytes available\n",id3header.size,len);\r
- delete id3tag;\r
- return -1;\r
- }\r
- if (id3header.flags.extended_header)\r
- {\r
- int extended_hdr_hdr=4; //still to be discussed (id3.org...)\r
- //read extended header size\r
- if (len < extended_hdr_hdr) {\r
- if (debug) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"extended header found but cannot read\n");\r
- delete id3tag;\r
- return -1;\r
- }\r
- if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"remaining %d chars after extended hdr hdr\n", len);\r
- id3header.extended_header.size = (data[0] & 0x7F) << 21 | (data[1] & 0x7F) << 14 | (data[2] & 0x7F) << 7 | (data[3] & 0x7F);\r
- if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Extended header size: %d\n", id3header.extended_header.size);\r
- if (len <= id3header.extended_header.size+extended_hdr_hdr) {\r
- if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"extended Header to big, only %d bytes available\n",len);\r
- delete id3tag;\r
- return -1;\r
- }\r
- //lseek(fd, id3header.extended_header.size - 6, SEEK_CUR);\r
- data+=id3header.extended_header.size+extended_hdr_hdr;\r
- len-=id3header.extended_header.size+extended_hdr_hdr;\r
- }\r
- //set the end of the header\r
- UCHAR * eob=start+id3header.size+10;\r
- bool readNext=true;\r
- while (data < eob && readNext)\r
- {\r
- //skip over some padding - found this in lame MCDI tag...\r
- if (*data == 0) {\r
- data++;\r
- continue;\r
- }\r
- readNext=false;\r
- switch(id3header.major)\r
- {\r
- case 2: //ID3 V2.2.x\r
- 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));\r
- if (data + 6 >= eob)\r
- {\r
- break;\r
- }\r
- if (id3_2_2_FrameParse(data, &id3frame) < 0)\r
- {\r
- break;\r
- }\r
- if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"frame size: %d\n", id3frame.size);\r
- fillId3Tag(id3tag,data,id3frame.size,6,false);\r
- if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"frame payload: %s\n", data + 6 +1);\r
- data+=6+id3frame.size;\r
- readNext=true;\r
- break;\r
- case 3: //ID3 V2.3.x\r
- {\r
- if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"version 2.3 frame, %d : %c %c %c %c\n", data-start,\r
- *data,*(data+1),*(data+2),*(data+3));\r
- if (data + 10 >= eob)\r
- {\r
- break;\r
- }\r
- if (id3_2_3_FrameParse(data, &id3frame) <0)\r
- {\r
- break;\r
- }\r
- if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Frame size: %d\n", id3frame.size);\r
- int dataOffset=10;\r
- if (id3frame.flags.groupId)\r
- {\r
- dataOffset++;\r
- if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Frame group: %d\n", data[dataOffset]);\r
- }\r
- if (id3frame.flags.compression)\r
- {\r
- if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Frame compressed: %d\n", id3frame.flags.compression);\r
- }\r
- if (id3frame.flags.encryption)\r
- {\r
- dataOffset+=1;\r
- if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Frame encryption method: %d\n", data[dataOffset]);\r
- }\r
- fillId3Tag(id3tag,data,id3frame.size-dataOffset+10,dataOffset,true);\r
- if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"frame payload: %s\n", data + dataOffset +1);\r
- data+=10+id3frame.size;\r
- readNext=true;\r
- break;\r
- }\r
- default:\r
- //don't support this version\r
- delete id3tag;\r
- return -1;\r
- }\r
- }\r
-\r
- data=eob;\r
- //store the found tag\r
- if (id3) delete id3;\r
- id3=id3tag;\r
- return data-start;\r
- }\r
- return -1;\r
-}\r
-\r
-/**\r
- * parse an id3v1 Header\r
- * based on code provided by Brian Walton\r
- * @returns -1 of nothing found\r
- * otherwise the id3 info is filled\r
- */\r
-#define MEMCPY(type,len,offset) {int type##max=sizeof(tag->type)-1<len?sizeof(tag->type)-1:len;strncpy(tag->type,(char*)&data[offset],type##max);tag->type[type##max]=0;}\r
-\r
-int DemuxerAudio::parseID3V1(UCHAR *data, int len) {\r
- int debug=1;\r
- if (len < 128) return -1;\r
- if(data[0]=='T' && data[1]=='A' && data[2]=='G')\r
- {\r
- id3_tag * tag=new id3_tag();\r
- if (debug != 0)log->log("DemuxerAudio::parseID3V1",Log::DEBUG,"ID3 V1 tag found\n");\r
- MEMCPY(title,30,3);\r
- MEMCPY(artist,30,33);\r
- MEMCPY(album,30,63);\r
- MEMCPY(year,4,93);\r
- if (data[125]==0 && data[126]!=0)\r
- { //ID3 V1.1\r
- if (debug != 0)log->log("DemuxerAudio::parseID3V1",Log::DEBUG,"ID3 V1.1 tag\n");\r
- MEMCPY(comment,29,97);\r
- sprintf(tag->track, "%d", data[126]);\r
- } else {\r
- if (debug != 0)log->log("DemuxerAudio::parseID3V1",Log::DEBUG,"ID3 V1.0 tag\n");\r
- MEMCPY(comment,30,97);\r
- }\r
- if (data[127] < sizeof(id3_1_genre)/sizeof(id3_1_genre[0]))\r
- {\r
- sprintf(tag->genre, id3_1_genre[data[127]]);\r
- }\r
- if (id3) delete id3;\r
- id3=tag;\r
- return 0;\r
- }\r
- return -1;\r
-}\r
-\r
-//infos from http://www.multiweb.cz/twoinches/MP3inside.htm\r
-int DemuxerAudio::parseVBR(UCHAR *data, int len) {\r
- UCHAR *hdr=findHeader(data,len);\r
- //we expect the header exactly here\r
- if (hdr != data) return -1;\r
- const static char * VBRHDR="Xing";\r
- int vbridpos=36;\r
- UCHAR mpgtype=(data[1] & 0x18)>>3;\r
- UCHAR chmode=(data[2] & 0xc0) >> 6;\r
- UCHAR layer=(data[2] & 0x06) >>1;\r
- if ( mpgtype == 3 && chmode == 11) vbridpos=21;\r
- if ( mpgtype != 3 && chmode != 11) vbridpos=21;\r
- if ( mpgtype != 3 && chmode == 11) vbridpos=13;\r
- //check for the header ID\r
- if (vbridpos+(int)strlen(VBRHDR)+4 >= len) {\r
- Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"frame to short for VBR header %d",len);\r
- return -1;\r
- }\r
- for (int i=4;i<vbridpos;i++) {\r
- if (data[i] != 0) {\r
- Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"garbage when searching VBR header at pos %d",i);\r
- return -1;\r
- }\r
- }\r
- if ( strncmp((char *)&data[vbridpos],VBRHDR,strlen(VBRHDR)) != 0) {\r
- Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"no VBR header at pos %d",vbridpos);\r
- return -1;\r
- }\r
- int framedata=vbridpos+strlen(VBRHDR);\r
- int expectedLen=0;\r
- //OK we should now have a valid vbr header\r
- bool hasFramenum=data[framedata+3] & 1;\r
- bool hasBytes=data[framedata+3] & 0x2;\r
- bool hasTOC=data[framedata+3] & 0x4;\r
- expectedLen+=8;\r
- if (hasTOC) expectedLen+=100;\r
- if (framedata+expectedLen > len) {\r
- Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"frame to short for VBR header data %d, expected %d",\r
- len,framedata+expectedLen);\r
- return -1;\r
- }\r
- if (!hasFramenum || ! hasBytes || ! hasTOC) {\r
- //not usefull for us..\r
- Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"not all parts in VBR header - ignore");\r
- return -1;\r
- }\r
- framedata+=4;\r
- if (vbr) delete vbr;\r
- vbr=new vbrInfo();\r
- vbr->numFrames=data[framedata] << 24 | data[framedata+1]<<16|\r
- data[framedata+2]<<8 |data[framedata+3];\r
- framedata+=4;\r
- vbr->numBytes=data[framedata] << 24 | data[framedata+1]<<16|\r
- data[framedata+2]<<8 |data[framedata+3];\r
- framedata+=4;\r
- for (int ti=0;ti<100;ti++) {\r
- vbr->table[ti]=data[framedata+ti];\r
- }\r
- //compute file size in seconds\r
- //should be (#of frames -1) *samplesPerFrame / sampleRate\r
- //TODO: difference for Mono?\r
- ULONG samplesPerFrame=384; //layer1\r
- if (layer != 3) samplesPerFrame=1152;\r
- vbr->fileSeconds=(vbr->numFrames-1)*samplesPerFrame/hdrSamplingRate;\r
- Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"successfully read VBR %ldbytes, %ld frames, %ldsec",\r
- vbr->numBytes,vbr->numFrames,vbr->fileSeconds);\r
- return hdrFramelen;\r
-}\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-UCHAR * DemuxerAudio::findHeader(UCHAR *buf, int len, bool writeInfo) {\r
- while (len >= 3) //assume hdr+crc\r
- {\r
- UCHAR pattern=*buf;\r
- buf++; len--;\r
- if (pattern != HDRBYTE1 ) continue;\r
- if ((*buf & HDRBYTE2MASK) != HDRBYTE2) continue;\r
- if (readHeader((buf-1),4,writeInfo) != 0) continue;\r
- return buf-1;\r
- }\r
- return NULL;\r
-}\r
-\r
-\r
-\r
-int DemuxerAudio::readHeader(UCHAR * hbuf,int len,bool writeInfo) {\r
- int curFramelen=0;\r
- int curBitrate=0;\r
- int curSamplingRate=0;\r
- if (*hbuf != HDRBYTE1) return -1;\r
- hbuf++;\r
- if ((*hbuf & HDRBYTE2MASK) != HDRBYTE2) return -1;\r
- UCHAR mpgtype=(*hbuf & 0x18)>>3;\r
- if (mpgtype == 1) {\r
- log->log("DemuxerAudio",Log::DEBUG,"header invalid mpgtype %s %i %i %i",\r
- mpegString(mpgtype),*hbuf,*(hbuf+1),*(hbuf+2));\r
- return 1;\r
- }\r
- UCHAR layer=(*hbuf & 0x06) >>1;\r
- //bool hasCRC=!(*hbuf & 1);\r
- hbuf++;\r
- UCHAR bitrateCode=(*hbuf & 0xf0) >>4;\r
- UCHAR samplingCode=(*hbuf & 0x0c) >> 2;\r
- bool padding=*hbuf & 0x02;\r
- hbuf++;\r
- //0 Stereo, 1 JointStereo, 2 Dual, 3 Mono\r
- UCHAR chmode=(*hbuf & 0xc0) >> 6;\r
- //UCHAR extension=(*hbuf & 0x30) >> 4;\r
-\r
- //layercode: 1-L3, 2-L2, 3-L1\r
- //columns 0,1,2 for MPEG1\r
- UCHAR bitrateColumn=3-layer;\r
- if (bitrateColumn > 2) {\r
- log->log("DemuxerAudio",Log::DEBUG,"header invalid layer %s %i %i %i",\r
- layerString(layer),*(hbuf-2),*(hbuf-1),*hbuf);\r
- return 1;\r
- }\r
- if (mpgtype != 3) bitrateColumn+=3;\r
- if (bitrateColumn>4) bitrateColumn=4;\r
- curBitrate=1000*bitrateTable[bitrateCode][bitrateColumn];\r
- UCHAR sampleRateColumn=0;\r
- if (mpgtype == 10) sampleRateColumn=1;\r
- if (mpgtype == 0) sampleRateColumn=2;\r
- curSamplingRate=samplingRateTable[samplingCode][sampleRateColumn];\r
- if (curSamplingRate < 0 || curBitrate < 0) {\r
- log->log("DemuxerAudio",Log::DEBUG,"header invalid rates br=%d sr=%d %i %i %i",\r
- curBitrate,curSamplingRate,*(hbuf-2),*(hbuf-1),*hbuf);\r
- return 1;\r
- }\r
- int padbytes=0;\r
- if (padding) {\r
- if (layer == 3) padbytes=4;\r
- else padbytes=1;\r
- }\r
- if (layer == 3) {\r
- //Layer 1\r
- //FrameLengthInBytes = (12 * BitRate / SampleRate + Padding) * 4\r
- curFramelen=(12*curBitrate/curSamplingRate+padbytes) * 4;\r
- }\r
- else {\r
- //Layer 2/3\r
- //FrameLengthInBytes = 144 * BitRate / SampleRate + Padding\r
- curFramelen=144*curBitrate/curSamplingRate+padbytes;\r
- }\r
- //the header itself\r
- if (curFramelen < 32) {\r
- log->log("DemuxerAudio",Log::DEBUG,"read header %ld mpgv=%s lc=%s br=%d sr=%d, fl=%d-invalid %i %i %i",\r
- readHeaders,mpegString(mpgtype),layerString(layer),\r
- curBitrate,curSamplingRate,curFramelen,*(hbuf-2),*(hbuf-1),*hbuf);\r
- return 1;\r
- }\r
- if (writeInfo || isStarting){\r
- log->log("DemuxerAudio",Log::DEBUG,"read header %ld mpgv=%s lc=%s br=%d sr=%d, fl=%d %i %i %i",\r
- readHeaders,mpegString(mpgtype),layerString(layer),\r
- curBitrate,curSamplingRate,curFramelen,*(hbuf-2),*(hbuf-1),*hbuf);\r
- if (info) delete info;\r
- info=new mpegInfo();\r
- strcpy(info->mpegVersion,mpegString(mpgtype));\r
- info->mpegLayer=4-layer;\r
- info->bitRate=curBitrate;\r
- info->avrBitrate=curBitrate;\r
- info->sampleRate=curSamplingRate;\r
- const char *chmodStr=tr("Stereo");\r
- switch (chmode) {\r
- case 1: chmodStr=tr("JointStereo");break;\r
- case 2: chmodStr=tr("Dual");break;\r
- case 3: chmodStr=tr("Mono");break;\r
- }\r
- SNPRINTF(info->info,sizeof(info->info)-1,"%s",chmodStr);\r
- }\r
- if (isStarting) avrBitrate=curBitrate;\r
- isStarting=false;\r
- readHeaders++;\r
- //moving average F=0.005\r
- avrBitrate=avrBitrate+((5*(curBitrate-avrBitrate))/1024);\r
- hdrBitrate=curBitrate;\r
- hdrFramelen=curFramelen;\r
- hdrSamplingRate=curSamplingRate;\r
- hasHdrInfo=true;\r
- return 0;\r
-}\r
-\r
-int DemuxerAudio::findPTS(UCHAR* buf, int len, ULLONG* dest)\r
-{\r
- //we have no PTS number ...\r
- *dest=0;\r
- return (findHeader(buf,len) != NULL)?1:0;\r
-}\r
-\r
-bool PacketBuffer::doSkip() {\r
- if (!bufferFull()) return false;\r
- if (skipfactor == 0) return false;\r
- numskip++;\r
- if (numskip >= skipfactor) {\r
- //sent at least always 2 packets\r
- if (numskip > skipfactor) numskip=0;\r
- return false;\r
- }\r
- packetWritten();\r
- return true;\r
-}\r
- \r
-// just handle the real stream without dealing with the header\r
-int PacketBuffer::putInternal(UCHAR * wbuf, int len,unsigned int &packetnum)\r
-{\r
- /* Important, the type passed to stream must be a mediapacket type as defined in \r
- Draintarget.h and not the device setting of the mvp, so we have to translate it here,\r
- in order to get it working on windows\r
- --MR */\r
- UCHAR mptype=0;\r
- switch (streamtype) {\r
- case Audio::MPEG1_PES: //?\r
- case Audio::MPEG2_PES: //Important, this must be a PES !\r
- mptype=MPTYPE_MPEG_AUDIO; break;\r
- default:\r
- case Audio::MP3: //this can be any Mpeg1 Audio not only layer 3 not packed into PES\r
- mptype=MPTYPE_MPEG_AUDIO_LAYER3;break;\r
- };\r
- if (bufferFull()) {\r
-#ifndef WIN32\r
- if (doSkip()) return 0;//NoSkip on Windows\r
-#endif\r
- //we are still full - so try to write\r
- int sent=audio->put(store+bytesWritten,framelen-bytesWritten,/*streamtype*/mptype,packetnum);packetnum++;\r
- //log->log("DemuxerAudio::PacketBuffer",Log::DEBUG,"written %d bytes to stream (still full) pp=%d, framelen=%d, written=%d",sent,partPacket,framelen, bytesWritten );\r
- if (sent < (framelen - bytesWritten)) {\r
- //packet still not written\r
- bytesWritten+=sent;\r
- return 0;\r
- }\r
- packetWritten();\r
- //let the demuxer come back with the rest - need to check header first\r
- return 0;\r
- }\r
- if (partPacket+len >= framelen) {\r
- //now we have at least a complete packet\r
- int bytesConsumed=framelen-partPacket;\r
- memcpy(store+partPacket,wbuf,bytesConsumed);\r
- partPacket=framelen;\r
- //log->log("DemuxerAudio::PacketBuffer",Log::DEBUG,"stored packet %ld, length %d (last %d) for stream %p",numpackets,framelen,bytesConsumed,audio );\r
-#ifndef WIN32 //No Skip on Windows\r
- if (doSkip()) return bytesConsumed;\r
-#endif\r
- int sent=audio->put(store,framelen,mptype,packetnum);packetnum++;\r
- bytesWritten+=sent;\r
- //log->log("DemuxerAudio::PacketBuffer",Log::DEBUG,"written %d bytes to stream",sent );\r
- if (bytesWritten < framelen) {\r
- //still not completely written\r
- return bytesConsumed;\r
- }\r
- packetWritten();\r
- //let the player come back...\r
- return bytesConsumed;\r
- }\r
- //OK packet still not complete\r
- if (len == 0) return 0;\r
- int bytesConsumed=len;\r
- memcpy(store+partPacket,wbuf,bytesConsumed);\r
- partPacket+=bytesConsumed;\r
- return bytesConsumed;\r
-}\r
-\r
-/**\r
- major entry for data from a player\r
- the demuxer is either in the state headerSearch (packet written or\r
- just at the beginning), writing garbage (inSync=false) or\r
- handling data (none set)\r
- A header is expected at the first byte after the previous packet -\r
- otherwise we switch to garbage where we always search for a header\r
- (but anyway provide the data to the underlying device - it's probably\r
- more intelligent then we are...\r
- We only loose a correct position display.\r
- */\r
-int DemuxerAudio::put(UCHAR* wbuf, int len)\r
-{\r
- //return audiostream.put(wbuf,len,streamtype);\r
- int framelen=PACKET_SIZE;\r
- UCHAR *hdr;\r
- int bytesConsumed=0;\r
- int oldBytes=0;\r
- if (tmpFill != 0 || (buffer->bufferEmpty() && len < HDRLEN)) {\r
- //OK we have to copy everything to the tmp buffer\r
- int cp=(UINT)len<(PACKET_SIZE-tmpFill)?(UINT)len:(PACKET_SIZE-tmpFill);\r
- memcpy(&tmpBuffer[tmpFill],wbuf,cp);\r
- oldBytes=tmpFill;\r
- tmpFill+=cp;\r
- wbuf=tmpBuffer;\r
- len=tmpFill;\r
- //if there is no header here and our buffer\r
- //is empty - just wait for the next header\r
- if (len < HDRLEN && buffer->bufferEmpty()) {\r
- log->log("DemuxerAudio",Log::DEBUG,"len to small for header %d at bytes %ld",len,globalBytesWritten);\r
- return cp;\r
- }\r
- }\r
- while (bytesConsumed < len ) {\r
- if (buffer->bufferFull()) {\r
- //if this is the first part of the loop, try to write to the stream\r
- if (bytesConsumed == 0) buffer->putInternal(wbuf,0,packetnum);\r
- //if the buffer is full, no need to continue\r
- if (buffer->bufferFull()) break;\r
- }\r
- //either we are in a packet (buffer != full && buffer != empty)\r
- //or we are searching a header\r
- if (buffer->bufferEmpty()) {\r
- if (len-bytesConsumed < HDRLEN) {\r
- // we cannot still search\r
- if (tmpFill != 0) {\r
- //we are already working at the buffer\r
- break;\r
- }\r
- memcpy(tmpBuffer,wbuf,len-bytesConsumed);\r
- tmpFill=len-bytesConsumed;\r
- log->log("DemuxerAudio",Log::DEBUG,"len to small for header %d at bytes %ld",len,globalBytesWritten);\r
- return len;\r
- }\r
-\r
- int lastFramelen=hdrFramelen;\r
- //if the header has been valid before and we are searching\r
- //it should be here\r
- int maxsearch=((len-bytesConsumed) < (int)PACKET_SIZE)?len-bytesConsumed:(int)PACKET_SIZE;\r
- hdr=findHeader(wbuf,maxsearch);\r
- if (hdr != NULL) {\r
- //log->log("DemuxerAudio",Log::DEBUG,"header found at offset %d",hdr-wbuf);\r
- }\r
- //hdr now points to the new header\r
- if (hdr != wbuf) {\r
- //still at least bytes before the header\r
- inSync=false;\r
- outOfSync++;\r
- int garbageBytes=(hdr!=NULL)?(hdr-wbuf):maxsearch;\r
- //we consider the garbage now as an own packet\r
- //we can consume at most our buffer\r
- //basically the buffer should be empty here (we are\r
- //in header search - and we fill up each garbage\r
-\r
- int nframesize=garbageBytes;\r
- if (nframesize > (int)PACKET_SIZE) {\r
- nframesize=(int)PACKET_SIZE;\r
- }\r
- if (! buffer->bufferEmpty()) {\r
- log->log("DemuxerAudio",Log::WARN,"buffer not empty when having garbage to store");\r
- //at the end no big problem, we only write the remaining bytes that we have...\r
- }\r
- else {\r
- buffer->setFramelen(nframesize);\r
- }\r
- log->log("DemuxerAudio",Log::DEBUG,"garbage found at packet %ld (bytes %ld) of length %d, "\r
- "framelen set to %d (last fl=%d)",\r
- readHeaders,globalBytesWritten,garbageBytes,buffer->getFramelen(),lastFramelen);\r
-#ifndef WIN32 \r
- //hmm - we assume that he low level driver is more intelligent\r
- //and give him the data "as is"\r
- int written=buffer->putInternal(wbuf,garbageBytes,packetnum);\r
- globalBytesWritten+=written;\r
- bytesConsumed+=written;\r
- if (written != garbageBytes || hdr == NULL ) {\r
- break;\r
- }\r
-#else //DS is not intelligent\r
- globalBytesWritten+=garbageBytes;\r
- bytesConsumed+=garbageBytes;\r
-#endif\r
- //OK either all the "garbage" is written or\r
- //we found the next header as expected\r
- //continue with the next package\r
- wbuf=hdr;\r
- }\r
- //we have to wait now until the buffer is \r
- //free for the next package\r
- if ( ! buffer->bufferEmpty()) return bytesConsumed;\r
- //this is the place where we start a new packet\r
- framelen=hdrFramelen;\r
- if ( !buffer->setFramelen(framelen) ) {\r
- log->log("DemuxerAudio",Log::DEBUG,"unable to set framelen should=%d, current=%d",\r
- framelen,buffer->getFramelen());\r
- }\r
- inSync=true;\r
- }\r
- //now we are surely within a packet\r
- int written=buffer->putInternal(wbuf,len-bytesConsumed,packetnum);\r
- //update the status\r
- globalBytesWritten+=written;\r
- wbuf+=written;\r
- bytesConsumed+=written;\r
- if (written == 0) {\r
- //stream is full\r
- break;\r
- }\r
- //OK - handle the rest\r
- }\r
- if (bytesConsumed >= oldBytes) {\r
- //if we consumed more than the old bytes - OK the buffer\r
- //can be thrown away\r
- tmpFill=0;\r
- return bytesConsumed-oldBytes;\r
- }\r
- else {\r
- //only consumed bytes from the buffer\r
- if (bytesConsumed == 0) {\r
- //restore the buffer\r
- tmpFill=oldBytes;\r
- return 0;\r
- }\r
- //shift bytes in buffer\r
- for (int i=0;i<oldBytes-bytesConsumed;i++) {\r
- tmpBuffer[i]=tmpBuffer[i+bytesConsumed];\r
- }\r
- tmpFill=oldBytes-bytesConsumed;\r
- return 0;\r
- }\r
-}\r
-\r
-//info functions\r
-const id3_tag * DemuxerAudio::getId3Tag() {\r
- return id3;\r
-\r
-}\r
-const DemuxerAudio::mpegInfo * DemuxerAudio::getMpegInfo() {\r
- if (! hasHdrInfo) return NULL;\r
- info->avrBitrate=avrBitrate;\r
- info->bitRate=hdrBitrate;\r
- return info;\r
-}\r
-\r
-const DemuxerAudio::vbrInfo * DemuxerAudio::getVBRINfo() {\r
- return vbr;\r
-}\r
-\r
-int DemuxerAudio::checkStart(UCHAR *b, int len) {\r
- UCHAR *start=b;\r
- int id3len=parseID3V2(b,len);\r
- if (id3len > 0) {\r
- char * str=id3->toString();\r
- log->log("DemuxerAudio",Log::DEBUG,"parseID3V2 %d bytes of %d",id3len,len);\r
- log->log("DemuxerAudio",Log::DEBUG,"parseID3V2 found:%s",str);\r
- delete str;\r
- b+=id3len;\r
- len-=id3len;\r
- }\r
- int vbrlen=parseVBR(b,len);\r
- if (vbrlen > 0 ) {\r
- hasVBRInfo=true;\r
- b+=vbrlen;\r
- len-=vbrlen;\r
- }\r
- UCHAR * hdr=findHeader(b,len,true);\r
- if (hdr == NULL) return -1;\r
- return hdr-start;\r
-}\r
-\r
-int DemuxerAudio::checkID3(UCHAR *b, int len) {\r
- if (len != 128) return -1;\r
- int rt=parseID3V1(b,len);\r
- if (rt >= 0) {\r
- char * str=id3->toString();\r
- log->log("DemuxerAudio",Log::DEBUG,"parseID3V1 found:%s",str);\r
- delete str;\r
- }\r
- return rt;\r
-}\r
-\r
-bool DemuxerAudio::isSync() {\r
- return inSync;\r
-}\r
-\r
-UINT DemuxerAudio::getSyncErrors() {\r
- return outOfSync;\r
-}\r
-\r
-ULONG DemuxerAudio::getBytesPerSecond()\r
-{\r
- ULONG bps=hdrBitrate;\r
- if (! hasHdrInfo) return 0;\r
- if (hdrBitrate != avrBitrate) {\r
- //we seem to have vbr\r
- if (hasVBRInfo) {\r
- //vbr stuff TODO\r
- bps=avrBitrate;\r
- }\r
- else {\r
- //some guessing\r
- bps=avrBitrate;\r
- }\r
- }\r
- bps=bps/8;\r
- return bps;\r
-}\r
-\r
-ULONG DemuxerAudio::getSecondsFromLen(ULONG len) {\r
- if (! hasHdrInfo) return 0;\r
- if (vbr) {\r
- //first find the index where we are between\r
- //rough starting point:\r
- ULONG idx=100*len/vbr->numBytes;\r
- if (idx >= 100) idx=99;\r
- ULONG idxPos=(vbr->table[idx]) * vbr->numBytes/256;\r
- ULONG pbefore=idxPos;\r
- ULONG pafter=idxPos;\r
- //OK now we know whether we have to go up or down\r
- if (idxPos > len) {\r
- //down\r
- while (idxPos > len && idx > 0) {\r
- pafter=idxPos;\r
- idx--;\r
- idxPos=(vbr->table[idx]) * vbr->numBytes/256;\r
- pbefore=idxPos;\r
- }\r
- //OK we are now before our postion\r
- }\r
- else {\r
- //up\r
- while (idxPos < len && idx < 100 ) {\r
- pbefore=idxPos;\r
- idx++;\r
- idxPos=(vbr->table[idx]) * vbr->numBytes/256;\r
- pafter=idxPos;\r
- }\r
- //after our position\r
- if (idx > 0) idx --;\r
- }\r
- //idx is now the index before our position\r
- //approximate between the 2 points\r
- ULONG idxTime=idx * vbr->fileSeconds/100;\r
- if (pafter == pbefore) return idxTime;\r
- ULONG rt=idxTime+ (len-pbefore)* vbr->fileSeconds/(100 * (pafter-pbefore)) ;\r
- if (rt > vbr -> fileSeconds) return vbr->fileSeconds;\r
- if (rt < idxTime) return idxTime;\r
- return rt;\r
- }\r
- else {\r
- ULONG bps=getBytesPerSecond();\r
- if (bps == 0) return 0;\r
- return len/bps;\r
- }\r
-}\r
- \r
-ULONG DemuxerAudio::positionFromSeconds(ULONG seconds) {\r
- if (! hasHdrInfo) return 0;\r
- if (vbr) {\r
- ULONG idx=seconds*100/vbr->fileSeconds;\r
- if (idx > 99) idx=99;\r
- ULONG idxPos=vbr->table[idx] * vbr->numBytes/256;\r
- ULONG idx2=idx;\r
- if (idx < 99) idx2++;\r
- ULONG nextPos=vbr->table[idx] * vbr->numBytes/256;\r
- ULONG rt=idxPos+(nextPos-idxPos) * (seconds - idx * vbr->fileSeconds /256);\r
- if (rt < idxPos) return idxPos;\r
- if ( rt > vbr->numBytes) return vbr->numBytes;\r
- return rt;\r
- }\r
- else {\r
- ULONG bps=getBytesPerSecond();\r
- return bps*seconds;\r
- }\r
-}\r
-\r
-int id3_tag::stringlen(bool withTitle) const {\r
- if (!withTitle)\r
- return strlen(artist)+strlen(genre)+strlen(year)+strlen(album)+\r
- strlen(track)+strlen(composer)+strlen(comment)+8+3+\r
- 90; //30 chars for each name...\r
- else\r
- return strlen(title)+strlen(artist)+strlen(genre)+strlen(year)+strlen(album)+\r
- strlen(track)+strlen(composer)+strlen(comment)+8+3+\r
- 120; //30 chars for each name...\r
-}\r
-//create a string out of the id3 tag info\r
-//delete this string afterwards if you did not provide the buffer\r
-char * id3_tag::toString(char *b, int len, bool withTitle) const {\r
- const char *ta=tr("Artist");\r
-//char *tg=tr("Genre");\r
-//char *ty=tr("Year");\r
- const char *tx=tr("Album");\r
-//char *to=tr("Composer");\r
-//char *tc=tr("Comment");\r
- const char *tn=tr("Track");\r
- /* in the moment:\r
- Title: \r
- Artist:\r
- Album: year - name\r
- Track:\r
- */\r
- if (b == NULL) {\r
- len=stringlen(withTitle);\r
- b=new char[len];\r
- }\r
- const char * del=" - ";\r
- if (strlen(year) == 0) del="";\r
- if (withTitle){\r
- const char *tt=tr("Title");\r
- SNPRINTF(b,len-1,"%s: %s\n%s: %s\n%s: %s%s%s\n%s: %s",\r
- tt,title,ta,artist,tx,year,del,album,tn,track);\r
- }\r
- else {\r
-SNPRINTF(b,len-1,"%s: %s\n%s: %s%s%s\n%s: %s",\r
- ta,artist,tx,year,del,album,tn,track);\r
- }\r
- b[len-1]=0;\r
- return b;\r
-}\r
-\r
-void DemuxerAudio::setSkipFactor(int factor) {\r
- Log::getInstance()->log("DemuxerAudio",Log::DEBUG,"set skipfactor %d\n",factor);\r
- buffer->setSkipFactor(factor);\r
-}\r
-\r
+/*
+ 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. <text string according to encoding> $00 (00)
+ //The actual text <full text string according to encoding>
+ //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)frameLen<sizeof(tag->tagtype)-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)-1<len?sizeof(tag->type)-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;i<vbridpos;i++) {
+ if (data[i] != 0) {
+ Log::getInstance()->log("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;i<oldBytes-bytesConsumed;i++) {
+ tmpBuffer[i]=tmpBuffer[i+bytesConsumed];
+ }
+ tmpFill=oldBytes-bytesConsumed;
+ return 0;
+ }
+}
+
+//info functions
+const id3_tag * DemuxerAudio::getId3Tag() {
+ return id3;
+
+}
+const DemuxerAudio::mpegInfo * DemuxerAudio::getMpegInfo() {
+ if (! hasHdrInfo) return NULL;
+ info->avrBitrate=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);
+}
+
-/*\r
- Copyright 2006-2007 Mark Calderbank\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef DEMUXERTS_H\r
-#define DEMUXERTS_H\r
-\r
-#include "mutex.h"\r
-#include <deque>\r
-\r
-#include "demuxer.h"\r
-#include "defines.h"\r
-#include "channel.h"\r
-\r
-#define TS_SIZE 188\r
-#define TS_SIG 0x47\r
-\r
-class DemuxerTS : public Demuxer\r
-{\r
- public:\r
- DemuxerTS(int p_vID = 0, int p_aID = 0, int p_subID = 0, int p_tID = 0); \r
- void flush();\r
- int scan(UCHAR* buf, int len);\r
- int findPTS(UCHAR* buf, int len, ULLONG* dest);\r
- void setVID(int p_vID);\r
- void setTID(int p_tID);\r
- void setAID(int p_aID, int type, int streamtype);\r
- void setSubID(int p_subID);\r
- int getVID() { return vID; }\r
- int getTID() { return tID; }\r
- int getAID() { return aID; }\r
- int getSubID() { return subID; }\r
- int put(UCHAR* buf, int len);\r
-\r
- void setFrameNum(ULONG frame);\r
- void setPacketNum(ULONG npacket);\r
- ULONG getFrameNumFromPTS(ULLONG pts);\r
- ULONG getPacketNum();\r
- UINT stripAudio(UCHAR* buf, UINT len);\r
- static bool scanForVideo(UCHAR* buf, UINT len, bool &ish264);\r
- Channel *getChannelInfo() {return &channelinfo;};\r
-\r
-\r
- private:\r
- int processTS(UCHAR* buf);\r
-\r
- UCHAR store[TS_SIZE]; // Storage for partial packets\r
- int partPacket; // Length of partial packet stored from previous put()\r
- bool parsed; // Whether PES packet to be submitted has been parsed yet\r
- PESPacket vPacket; // Video PES packet under construction\r
- PESPacket aPacket; // Audio PES packet under construction\r
- PESPacket subPacket; // Subtitles PES packet under construction\r
- PESPacket tPacket; // Teletext PES packet under construction\r
- int vID, aID, subID,tID; // TS IDs for video/audio/subtitles \r
- int PMTPID; //TODO HANS which of these do I Need:\r
-\r
- int atype;\r
- bool subActive; // Same for subtitles\r
- UINT subLength; // Expected length of subtitle packet \r
- bool vActive, aActive, tActive; // Whether video/audio is actively being captured\r
-\r
- bool havechannelinfo;\r
- Channel channelinfo;\r
- \r
-\r
- //TODO HANS which of next do I need\r
- ULONG frameNumber, packetNumber;\r
- bool frameCounting, packetCounting;\r
- // bool doubledframerate;\r
- int framereserve;\r
- typedef struct { ULLONG pts; ULONG frame; } PTSMapEntry;\r
- typedef std::deque<PTSMapEntry> PTSMap;\r
- PTSMap pts_map;\r
- Mutex pts_map_mutex;\r
- void parseTSPacketDetails(PESPacket &packet);\r
- \r
-\r
-};\r
-\r
-#endif\r
+/*
+ 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 <deque>
+
+#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, int streamtype);
+ 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<PTSMapEntry> PTSMap;
+ PTSMap pts_map;
+ Mutex pts_map_mutex;
+ void parseTSPacketDetails(PESPacket &packet);
+
+
+};
+
+#endif
-/*\r
- Copyright 2005-2008 Mark Calderbank\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software Foundation, Inc.,\r
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#include "demuxervdr.h"\r
-#include "video.h"\r
-#include "dvbsubtitles.h"\r
-#include "log.h"\r
-\r
-#ifndef WIN32\r
-#include <endian.h>\r
-#else\r
-#define __LITTLE_ENDIAN 1234\r
-#define __BIG_ENDIAN 4321\r
-#define __BYTE_ORDER __LITTLE_ENDIAN\r
-#endif\r
-\r
-#define PTS_JUMP_MARGIN 10000\r
-#define PTS_ALLOWANCE 90000\r
-\r
-// TODO: PTS class to handle wrapping arithmetic & comparisons?\r
-static ULLONG PTSDistance(ULLONG pts1, ULLONG pts2)\r
-{\r
- // Assume pts1, pts2 < 2^33; calculate shortest distance between\r
- ULLONG ret = (pts1 > pts2) ? pts1 - pts2 : pts2 - pts1;\r
- if (ret > (1LL<<32)) ret = (1LL<<33) - ret;\r
- return ret;\r
-}\r
-\r
-static ULLONG PTSDifference(ULLONG pts1, ULLONG pts2)\r
-{\r
- // Assume pts1, pts2 < 2^33; calculate pts1 - pts2\r
- if (pts1 > pts2)\r
- return pts1 - pts2;\r
- else\r
- return (1LL<<33) + pts1 - pts2;\r
-}\r
-\r
-DemuxerVDR::DemuxerVDR()\r
-{\r
- frameCounting = false;\r
- packetCounting = false;\r
- subtitlePacketPosition = 0;\r
-}\r
-\r
-void DemuxerVDR::reset()\r
-{\r
- frameCounting = false;\r
- packetCounting = false;\r
- pts_map.clear();\r
- Demuxer::reset();\r
-}\r
-\r
-void DemuxerVDR::flush()\r
-{\r
- state = 0;\r
- submitting = false;\r
- subtitlePacketPosition = 0;\r
- Demuxer::flush();\r
-}\r
-\r
-int DemuxerVDR::scan(UCHAR *buf, int len) {\r
- // Temporarily, just look for the lowest audio stream and return it\r
- UCHAR HiByte = PESTYPE_AUDMAX;\r
- int ret = 0;\r
-\r
- while (len >= 4) {\r
- UINT pattern = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];\r
- buf++;\r
- len--;\r
-\r
- if (pattern < (UINT)(0x100 | PESTYPE_AUD0) || pattern > (UINT)(\r
- 0x100 | HiByte))\r
- continue;\r
- HiByte = pattern & 0xFF;\r
-\r
- ret = HiByte;\r
- if (HiByte == PESTYPE_AUD0)\r
- break;\r
- }\r
- return ret;\r
-}\r
-\r
-int DemuxerVDR::findPTS(UCHAR* buf, int len, ULLONG* dest) {\r
- while (len >= 14) {\r
- UINT pattern = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];\r
- buf++;\r
- len--;\r
- if (pattern < (0x100 | PESTYPE_AUD0) || pattern > (0x100\r
- | PESTYPE_VIDMAX))\r
- continue;\r
-\r
- if ((buf[5] & 0xC0) != 0x80)\r
- continue;\r
-\r
- UINT packetlength = ((UINT) buf[3] << 8) | buf[4];\r
-\r
- if (buf[6] & 0x80) // PTS_DTS_flags indicate that PTS is present\r
- {\r
- if ((buf[8] & 0x01) != 0x01 || (buf[10] & 0x01) != 0x01 || (buf[12]\r
- & 0x01) != 0x01)\r
- continue;\r
-\r
- *dest = ((ULLONG)(buf[8] & 0x0E) << 29) | ((ULLONG)(buf[9]) << 22)\r
- | ((ULLONG)(buf[10] & 0xFE) << 14) | ((ULLONG)(buf[11])\r
- << 7) | ((ULLONG)(buf[12] & 0xFE) >> 1);\r
- return 1;\r
- }\r
-\r
- buf += 5;\r
- len -= 5;\r
- buf += packetlength;\r
- len -= packetlength;\r
- }\r
- // No PTS found.\r
- return 0;\r
-}\r
-\r
-void DemuxerVDR::setFrameNum(ULONG frame)\r
-{\r
- frameCounting = true;\r
- frameNumber = frame;\r
- Log::getInstance()->log("Demuxer", Log::DEBUG, "setFrameNum %d", frame);\r
-}\r
-\r
-void DemuxerVDR::setPacketNum(ULONG npacket)\r
-{\r
- packetCounting = true;\r
- packetNumber = npacket;\r
- Log::getInstance()->log("Demuxer", Log::DEBUG, "setPacketNum %d", npacket);\r
-}\r
-\r
-int DemuxerVDR::put(UCHAR* buf, int len)\r
-{\r
- int ret = 0; // return number of bytes consumed\r
- if (submitting)\r
- {\r
- if (submitPacket(packet) == 0) // Still full!\r
- return ret;\r
- else\r
- submitting = false;\r
- }\r
-\r
- if (state > 0) // We are half way through a PES packet.\r
- {\r
- if (len >= state) // The remainder of the packet is available.\r
- {\r
- packet.write(buf, state);\r
- buf += state; len -= state; ret += state;\r
- state = 0;\r
- parseVDRPacketDetails();\r
- if (submitPacket(packet) == 0) // Stream is full\r
- {\r
- submitting = true;\r
- return ret;\r
- }\r
- }\r
- else // Write what we have, then exit.\r
- {\r
- packet.write(buf, len);\r
- state -= len;\r
- return len;\r
- }\r
- }\r
-\r
- while (len > 0)\r
- {\r
- switch (state)\r
- {\r
- case 0:\r
- case -1:\r
- if (*buf == 0x00) state--; else state = 0;\r
- buf++; len--; ret++;\r
- break;\r
- case -2:\r
- if (*buf == 0x01) state--; else if (*buf != 0x00) state = 0;\r
- buf++; len--; ret++;\r
- break;\r
- case -3:\r
- if ((*buf >= PESTYPE_VID0 && *buf <= PESTYPE_VIDMAX) ||\r
- (*buf >= PESTYPE_AUD0 && *buf <= PESTYPE_AUDMAX) ||\r
- (*buf == PESTYPE_PRIVATE_1))\r
- {\r
- packet.init(*buf);\r
- state--;\r
- }\r
- else if (*buf == 0x00)\r
- state = -1;\r
- else\r
- state = 0;\r
- buf++; len--; ret++;\r
- break;\r
- case -4:\r
- packetLength = ((UINT)*buf) << 8;\r
- state--;\r
- buf++; len--; ret++;\r
- break;\r
- case -5:\r
- packetLength += *buf;\r
- state--;\r
- buf++; len--; ret++;\r
- break;\r
- }\r
-\r
- if (state == -6) // Packet header complete\r
- {\r
- if (len >= packetLength) // The entire packet is available.\r
- {\r
- packet.write(buf, packetLength);\r
- buf += packetLength; len -= packetLength; ret += packetLength;\r
- state = 0;\r
- parseVDRPacketDetails();\r
- if (submitPacket(packet) == 0) // Stream is full\r
- {\r
- submitting = true;\r
- return ret;\r
- }\r
- }\r
- else // Write what we have.\r
- {\r
- packet.write(buf, len);\r
- state = packetLength - len;\r
- ret += len;\r
- len = 0;\r
- }\r
- }\r
- }\r
- return ret;\r
-}\r
-\r
-ULONG DemuxerVDR::getPacketNum()\r
-{\r
- return packetNumber;\r
-}\r
-\r
-ULONG DemuxerVDR::getFrameNumFromPTS(ULLONG pts)\r
-{\r
- ULLONG difference = (1LL<<33);\r
- ULONG ref_frame = 0;\r
- int total = 0, actual = 0;\r
- pts_map_mutex.Lock();\r
- PTSMap::iterator iter = pts_map.begin();\r
- while (iter != pts_map.end())\r
- {\r
- ++total;\r
- if (PTSDifference(iter->pts, pts) < PTS_ALLOWANCE)\r
- {\r
- difference = 0;\r
- ref_frame = iter->frame;\r
- actual = total;\r
- break;\r
- }\r
- ULLONG newdiff = PTSDifference(pts, iter->pts);\r
- if (newdiff < difference)\r
- {\r
- difference = newdiff;\r
- ref_frame = iter->frame;\r
- actual = total;\r
- }\r
- ++iter;\r
- }\r
- if (total > 1 && actual == 1) // We are using the most recent PTS ref.\r
- { // Delete the rest.\r
- iter = pts_map.begin(); iter++;\r
- pts_map.erase(iter, pts_map.end());\r
- }\r
- pts_map_mutex.Unlock();\r
-\r
- if (difference == (1LL<<33))\r
- return 0; // We cannot make sense of the pts\r
- else\r
- return ref_frame + (ULONG)((double) (difference / 90000) *fps);\r
-}\r
-\r
-void DemuxerVDR::dealWithSubtitlePacket()\r
-{\r
- const UCHAR* data = packet.getData();\r
- UINT packetSize = packet.getSize();\r
- if (packetSize < 9) return;\r
- UINT payloadOffset = data[8] + 9;\r
- if (packetSize < payloadOffset + 5) return;\r
- if (data[payloadOffset + 3] == 0x00)\r
- { // Not a continuation packet\r
- if (packetSize < payloadOffset + 7) return;\r
- subtitlePacket.init(data[3]);\r
- subtitlePacket.write(&data[6], payloadOffset - 6);\r
- subtitlePacket.write(&data[payloadOffset+4], packetSize-payloadOffset-4);\r
- subtitlePacketPosition = payloadOffset + 2;\r
- }\r
- else\r
- { // Continuation packet\r
- if (subtitlePacketPosition == 0) return;\r
- subtitlePacket.write(&data[payloadOffset+4], packetSize-payloadOffset-4);\r
- }\r
- \r
- const UCHAR* sub_data = subtitlePacket.getData();\r
- UINT subSize = subtitlePacket.getSize();\r
- while (subtitlePacketPosition < subSize)\r
- {\r
- if (sub_data[subtitlePacketPosition] == 0xFF)\r
- { // Packet is complete. Switch it into the "real" packet to be submitted.\r
- packet = subtitlePacket;\r
- parsePacketDetails(packet);\r
- subtitlePacketPosition = 0; // Wait for next non-continuation packet\r
- break;\r
- }\r
- if (sub_data[subtitlePacketPosition] != 0x0F)\r
- {\r
- subtitlePacketPosition = 0; // Wait for next non-continuation packet\r
- break;\r
- }\r
- if (subSize < subtitlePacketPosition + 6) break;\r
- UINT segmentLength = (sub_data[subtitlePacketPosition + 4] << 8)\r
- + sub_data[subtitlePacketPosition + 5];\r
- subtitlePacketPosition += segmentLength + 6;\r
- }\r
-}\r
-\r
-void DemuxerVDR::parseVDRPacketDetails()\r
-{\r
- if (packet.getPacketType() == PESTYPE_PRIVATE_1 && packet.getSize() > 8)\r
- {\r
- UINT payload_begin = packet[8] + 9;\r
- if (packet.getSize() > payload_begin)\r
- {\r
- UCHAR substream_type = packet[payload_begin] & 0xF0;\r
- if (substream_type == 0x20) // Subtitles\r
- {\r
- dealWithSubtitlePacket();\r
- }\r
- else // Not subtitles\r
- parsePacketDetails(packet);\r
- }\r
- }\r
- else // Not a private packet*/\r
- parsePacketDetails(packet);\r
-\r
- if (packetCounting && packet.getPacketType() >= PESTYPE_AUD0 &&\r
- packet.getPacketType() <= PESTYPE_AUDMAX)\r
- {\r
- packetNumber++;\r
- }\r
-\r
- if (frameCounting && packet.findPictureHeader(h264) &&\r
- packet.getPacketType() >= PESTYPE_VID0 &&\r
- packet.getPacketType() <= PESTYPE_VIDMAX)\r
- {\r
- ULONG frame_num = (frameNumber)++;\r
- if (packet.findSeqHeader(h264) > 1 && packet.hasPTS())\r
- {\r
- PTSMapEntry me;\r
- pts_map_mutex.Lock();\r
- if (pts_map.empty())\r
- {\r
- me.pts = packet.getPTS();\r
- me.frame = frame_num;\r
- pts_map_mutex.Unlock();\r
-Log::getInstance()->log("Demuxer", Log::DEBUG, "+* PTS INIT *+ %llu %u", me.pts, me.frame);\r
- pts_map_mutex.Lock();\r
- pts_map.push_front(me);\r
- }\r
- me = pts_map.front();\r
- pts_map_mutex.Unlock();\r
-\r
-// UINT fps = Video::getInstance()->getFPS();\r
- ULLONG pts_expected = me.pts + 90000*((ULONG)((double)(frame_num - me.frame)) / fps);\r
- while (pts_expected > (1LL<<33)) pts_expected -= (1LL<<33);\r
-\r
- if (PTSDistance(pts_expected, packet.getPTS()) > PTS_JUMP_MARGIN) // PTS jump!\r
- {\r
-Log::getInstance()->log("Demuxer", Log::DEBUG, "+* PTS JUMP *+ %llu %u", packet.getPTS(), frame_num);\r
- me.pts = packet.getPTS();\r
- me.frame = frame_num;\r
- pts_map_mutex.Lock();\r
- pts_map.push_front(me);\r
- pts_map_mutex.Unlock();\r
- }\r
- }\r
- }\r
-}\r
+/*
+ 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 <endian.h>
+#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();
+ }
+ }
+ }
+}
-/*\r
- Copyright 2004-2005 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef DRAINTARGET_H\r
-#define DRAINTARGET_H\r
-\r
-#include "defines.h"\r
-#include <list>\r
-\r
-#define MPTYPE_VIDEO_MPEG2 0x00\r
-#define MPTYPE_MPEG_AUDIO 0x01\r
-#define MPTYPE_AC3 0x02\r
-#define MPTYPE_AC3_PRE13 0x03 //old vdr recording compatmode\r
-#define MPTYPE_MPEG_AUDIO_LAYER3 0x04 //for media mp3 playback\r
-#define MPTYPE_TELETEXT 0x05 //for EBU VBI teletext\r
-#define MPTYPE_VIDEO_H264 0x06\r
-#define MPTYPE_AAC_LATM 0x07\r
-\r
-\r
-\r
-struct MediaPacket\r
-{\r
- ULONG pos_buffer; //position in stream buffer\r
- ULONG length; //length of the packet\r
- // The fields below are not needed by the MVP\r
- UCHAR type;\r
- ULLONG pts;\r
- ULLONG dts;\r
- bool synched;\r
- int index;\r
-#ifndef VOMP_PLATTFORM_MVP\r
- long long presentation_time;/* native time of plattform, in 100 ns units(Windows)*/\r
- bool disconti;\r
-#endif\r
-};\r
-\r
-using namespace std;\r
-typedef list<MediaPacket> MediaPacketList;\r
-\r
-\r
-\r
-class DrainTarget\r
-{\r
- public:\r
- DrainTarget() {\r
-\r
- }\r
- virtual ~DrainTarget(){\r
-\r
- }\r
-\r
- virtual long long SetStartOffset(long long curreftime, bool *rsync)=0;\r
- virtual void ResetTimeOffsets()=0;\r
-\r
- virtual bool DrainTargetReady() {return false;}; //if the draintarget is blocking in paused state, this tells that it is ready to rumble\r
-\r
- virtual bool dtsTimefix(){return false;} //determines if the draintargets needs a mixure of pts and dts or not\r
-\r
-// The following two functions are used by the Stream\r
-// to deliver media packets to the front end (DrainTarget).\r
-//\r
-// First, the Stream calls PrepareMediaSample, which gives the front end\r
-// read-only access to the Stream's MediaPacketList. PrepareMediaSample should\r
-// examine the list to determine how much it wishes to consume, and\r
-// should copy any data it requires from the MediaPacket objects into\r
-// local storage.\r
-// This function call takes place under a mutex lock to ensure integrity\r
-// of the list structure. It should be fast and must not contain any\r
-// cancellation points, such as I/O calls for logging.\r
-//\r
-// Second, the Stream releases the mutex and calls DeliverMediaSample.\r
-// This function delivers data from the Stream buffer to the presentation\r
-// device and returns information to the Stream regarding how many MediaPackets\r
-// were consumed. Any data copied from the MediaPackets objects during\r
-// PrepareMediaSample is guaranteed by the Stream still to be valid\r
-// during the following DeliverMediaSample call.\r
-\r
- // samplepos is equal to the number of bytes from the first MediaPacket\r
- // in the list that have already been consumed in a previous call.\r
- virtual void PrepareMediaSample(const MediaPacketList&, UINT samplepos)=0;\r
-\r
- // The Stream guarantees that the value of *samplepos passed to\r
- // DeliverMediaSample will be equal to the value of samplepos passed to\r
- // PrepareMediaSample in the previous call.\r
- // This function should consume data from the buffer according to the\r
- // decisions made in PrepareMediaSample. Its return value and *samplepos\r
- // tell the Stream how much data it consumed.\r
- // If DeliverMediaSample returns X, the Stream will remove packets 0 to X-1\r
- // (inclusive) from the list before the next call.\r
- // DeliverMediaSample must also set *samplepos equal to the number of bytes\r
- // processed from packet X (usually zero).\r
- // It is allowed, that the draintarget modifies the part of the buffer, which belongs \r
- // to the mediapackets it is processing.\r
- virtual UINT DeliverMediaSample(UCHAR* buffer, UINT *samplepos)=0;\r
- // The drain target might advice the feeder about free buffers with threadSignal\r
-\r
-\r
-};\r
-\r
-#endif\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.
+*/
+
+#ifndef DRAINTARGET_H
+#define DRAINTARGET_H
+
+#include "defines.h"
+#include <list>
+
+#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
+#define MPTYPE_AAC_LATM 0x07
+
+
+
+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;
+#ifndef VOMP_PLATTFORM_MVP
+ long long presentation_time;/* native time of plattform, in 100 ns units(Windows)*/
+ bool disconti;
+#endif
+};
+
+using namespace std;
+typedef list<MediaPacket> MediaPacketList;
+
+
+
+class DrainTarget
+{
+ public:
+ DrainTarget() {
+
+ }
+ virtual ~DrainTarget(){
+
+ }
+
+ virtual long long SetStartOffset(long long curreftime, bool *rsync)=0;
+ virtual void ResetTimeOffsets()=0;
+
+ virtual bool DrainTargetReady() {return false;}; //if the draintarget is blocking in paused state, this tells that it is ready to rumble
+
+ 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
-/*\r
- Copyright 2011 Marten Richter\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef FEED_H\r
-#define FEED_H\r
-\r
-class Feed{\r
-public:\r
- virtual void SignalFeeder()=0;\r
-};\r
-\r
-#endif\r
+/*
+ Copyright 2011 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 FEED_H
+#define FEED_H
+
+class Feed{
+public:
+ virtual void SignalFeeder()=0;
+};
+
+#endif
-Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.\r
-\r
-This Font Software is licensed under the SIL Open Font License, Version 1.1.\r
-\r
-This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL\r
-\r
-\r
------------------------------------------------------------\r
-SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007\r
------------------------------------------------------------\r
-\r
-PREAMBLE\r
-The goals of the Open Font License (OFL) are to stimulate worldwide\r
-development of collaborative font projects, to support the font creation\r
-efforts of academic and linguistic communities, and to provide a free and\r
-open framework in which fonts may be shared and improved in partnership\r
-with others.\r
-\r
-The OFL allows the licensed fonts to be used, studied, modified and\r
-redistributed freely as long as they are not sold by themselves. The\r
-fonts, including any derivative works, can be bundled, embedded, \r
-redistributed and/or sold with any software provided that any reserved\r
-names are not used by derivative works. The fonts and derivatives,\r
-however, cannot be released under any other type of license. The\r
-requirement for fonts to remain under this license does not apply\r
-to any document created using the fonts or their derivatives.\r
-\r
-DEFINITIONS\r
-"Font Software" refers to the set of files released by the Copyright\r
-Holder(s) under this license and clearly marked as such. This may\r
-include source files, build scripts and documentation.\r
-\r
-"Reserved Font Name" refers to any names specified as such after the\r
-copyright statement(s).\r
-\r
-"Original Version" refers to the collection of Font Software components as\r
-distributed by the Copyright Holder(s).\r
-\r
-"Modified Version" refers to any derivative made by adding to, deleting,\r
-or substituting -- in part or in whole -- any of the components of the\r
-Original Version, by changing formats or by porting the Font Software to a\r
-new environment.\r
-\r
-"Author" refers to any designer, engineer, programmer, technical\r
-writer or other person who contributed to the Font Software.\r
-\r
-PERMISSION & CONDITIONS\r
-Permission is hereby granted, free of charge, to any person obtaining\r
-a copy of the Font Software, to use, study, copy, merge, embed, modify,\r
-redistribute, and sell modified and unmodified copies of the Font\r
-Software, subject to the following conditions:\r
-\r
-1) Neither the Font Software nor any of its individual components,\r
-in Original or Modified Versions, may be sold by itself.\r
-\r
-2) Original or Modified Versions of the Font Software may be bundled,\r
-redistributed and/or sold with any software, provided that each copy\r
-contains the above copyright notice and this license. These can be\r
-included either as stand-alone text files, human-readable headers or\r
-in the appropriate machine-readable metadata fields within text or\r
-binary files as long as those fields can be easily viewed by the user.\r
-\r
-3) No Modified Version of the Font Software may use the Reserved Font\r
-Name(s) unless explicit written permission is granted by the corresponding\r
-Copyright Holder. This restriction only applies to the primary font name as\r
-presented to the users.\r
-\r
-4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font\r
-Software shall not be used to promote, endorse or advertise any\r
-Modified Version, except to acknowledge the contribution(s) of the\r
-Copyright Holder(s) and the Author(s) or with their explicit written\r
-permission.\r
-\r
-5) The Font Software, modified or unmodified, in part or in whole,\r
-must be distributed entirely under this license, and must not be\r
-distributed under any other license. The requirement for fonts to\r
-remain under this license does not apply to any document created\r
-using the Font Software.\r
-\r
-TERMINATION\r
-This license becomes null and void if any of the above conditions are\r
-not met.\r
-\r
-DISCLAIMER\r
-THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF\r
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT\r
-OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE\r
-COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\r
-INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL\r
-DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\r
-FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM\r
-OTHER DEALINGS IN THE FONT SOFTWARE.\r
+Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+
+This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
-/*\r
- Copyright 2012 Marten Richter\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-#include "glosdshader.h"\r
-\r
-const GLchar generic_vertex_shader[] =\r
- "attribute vec4 vec_pos;\n"\r
- "attribute vec2 tex_coord;\n"\r
- "varying vec2 out_texCoord;\n"\r
- "void main()\n"\r
- "{\n"\r
- " gl_Position=vec_pos;\n"\r
- " out_texCoord=tex_coord;\n"\r
- "}\n";\r
-\r
-const GLchar osd_frag_shader[] =\r
- "precision mediump float;\n"\r
- "uniform sampler2D texture;\n"\r
- "varying vec2 out_texCoord;\n"\r
- "void main()\n"\r
- "{\n"\r
- " gl_FragColor=texture2D(texture,out_texCoord);\n"\r
- "}\n";\r
-\r
-GLOsdShader::GLOsdShader(): GLShader("GLOsdShader")\r
-{\r
-\r
-}\r
-\r
-GLOsdShader::~GLOsdShader()\r
-{\r
- //parent does everything\r
-}\r
-\r
-int GLOsdShader::init()\r
-{\r
- if (!initShaders(generic_vertex_shader,osd_frag_shader)) {\r
- return 0;\r
- }\r
- osd_sampler_loc=glGetUniformLocation(shad_program,"texture");\r
- return 1;\r
-\r
-}\r
-\r
-int GLOsdShader::deinit()\r
-{\r
- return deinitShaders();\r
-}\r
-\r
-int GLOsdShader::PrepareRendering(GLuint osd_tex){ // This Function setups the rendering pipeline according to the shaders standards\r
- glUseProgram(shad_program);\r
-\r
- glActiveTexture(GL_TEXTURE0);\r
- glBindTexture(GL_TEXTURE_2D,osd_tex);\r
-\r
- glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);\r
- glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);\r
-\r
- glUniform1i(osd_sampler_loc,0);\r
- return 1;\r
-\r
-}\r
-\r
-int GLOsdShader::BindAttributes()\r
-{\r
- glBindAttribLocation(shad_program,0,"vec_pos");\r
- glBindAttribLocation(shad_program,1,"tex_coord");\r
- return 1;\r
-}\r
+/*
+ Copyright 2012 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 "glosdshader.h"
+
+const GLchar generic_vertex_shader[] =
+ "attribute vec4 vec_pos;\n"
+ "attribute vec2 tex_coord;\n"
+ "varying vec2 out_texCoord;\n"
+ "void main()\n"
+ "{\n"
+ " gl_Position=vec_pos;\n"
+ " out_texCoord=tex_coord;\n"
+ "}\n";
+
+const GLchar osd_frag_shader[] =
+ "precision mediump float;\n"
+ "uniform sampler2D texture;\n"
+ "varying vec2 out_texCoord;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor=texture2D(texture,out_texCoord);\n"
+ "}\n";
+
+GLOsdShader::GLOsdShader(): GLShader("GLOsdShader")
+{
+
+}
+
+GLOsdShader::~GLOsdShader()
+{
+ //parent does everything
+}
+
+int GLOsdShader::init()
+{
+ if (!initShaders(generic_vertex_shader,osd_frag_shader)) {
+ return 0;
+ }
+ osd_sampler_loc=glGetUniformLocation(shad_program,"texture");
+ return 1;
+
+}
+
+int GLOsdShader::deinit()
+{
+ return deinitShaders();
+}
+
+int GLOsdShader::PrepareRendering(GLuint osd_tex){ // This Function setups the rendering pipeline according to the shaders standards
+ glUseProgram(shad_program);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D,osd_tex);
+
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
+
+ glUniform1i(osd_sampler_loc,0);
+ return 1;
+
+}
+
+int GLOsdShader::BindAttributes()
+{
+ glBindAttribLocation(shad_program,0,"vec_pos");
+ glBindAttribLocation(shad_program,1,"tex_coord");
+ return 1;
+}
-/*\r
- Copyright 2012 Marten Richter\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef GL_OSDSHADER_H\r
-#define GL_OSDSHADER_H\r
-\r
-#include "glshader.h"\r
-\r
-class GLOsdShader: public GLShader {\r
-public:\r
- GLOsdShader();\r
- virtual ~GLOsdShader();\r
-\r
- int init();\r
- int deinit();\r
-\r
- int PrepareRendering(GLuint osd_tex); // This Function setups the rendering pipeline according to the shaders standards\r
-\r
-protected:\r
- virtual int BindAttributes();\r
-\r
- GLint osd_sampler_loc;\r
-\r
-};\r
-\r
-\r
-#endif\r
+/*
+ Copyright 2012 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 GL_OSDSHADER_H
+#define GL_OSDSHADER_H
+
+#include "glshader.h"
+
+class GLOsdShader: public GLShader {
+public:
+ GLOsdShader();
+ virtual ~GLOsdShader();
+
+ int init();
+ int deinit();
+
+ int PrepareRendering(GLuint osd_tex); // This Function setups the rendering pipeline according to the shaders standards
+
+protected:
+ virtual int BindAttributes();
+
+ GLint osd_sampler_loc;
+
+};
+
+
+#endif
-/*\r
- Copyright 2012 Marten Richter\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-#include "glshader.h"\r
-\r
-GLShader::GLShader(const char* name)\r
-{\r
- initted=0;\r
- objname=name;\r
-}\r
-\r
-GLShader::~GLShader()\r
-{\r
- if (initted) {\r
- deinitShaders();\r
- }\r
-\r
-\r
-\r
-}\r
-\r
-int GLShader::initShaders(const char * vertex_shad,const char *fragment_shad)\r
-{\r
- vertex_shader=CreateShader(vertex_shad, GL_VERTEX_SHADER);\r
- frag_shader=CreateShader(fragment_shad, GL_FRAGMENT_SHADER);\r
-\r
- // Create the program for osd rendering\r
- shad_program=glCreateProgram();\r
- if (shad_program==0) {\r
- Log::getInstance()->log("GLShader", Log::WARN, "%s: Creating glsl program failed!%d",objname,glGetError());\r
- return 0;\r
- }\r
- glAttachShader(shad_program,vertex_shader);\r
- glAttachShader(shad_program,frag_shader);\r
- if (!BindAttributes()) {\r
- return 0;\r
- }\r
- glLinkProgram(shad_program);\r
-\r
-\r
- GLint link_status;\r
- glGetProgramiv(shad_program,GL_LINK_STATUS, &link_status);\r
-\r
- if (!link_status) {\r
- char buffer[1024];\r
- glGetProgramInfoLog(shad_program,1024,NULL,buffer);\r
- Log::getInstance()->log("GLShader", Log::WARN, "%s: Compiling Programm failed!",objname);\r
- Log::getInstance()->log("GLShader", Log::WARN, "%s",buffer);\r
- glDeleteProgram(shad_program);\r
- return 0;\r
- }\r
-\r
-}\r
-\r
-GLuint GLShader::CreateShader(const GLchar * source, GLenum type)\r
-{\r
- GLuint ret_shad=0;\r
-\r
- ret_shad=glCreateShader(type);\r
- if (ret_shad==0 ) {\r
- Log::getInstance()->log("GLShader", Log::WARN, "%s: Creating Shader failed! %d",objname, glGetError());\r
- return 0;\r
- }\r
- glShaderSource(ret_shad,1,&source,NULL);\r
- glCompileShader(ret_shad);\r
- GLint comp_status;\r
- glGetShaderiv(ret_shad,GL_COMPILE_STATUS, &comp_status);\r
-\r
- if (!comp_status) {\r
- char buffer[1024];\r
- Log::getInstance()->log("GLShader", Log::WARN, "%s: Compiling Shader failed!",objname);\r
- glGetShaderInfoLog(ret_shad,1024,NULL,buffer);\r
- Log::getInstance()->log("GLShader", Log::WARN, "%s: %s",objname,buffer);\r
- glDeleteShader(ret_shad);\r
- return 0;\r
- }\r
- return ret_shad;\r
-}\r
-\r
-int GLShader::deinitShaders()\r
-{\r
- if (frag_shader!=0) glDeleteShader(frag_shader);\r
- frag_shader=0;\r
- if (vertex_shader!=0) glDeleteShader(vertex_shader);\r
- vertex_shader=0;\r
- if (shad_program!=0) glDeleteProgram(shad_program);\r
- shad_program=0;\r
- initted=0;\r
- return 0;\r
-}\r
-\r
+/*
+ Copyright 2012 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 "glshader.h"
+
+GLShader::GLShader(const char* name)
+{
+ initted=0;
+ objname=name;
+}
+
+GLShader::~GLShader()
+{
+ if (initted) {
+ deinitShaders();
+ }
+
+
+
+}
+
+int GLShader::initShaders(const char * vertex_shad,const char *fragment_shad)
+{
+ vertex_shader=CreateShader(vertex_shad, GL_VERTEX_SHADER);
+ frag_shader=CreateShader(fragment_shad, GL_FRAGMENT_SHADER);
+
+ // Create the program for osd rendering
+ shad_program=glCreateProgram();
+ if (shad_program==0) {
+ Log::getInstance()->log("GLShader", Log::WARN, "%s: Creating glsl program failed!%d",objname,glGetError());
+ return 0;
+ }
+ glAttachShader(shad_program,vertex_shader);
+ glAttachShader(shad_program,frag_shader);
+ if (!BindAttributes()) {
+ return 0;
+ }
+ glLinkProgram(shad_program);
+
+
+ GLint link_status;
+ glGetProgramiv(shad_program,GL_LINK_STATUS, &link_status);
+
+ if (!link_status) {
+ char buffer[1024];
+ glGetProgramInfoLog(shad_program,1024,NULL,buffer);
+ Log::getInstance()->log("GLShader", Log::WARN, "%s: Compiling Programm failed!",objname);
+ Log::getInstance()->log("GLShader", Log::WARN, "%s",buffer);
+ glDeleteProgram(shad_program);
+ return 0;
+ }
+
+}
+
+GLuint GLShader::CreateShader(const GLchar * source, GLenum type)
+{
+ GLuint ret_shad=0;
+
+ ret_shad=glCreateShader(type);
+ if (ret_shad==0 ) {
+ Log::getInstance()->log("GLShader", Log::WARN, "%s: Creating Shader failed! %d",objname, glGetError());
+ return 0;
+ }
+ glShaderSource(ret_shad,1,&source,NULL);
+ glCompileShader(ret_shad);
+ GLint comp_status;
+ glGetShaderiv(ret_shad,GL_COMPILE_STATUS, &comp_status);
+
+ if (!comp_status) {
+ char buffer[1024];
+ Log::getInstance()->log("GLShader", Log::WARN, "%s: Compiling Shader failed!",objname);
+ glGetShaderInfoLog(ret_shad,1024,NULL,buffer);
+ Log::getInstance()->log("GLShader", Log::WARN, "%s: %s",objname,buffer);
+ glDeleteShader(ret_shad);
+ return 0;
+ }
+ return ret_shad;
+}
+
+int GLShader::deinitShaders()
+{
+ if (frag_shader!=0) glDeleteShader(frag_shader);
+ frag_shader=0;
+ if (vertex_shader!=0) glDeleteShader(vertex_shader);
+ vertex_shader=0;
+ if (shad_program!=0) glDeleteProgram(shad_program);
+ shad_program=0;
+ initted=0;
+ return 0;
+}
+
-/*\r
- Copyright 2012 Marten Richter\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef GL_SHADER_H\r
-#define GL_SHADER_H\r
-\r
-#include <GLES2/gl2.h>\r
-#include "log.h"\r
-\r
-class GLShader {\r
-public:\r
- GLShader(const char* name);\r
- virtual ~GLShader();\r
-\r
- int initShaders(const GLchar * vertex_shad,const GLchar *fragment_shad);\r
-\r
- int deinitShaders();\r
-\r
-\r
-\r
-protected:\r
-\r
- GLuint CreateShader(const GLchar * source, GLenum type);\r
-\r
- virtual int BindAttributes() {return 1;};\r
-\r
- GLuint vertex_shader;\r
- GLuint frag_shader;\r
-\r
- GLuint shad_program;\r
- int initted;\r
- const char* objname;\r
-\r
-};\r
-\r
-\r
-\r
-#endif\r
+/*
+ Copyright 2012 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 GL_SHADER_H
+#define GL_SHADER_H
+
+#include <GLES2/gl2.h>
+#include "log.h"
+
+class GLShader {
+public:
+ GLShader(const char* name);
+ virtual ~GLShader();
+
+ int initShaders(const GLchar * vertex_shad,const GLchar *fragment_shad);
+
+ int deinitShaders();
+
+
+
+protected:
+
+ GLuint CreateShader(const GLchar * source, GLenum type);
+
+ virtual int BindAttributes() {return 1;};
+
+ GLuint vertex_shader;
+ GLuint frag_shader;
+
+ GLuint shad_program;
+ int initted;
+ const char* objname;
+
+};
+
+
+
+#endif
-/*\r
- Copyright 2004-2006 Chris Tallon, Andreas Vogel\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef IMAGEREADER_H\r
-#define IMAGEREADER_H\r
-\r
-#include <stdio.h>\r
-#include <stdlib.h>\r
-#ifndef WIN32\r
-#include <sys/time.h>\r
-#endif\r
-#include <time.h>\r
-\r
-#include "callback.h"\r
-#include "thread.h"\r
-\r
-#include "threadsystem.h"\r
-\r
-\r
-\r
-class MediaProvider;\r
-class Log;\r
-\r
-\r
-class ImageReader : public Thread_TYPE, public Callback\r
-{\r
- public:\r
-\r
- //create an image reader for a media channel\r
- //the channel must already been opened\r
- ImageReader(int channel,MediaProvider *p);\r
- //call shutdown before destroying the reader!\r
- virtual ~ImageReader();\r
- //request the next image block\r
- //will return the current block (if already read) and request the next from the server\r
- //rsize will return the received len: 0 on EOF, -1 on error\r
- //the returned buffer has to be freed outside (really use free!)\r
- //if the buffer is not filled it will wait until the chunk is received!\r
- int getImageChunk(ULLONG offset,UINT len, UINT * rsize,UCHAR **buffer);\r
-\r
- //is the reader still running?\r
- bool isReaderRunning();\r
-\r
- void shutdown();\r
-\r
- //stop the reader (waits until the reader thread does not access anything)\r
- void stop();\r
-\r
- \r
- virtual void call(void * caller);\r
-\r
-\r
- protected:\r
- void threadMethod();\r
- void threadPostStopCleanup();\r
-\r
- private:\r
- MediaProvider * provider;\r
- int channel;\r
- static const int MAXCHUNKS=2;\r
- //start the player thread\r
- void run();\r
- Log* logger;\r
-\r
- bool running;\r
- bool readerRunning;\r
- void waitTimed(int ms);\r
-\r
- typedef enum {\r
- S_REQUEST=1,\r
- S_FETCHING=2,\r
- S_READY=3,\r
- S_BREAK=4,\r
- S_FREE=0\r
- } rstate;\r
-\r
- class Chunk{\r
- public:\r
- UCHAR * buffer; //receive buffer (to be deallocated with free)\r
- ULLONG offset; //offset within stream/file\r
- UINT reqlen; //requested len\r
- ULONG len; //received len\r
- rstate state; //current state\r
- Chunk() {\r
- buffer=NULL;\r
- offset=0;\r
- reqlen=0;\r
- len=0;\r
- state=S_FREE;\r
- } \r
- };\r
- //received data\r
- //setting state in this array requires the thread lock\r
- Chunk data[MAXCHUNKS];\r
-\r
-};\r
-\r
-#endif\r
-\r
+/*
+ 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 <stdio.h>
+#include <stdlib.h>
+#ifndef WIN32
+#include <sys/time.h>
+#endif
+#include <time.h>
+
+#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
+
-/*\r
- Copyright 2004-2005 Chris Tallon\r
- Copyright 2003-2004 University Of Bradford\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#include "log.h"\r
-\r
-#include "vdr.h"\r
-\r
-#ifdef __ANDROID__\r
-#include <android/log.h>\r
-#endif\r
-\r
-Log* Log::instance = NULL;\r
-\r
-Log::Log()\r
-{\r
- if (instance) return;\r
- instance = this;\r
- logfile = NULL;\r
- initted = 0;\r
- logLevel = 0;\r
- extlog = NULL;\r
-}\r
-\r
-Log::~Log()\r
-{\r
- instance = NULL;\r
-}\r
-\r
-Log* Log::getInstance()\r
-{\r
- return instance;\r
-}\r
-\r
-void Log::upLogLevel()\r
-{\r
- if (logLevel == Log::DEBUG)\r
- {\r
- log("Log", logLevel, "Log level is at its highest already");\r
- return;\r
- }\r
-\r
- logLevel++;\r
- log("Log", logLevel, "Log level is now %i", logLevel);\r
-}\r
-\r
-void Log::downLogLevel()\r
-{\r
- if (logLevel == Log::CRAZY)\r
- {\r
- log("Log", logLevel, "Log level is at its lowest already");\r
- return;\r
- }\r
-\r
- logLevel--;\r
- log("Log", logLevel, "Log level is now %i", logLevel);\r
-}\r
-\r
-int Log::init(int startLogLevel,const char* fileName, int tenabled)\r
-{\r
- initted = 1;\r
- logLevel = startLogLevel;\r
- enabled = tenabled;\r
-// logfile = fopen(fileName, "a");\r
-// logfile = fopen(stdout, "a");\r
- logfile = stdout;\r
-// logfile = fopen("/log", "a");\r
-\r
- if (logfile) return 1;\r
- else return 0;\r
-}\r
-\r
-int Log::shutdown()\r
-{\r
- if (!initted) return 1;\r
- if (enabled) fclose(logfile);\r
- return 1;\r
-}\r
-\r
-int Log::log(const char *fromModule, int level,const char* message, ...)\r
-{\r
- if (!instance || !logfile) return 0;\r
-\r
- if (!enabled && !extlog) return 1;\r
- if (level > logLevel) return 1;\r
-\r
- char buffer[151];\r
- int spaceLeft = 150;\r
-\r
-#ifndef _MSC_VER\r
- struct timeval tv;\r
- gettimeofday(&tv, NULL);\r
- struct tm* tms = localtime(&tv.tv_sec);\r
-#else\r
- struct _timeb tb;\r
- _ftime(&tb);\r
- struct tm* tms = localtime(&tb.time);\r
-#endif\r
- spaceLeft -= strftime(buffer, spaceLeft, "%H:%M:%S.", tms);\r
-#ifndef _MSC_VER\r
- spaceLeft -= SNPRINTF(&buffer[150-spaceLeft], spaceLeft, "%06lu ", (unsigned long)tv.tv_usec);\r
-#else\r
- spaceLeft -= SNPRINTF(&buffer[150-spaceLeft], spaceLeft, "%06lu ", (unsigned long)tb.millitm);\r
-#endif\r
-\r
- char levelString[10];\r
- if (level == CRAZY) strcpy(levelString, "[CRAZY] ");\r
- if (level == EMERG) strcpy(levelString, "[EMERG] ");\r
- if (level == ALERT) strcpy(levelString, "[ALERT] ");\r
- if (level == CRIT) strcpy(levelString, "[CRIT] ");\r
- if (level == ERR) strcpy(levelString, "[ERR] ");\r
- if (level == WARN) strcpy(levelString, "[WARN] ");\r
- if (level == NOTICE) strcpy(levelString, "[notice]");\r
- if (level == INFO) strcpy(levelString, "[info] ");\r
- if (level == DEBUG) strcpy(levelString, "[debug] ");\r
-\r
-#ifndef WIN32\r
- spaceLeft -= SNPRINTF(&buffer[150-spaceLeft], spaceLeft, "%s %d %s - ", levelString, getpid(), fromModule);\r
-#else\r
- spaceLeft -= SNPRINTF(&buffer[150-spaceLeft], spaceLeft, "%s %s - ", levelString, fromModule);\r
-#endif\r
-\r
- va_list ap;\r
- va_start(ap, message);\r
- spaceLeft = VSNPRINTF(&buffer[150-spaceLeft], spaceLeft, message, ap);\r
- va_end(ap);\r
-\r
- int messageLength = strlen(buffer);\r
- if (messageLength < 150)\r
- {\r
- buffer[messageLength] = '\n';\r
- buffer[messageLength+1] = '\0';\r
- }\r
- else\r
- {\r
- buffer[149] = '\n';\r
- buffer[150] = '\0';\r
- }\r
- \r
- int success = 1;\r
- if (enabled)\r
- {\r
-#ifndef __ANDROID__\r
- success = fputs(buffer, logfile);\r
- fflush(NULL);\r
-#else\r
- int and_level=0;\r
- switch (level) {\r
- case CRAZY:\r
- case ALERT:\r
- case EMERG :\r
- case CRIT:{\r
- and_level=ANDROID_LOG_FATAL;\r
- } break;\r
- case ERR: {\r
- and_level=ANDROID_LOG_ERROR;\r
- } break;\r
- case WARN: {\r
- and_level=ANDROID_LOG_WARN;\r
- } break;\r
- case NOTICE:\r
- case INFO: {\r
- and_level=ANDROID_LOG_INFO;\r
- } break;\r
- case DEBUG :{\r
- and_level=ANDROID_LOG_DEBUG;\r
- } break;\r
- };\r
- __android_log_vprint(and_level, fromModule,\r
- message, ap);\r
-#endif\r
- }\r
-\r
- if (extlog) extlog->LogExtern(buffer); //Replacement for network logging\r
-\r
-\r
- if (success != EOF)\r
- return 1;\r
- else\r
- return 0;\r
-\r
-}\r
-\r
-void Log::logLongString(const char *fromModule, int level,const char *message)\r
-{\r
- int string_size=strlen(message);\r
- char buffer[100];\r
- const char * pointer=message;\r
- for (int str_written=0; str_written<string_size;str_written+=99) {\r
- strncpy(buffer,pointer,99);\r
- buffer[99]=0;\r
- pointer+=99;\r
- log(fromModule,level,"%s",buffer);\r
- }\r
-\r
-}\r
-\r
-int Log::status()\r
-{\r
- if (instance && logfile) return 1;\r
- else return 0;\r
-}\r
-\r
-void Log::setExternLogger(ExternLogger* login) {\r
- extlog=login;\r
- log("Log", Log::DEBUG, "Extern logging started");\r
-}\r
-\r
-\r
-\r
-\r
+/*
+ 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 <android/log.h>
+#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;
+
+}
+
+void Log::logLongString(const char *fromModule, int level,const char *message)
+{
+ int string_size=strlen(message);
+ char buffer[100];
+ const char * pointer=message;
+ for (int str_written=0; str_written<string_size;str_written+=99) {
+ strncpy(buffer,pointer,99);
+ buffer[99]=0;
+ pointer+=99;
+ log(fromModule,level,"%s",buffer);
+ }
+
+}
+
+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");
+}
+
+
+
+
-/*\r
- Copyright 2004-2005 Chris Tallon\r
- Copyright 2003-2004 University Of Bradford\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef LOG_H\r
-#define LOG_H\r
-\r
-#include <stdio.h>\r
-#ifndef WIN32\r
- #include <unistd.h>\r
- #include <sys/time.h>\r
-#else\r
- #include <sys/timeb.h>\r
-#endif\r
-\r
-#include <time.h>\r
-#include <string.h>\r
-#include <stdarg.h>\r
-#include <sys/types.h>\r
-#include "defines.h"\r
-\r
-\r
-class ExternLogger {\r
-public:\r
- virtual bool LogExtern(const char* message)=0;\r
-};\r
-\r
-\r
-class Log\r
-{\r
- public:\r
- Log();\r
- ~Log();\r
- static Log* getInstance();\r
-\r
- int init(int defaultLevel,const char* fileName, int enabled);\r
- int shutdown();\r
- int log(const char *fromModule, int level,const char *message, ...);\r
- void logLongString(const char *fromModule, int level,const char *message);\r
- int status();\r
- void upLogLevel();\r
- void downLogLevel();\r
- void setExternLogger(ExternLogger* log);\r
- void unsetExternLogger() {\r
- extlog=NULL;\r
- }\r
-\r
-\r
-\r
- const static int CRAZY = 0; // mad crazy things that should never happen\r
- const static int EMERG = 1; // human assist required NOW\r
- const static int ALERT = 2; // system unusable, but happy to sit there\r
- const static int CRIT = 3; // still working, but maybe about to die\r
- const static int ERR = 4; // that response is not even listed...\r
- const static int WARN = 5; // this could be a bad thing. still running tho\r
- const static int NOTICE = 6; // significant good thing\r
- const static int INFO = 7; // verbose good thing\r
- const static int DEBUG = 8; // debug-level messages\r
-\r
- private:\r
- static Log* instance;\r
- int initted;\r
- int logLevel;\r
- int enabled;\r
-\r
- FILE *logfile;\r
- \r
- ExternLogger* extlog;\r
-\r
-\r
-};\r
-\r
-#endif\r
-\r
-/*\r
-\r
-Documentation\r
--------------\r
-\r
-This class is intended to be instatiated once by the core.\r
-For a one off use:\r
-\r
-Log::getInstance()->log("<module-name>", Log::<levelname>, "<message>");\r
-\r
-Or, a pointer can be stored and used:\r
-\r
-Log *myptr = Log::getInstance();\r
-\r
-myptr->log("<module-name>", Log::<levelname>, "<message>");\r
-myptr->log("<module-name>", Log::<levelname>, "<message>");\r
-\r
-Level usages are above.\r
-\r
-The message parameter in the log function can be used in the same way as printf, eg.\r
-\r
-myptr->log("<module-name>", Log::<levelname>, "Success: %s %i", stringpointer, integer);\r
-\r
-*/\r
+/*
+ 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 <stdio.h>
+#ifndef WIN32
+ #include <unistd.h>
+ #include <sys/time.h>
+#else
+ #include <sys/timeb.h>
+#endif
+
+#include <time.h>
+#include <string.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#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, ...);
+ void logLongString(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("<module-name>", Log::<levelname>, "<message>");
+
+Or, a pointer can be stored and used:
+
+Log *myptr = Log::getInstance();
+
+myptr->log("<module-name>", Log::<levelname>, "<message>");
+myptr->log("<module-name>", Log::<levelname>, "<message>");
+
+Level usages are above.
+
+The message parameter in the log function can be used in the same way as printf, eg.
+
+myptr->log("<module-name>", Log::<levelname>, "Success: %s %i", stringpointer, integer);
+
+*/
-/*\r
- Copyright 2004-2005 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#include <stdio.h>\r
-#include <stdlib.h>\r
-#include <string.h>\r
-#include <signal.h>\r
-#ifndef WIN32\r
-#include <unistd.h>\r
-#include <endian.h>\r
-#endif\r
-\r
-#include "defines.h"\r
-\r
-#ifdef HANDLE_VT_SWITCHING\r
-#include <signal.h>\r
-#include <sys/ioctl.h>\r
-#include <linux/vt.h>\r
-#endif\r
-#include "log.h"\r
-#include "timers.h"\r
-#include "vdr.h"\r
-#include "boxstack.h"\r
-#include "command.h"\r
-\r
-\r
-#ifdef VOMP_PLATTFORM_MVP\r
-\r
-\r
-#include "mtdmvp.h"\r
-#include "remotemvp.h"\r
-#include "ledmvp.h"\r
-#include "osdmvp.h"\r
-#include "audiomvp.h"\r
-#include "videomvp.h"\r
-\r
-extern "C"\r
-{\r
- int ticonfig_main(int, char**);\r
-}\r
-\r
-#endif\r
-\r
-#ifdef VOMP_PLATTFORM_NMT\r
-\r
-#include "mtdnmt.h"\r
-#include "remotelirc.h"\r
-#include "lednmt.h"\r
-#include "osddirectfb.h"\r
-#include "audionmt.h"\r
-#include "videonmt.h"\r
-\r
-#endif\r
-\r
-#ifdef VOMP_PLATTFORM_RASPBERRY\r
-\r
-#include "mtdraspberry.h"\r
-#include "remotelinux.h"\r
-#include "ledraspberry.h"\r
-#include "osdopenvg.h"\r
-#include "audioomx.h"\r
-#include "videoomx.h"\r
-\r
-#endif\r
-\r
-\r
-\r
-\r
-#include "wol.h"\r
-#include "vsleeptimer.h"\r
-\r
-\r
-#ifndef WIN32\r
-void sighandler(int signalReceived);\r
-#endif\r
-\r
-void shutdown(int code);\r
-\r
-\r
-\r
-// Global variables --------------------------------------------------------------------------------------------------\r
-Log* logger;\r
-Remote* remote;\r
-Mtd* mtd;\r
-Led* led;\r
-Osd* osd;\r
-Timers* timers;\r
-BoxStack* boxstack;\r
-Command* command;\r
-VDR* vdr;\r
-Video* video;\r
-Audio* audio;\r
-Wol* wol;\r
-Sleeptimer* sleeptimer;\r
-\r
-#ifdef HANDLE_VT_SWITCHING\r
-int fdtty;\r
-struct vt_mode old_vtmode;\r
-#endif\r
-\r
-// Linux MVP main function and sighandler\r
-#ifndef WIN32\r
-int main(int argc, char** argv)\r
-{\r
-#ifdef VOMP_PLATTFORM_MVP\r
- if (strstr(argv[0], "ticonfig")) return ticonfig_main(argc, argv);\r
-#endif\r
-\r
- bool daemonize = true;\r
- bool debugEnabled = false;\r
- bool crashed = false;\r
- char* setServer = NULL;\r
- int c;\r
-\r
- while ((c = getopt(argc, argv, "cdns:")) != -1)\r
- {\r
- switch (c)\r
- {\r
- case 'c':\r
- crashed = true;\r
- break;\r
- case 'd':\r
- debugEnabled = true; // and...\r
- case 'n':\r
- daemonize = false;\r
- break;\r
- case 's':\r
- setServer = optarg;\r
- break;\r
- case '?':\r
- printf("Unknown option\n");\r
- return 1;\r
- default:\r
- printf("Option error\n");\r
- return 1;\r
- }\r
- }\r
-\r
- // Init global vars ------------------------------------------------------------------------------------------------\r
- logger = new Log();\r
- timers = new Timers();\r
- vdr = new VDR();\r
-\r
- mtd = new Mtd_TYPE();\r
- remote = new Remote_TYPE();\r
- led = new Led_TYPE();\r
- osd = new Osd_TYPE();\r
- audio = new Audio_TYPE();\r
- video = new Video_TYPE();\r
-\r
-\r
-\r
- boxstack = new BoxStack();\r
- command = new Command();\r
- wol = new Wol();\r
- sleeptimer = new Sleeptimer();\r
-\r
- if (!logger || !remote || !mtd || !led || !osd || !video || !audio || !boxstack || !command || !wol || !sleeptimer)\r
- {\r
- printf("Could not create objects. Memory problems?\n");\r
- shutdown(1);\r
- }\r
-\r
- // Get logging module started --------------------------------------------------------------------------------------\r
-\r
- if (!logger->init(Log::DEBUG, "dummy", debugEnabled ? 1 : 0))\r
- {\r
- printf("Could not initialise log object. Aborting.\n");\r
- shutdown(1);\r
- }\r
-\r
- logger->log("Core", Log::INFO, "Starting up...");\r
-\r
- // Daemonize if not -d\r
-\r
- if (daemonize)\r
- {\r
- // Fork away\r
- pid_t forkTest = fork();\r
- if (forkTest == -1)\r
- { printf("Cannot fork (1).\n"); exit(1); }\r
- if (forkTest != 0) _exit(0); // PID returned, I am the parent\r
- // otherwise, I am the child\r
- setsid();\r
- forkTest = fork();\r
- if (forkTest == -1)\r
- { printf("Cannot fork (2).\n"); exit(1); }\r
- if (forkTest != 0) _exit(0); // PID returned, I am the parent\r
- // otherwise, I am the child\r
- close(0);\r
- close(1);\r
- close(2);\r
- }\r
-\r
- // Set up signal handling ------------------------------------------------------------------------------------------\r
-\r
- sighandler_t sigtest;\r
-\r
- sigtest = signal(SIGPIPE, SIG_IGN);\r
- if (sigtest == SIG_ERR)\r
- {\r
- logger->log("Core", Log::EMERG, "Could not set up signal handler for SIGPIPE. Aborting.");\r
- shutdown(1);\r
- }\r
- sigtest = signal(SIGINT, sighandler);\r
- if (sigtest == SIG_ERR)\r
- {\r
- logger->log("Core", Log::EMERG, "Could not set up signal handler for SIGINT. Aborting.");\r
- shutdown(1);\r
- }\r
- sigtest = signal(SIGTERM, sighandler);\r
- if (sigtest == SIG_ERR)\r
- {\r
- logger->log("Core", Log::EMERG, "Could not set up signal handler for SIGTERM. Aborting.");\r
- shutdown(1);\r
- }\r
- sigtest = signal(SIGUSR1, sighandler);\r
- if (sigtest == SIG_ERR)\r
- {\r
- logger->log("Core", Log::EMERG, "Could not set up signal handler for SIGUSR1. Aborting.");\r
- shutdown(1);\r
- }\r
-/*\r
- sigtest = signal(SIGUSR2, sighandler);\r
- if (sigtest == SIG_ERR)\r
- {\r
- logger->log("Core", Log::EMERG, "Could not set up signal handler for SIGUSR2. Aborting.");\r
- shutdown(1);\r
- }\r
-*/\r
- sigtest = signal(SIGURG, sighandler);\r
- if (sigtest == SIG_ERR)\r
- {\r
- logger->log("Core", Log::EMERG, "Could not set up signal handler for SIGURG. Aborting.");\r
- shutdown(1);\r
- }\r
-\r
- logger->log("Core", Log::INFO, "Signal handlers set up successfully");\r
-\r
-#ifdef HANDLE_VT_SWITCHING\r
- if ((fdtty = open("/dev/tty", O_WRONLY),0) == -1) {\r
- logger->log("Core", Log::EMERG, "Could not open /dev/tty. Please change permissions");\r
- } else {\r
- int free_vt;\r
- if (ioctl(fdtty,VT_OPENQRY,&free_vt)==-1 || free_vt==-1){\r
- logger->log("Core", Log::EMERG, "Could not retrieve free virtual console, please change permissions");\r
- } else {\r
- ioctl(fdtty,VT_ACTIVATE,free_vt);\r
- ioctl(fdtty,VT_WAITACTIVE,free_vt);\r
- ioctl(fdtty, VT_LOCKSWITCH, 1);\r
- }\r
- }\r
-\r
-#endif\r
-\r
- // Init modules ----------------------------------------------------------------------------------------------------\r
- int success;\r
-\r
- success = remote->init(RemoteStartDev);\r
-\r
- if (success)\r
- {\r
- logger->log("Core", Log::INFO, "Remote module initialised");\r
- }\r
- else\r
- {\r
- logger->log("Core", Log::EMERG, "Remote module failed to initialise");\r
- shutdown(1);\r
- }\r
-#ifdef VOMP_PLATTFORM_MVP\r
- success = led->init(((RemoteMVP*)remote)->getDevice());\r
-#else\r
- success = led->init(-1);\r
-#endif\r
- if (success)\r
- {\r
- logger->log("Core", Log::INFO, "LED module initialised");\r
- }\r
- else\r
- {\r
- logger->log("Core", Log::EMERG, "LED module failed to initialise");\r
- shutdown(1);\r
- }\r
-\r
- success = mtd->init();\r
- if (success)\r
- {\r
- logger->log("Core", Log::INFO, "Mtd module initialised");\r
- }\r
- else\r
- {\r
- logger->log("Core", Log::EMERG, "Mtd module failed to initialise");\r
- shutdown(1);\r
- }\r
-\r
- success = timers->init();\r
- if (success)\r
- {\r
- logger->log("Core", Log::INFO, "Timers module initialised");\r
- }\r
- else\r
- {\r
- logger->log("Core", Log::EMERG, "Timers module failed to initialise");\r
- shutdown(1);\r
- }\r
-\r
- UCHAR videoFormat = (UCHAR)mtd->getPALorNTSC();\r
- if (videoFormat == Video::PAL) logger->log("Core", Log::INFO, "Read from MTD: PAL 720x576");\r
- else if (videoFormat == Video::NTSC) logger->log("Core", Log::INFO, "Read from MTD: NTSC 720x480");\r
- else logger->log("Core", Log::INFO, "No help from MTD. Assuming NTSC 720x480");\r
-\r
- success = video->init(videoFormat);\r
- if (success)\r
- {\r
- logger->log("Core", Log::INFO, "Video module initialised");\r
- }\r
- else\r
- {\r
- logger->log("Core", Log::EMERG, "Video module failed to initialise");\r
- shutdown(1);\r
- }\r
-\r
- success = osd->init((void*)OsdStartDev);\r
- if (success)\r
- {\r
- logger->log("Core", Log::INFO, "OSD module initialised");\r
- }\r
- else\r
- {\r
- logger->log("Core", Log::EMERG, "OSD module failed to initialise");\r
- shutdown(1);\r
- }\r
-\r
- success = audio->init(Audio::MPEG2_PES);\r
- if (success)\r
- {\r
- logger->log("Core", Log::INFO, "Audio module initialised");\r
- }\r
- else\r
- {\r
- logger->log("Core", Log::EMERG, "Audio module failed to initialise");\r
- shutdown(1);\r
- }\r
-\r
- success = vdr->init(3024);\r
- if (success)\r
- {\r
- logger->log("Core", Log::INFO, "VDR module initialised");\r
- }\r
- else\r
- {\r
- logger->log("Core", Log::EMERG, "VDR module failed to initialise");\r
- shutdown(1);\r
- }\r
-\r
- success = boxstack->init();\r
- if (success)\r
- {\r
- logger->log("Core", Log::INFO, "BoxStack module initialised");\r
- }\r
- else\r
- {\r
- logger->log("Core", Log::EMERG, "BoxStack module failed to initialise");\r
- shutdown(1);\r
- }\r
-\r
- success = command->init(crashed, setServer);\r
- if (success)\r
- {\r
- logger->log("Core", Log::INFO, "Command module initialised");\r
- }\r
- else\r
- {\r
- logger->log("Core", Log::EMERG, "Command module failed to initialise");\r
- shutdown(1);\r
- }\r
-\r
- // Other init ------------------------------------------------------------------------------------------------------\r
-\r
- logger->log("Core", Log::NOTICE, "Startup successful");\r
-\r
- // Run main loop ---------------------------------------------------------------------------------------------------\r
-\r
- // Ok, all major device components and other bits are loaded and ready\r
- command->run();\r
-\r
- // When that returns quit ------------------------------------------------------------------------------------------\r
-\r
- shutdown(0);\r
- return 0;\r
-}\r
-\r
-// -------------------------------------------------------------------------------------------------------------------\r
-\r
-void sighandler(int signalReceived)\r
-{\r
- logger->log("Core", Log::NOTICE, "Signal %i received", signalReceived);\r
-\r
- switch (signalReceived)\r
- {\r
- case SIGINT:\r
- {\r
- logger->log("Core", Log::NOTICE, "Interrupt signal, shutting down...");\r
- command->stop(); // FIXME this is probably not safe - use the messaging system / is that even safe?\r
- break;\r
- }\r
- case SIGTERM:\r
- {\r
- logger->log("Core", Log::NOTICE, "Term signal, shutting down...");\r
- command->stop(); // FIXME this is probably not safe - use the messaging system / is that even safe?\r
- break;\r
- }\r
- case SIGUSR1:\r
- {\r
- command->sig1();\r
- break;\r
- }\r
-/*\r
- case SIGUSR1:\r
- {\r
- logger->log("Core", Log::DEBUG, "SIGUSR1 caught");\r
- logger->upLogLevel();\r
- break;\r
- }\r
- case SIGUSR2:\r
- {\r
- logger->log("Core", Log::DEBUG, "SIGUSR2 caught");\r
- logger->downLogLevel();\r
- break;\r
- }\r
-*/\r
- case SIGURG:\r
- {\r
- logger->log("Core", Log::DEBUG, "SIGURG caught");\r
- break;\r
- }\r
- }\r
-}\r
-#endif\r
-\r
-// -------------------------------------------------------------------------------------------------------------------\r
-\r
-void shutdown(int code)\r
-{\r
- if (boxstack)\r
- {\r
- boxstack->shutdown();\r
- delete boxstack;\r
- logger->log("Core", Log::NOTICE, "BoxStack module shut down");\r
- }\r
-\r
- // FIXME, send a del all to boxstack first, then get rid of it after command?\r
- if (command) // shut down command here in case views have posted messages\r
- {\r
- command->shutdown();\r
- delete command;\r
- logger->log("Core", Log::NOTICE, "Command module shut down");\r
- }\r
-\r
- if (vdr)\r
- {\r
- vdr->shutdown();\r
- delete vdr;\r
- logger->log("Core", Log::NOTICE, "VDR module shut down");\r
- }\r
-\r
- if (osd)\r
- {\r
- osd->shutdown();\r
- delete osd;\r
- logger->log("Core", Log::NOTICE, "OSD module shut down");\r
- }\r
-\r
- if (audio)\r
- {\r
- audio->shutdown();\r
- delete audio;\r
- logger->log("Core", Log::NOTICE, "Audio module shut down");\r
- }\r
-\r
- if (video)\r
- {\r
- video->shutdown();\r
- delete video;\r
- logger->log("Core", Log::NOTICE, "Video module shut down");\r
- }\r
-\r
- if (timers)\r
- {\r
- timers->shutdown();\r
- delete timers;\r
- logger->log("Core", Log::NOTICE, "Timers module shut down");\r
- }\r
-\r
- if (mtd)\r
- {\r
- mtd->shutdown();\r
- delete mtd;\r
- logger->log("Core", Log::NOTICE, "MTD module shut down");\r
- }\r
-\r
- if (led)\r
- {\r
- led->shutdown();\r
- delete led;\r
- logger->log("Core", Log::NOTICE, "LED module shut down");\r
- }\r
-\r
- if (remote)\r
- {\r
- remote->shutdown();\r
- delete remote;\r
- logger->log("Core", Log::NOTICE, "Remote module shut down");\r
- }\r
-\r
- if (wol)\r
- {\r
- delete wol;\r
- logger->log("Core", Log::NOTICE, "WOL module shut down");\r
- }\r
-\r
- if (sleeptimer)\r
- {\r
- delete sleeptimer;\r
- logger->log("Core", Log::NOTICE, "Sleeptimer module shut down");\r
- }\r
-#ifdef HANDLE_VT_SWITCHING\r
- ioctl(fdtty, VT_UNLOCKSWITCH, 1);\r
- close(fdtty);\r
-#endif\r
-\r
- if (logger)\r
- {\r
- logger->log("Core", Log::NOTICE, "Log module shutting down... bye!\n\n");\r
- logger->shutdown();\r
- delete logger;\r
- }\r
-\r
- exit(code);\r
-}\r
-\r
-// -------------------------------------------------------------------------------------------------------------------\r
-\r
-ULLONG htonll(ULLONG a)\r
-{\r
- #if BYTE_ORDER == BIG_ENDIAN\r
- return a;\r
- #else\r
- ULLONG b = 0;\r
-\r
- b = ((a << 56) & 0xFF00000000000000ULL)\r
- | ((a << 40) & 0x00FF000000000000ULL)\r
- | ((a << 24) & 0x0000FF0000000000ULL)\r
- | ((a << 8) & 0x000000FF00000000ULL)\r
- | ((a >> 8) & 0x00000000FF000000ULL)\r
- | ((a >> 24) & 0x0000000000FF0000ULL)\r
- | ((a >> 40) & 0x000000000000FF00ULL)\r
- | ((a >> 56) & 0x00000000000000FFULL) ;\r
-\r
- return b;\r
- #endif\r
-}\r
-\r
-ULLONG ntohll(ULLONG a)\r
-{\r
- return htonll(a);\r
-}\r
-\r
-void MILLISLEEP(ULONG a)\r
-{\r
-#ifndef WIN32\r
- struct timespec delayTime;\r
- delayTime.tv_sec = a / 1000;\r
- delayTime.tv_nsec = (a % 1000) * 1000000;\r
- nanosleep(&delayTime, NULL);\r
-#else\r
- Sleep(a);\r
-#endif\r
-}\r
-\r
-long long getTimeMS() {\r
- struct timespec ts;\r
- clock_gettime(VOMP_LINUX_CLOCK, &ts);\r
- return ts.tv_sec*1000+ts.tv_nsec/1000000LL;\r
-}\r
-\r
-int min(UINT a, int b)\r
-{\r
- if (a > b) return b;\r
- else return a;\r
-}\r
-\r
-int max(int a, int b)\r
-{\r
- if (a > b) return a;\r
- else return b;\r
-}\r
-\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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#ifndef WIN32
+#include <unistd.h>
+#include <endian.h>
+#endif
+
+#include "defines.h"
+
+#ifdef HANDLE_VT_SWITCHING
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <linux/vt.h>
+#endif
+#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 "osdopenvg.h"
+#include "audioomx.h"
+#include "videoomx.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;
+
+#ifdef HANDLE_VT_SWITCHING
+int fdtty;
+struct vt_mode old_vtmode;
+#endif
+
+// 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");
+
+#ifdef HANDLE_VT_SWITCHING
+ if ((fdtty = open("/dev/tty", O_WRONLY),0) == -1) {
+ logger->log("Core", Log::EMERG, "Could not open /dev/tty. Please change permissions");
+ } else {
+ int free_vt;
+ if (ioctl(fdtty,VT_OPENQRY,&free_vt)==-1 || free_vt==-1){
+ logger->log("Core", Log::EMERG, "Could not retrieve free virtual console, please change permissions");
+ } else {
+ ioctl(fdtty,VT_ACTIVATE,free_vt);
+ ioctl(fdtty,VT_WAITACTIVE,free_vt);
+ ioctl(fdtty, VT_LOCKSWITCH, 1);
+ }
+ }
+
+#endif
+
+ // 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");
+ }
+#ifdef HANDLE_VT_SWITCHING
+ ioctl(fdtty, VT_UNLOCKSWITCH, 1);
+ close(fdtty);
+#endif
+
+ 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
+}
+
+long long getTimeMS() {
+ struct timespec ts;
+ clock_gettime(VOMP_LINUX_CLOCK, &ts);
+ return ts.tv_sec*1000+ts.tv_nsec/1000000LL;
+}
+
+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;
+}
+
-/*\r
- Copyright 2004-2005 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef MESSAGE_H\r
-#define MESSAGE_H\r
-\r
-#include <stdio.h>\r
-\r
-class Message;\r
-#include "defines.h"\r
-\r
-// Usage of messages is more dubious now that the single master mutex lock\r
-// protects all gui actions. Reason(s) for usage:\r
-// 1. View A wants something to be done by View B *after* View A has been deleted\r
-// 2. A thread wants its object/view deleting *after* the thread has exited\r
-\r
-// Put a justification line after call to Message* m = new Message() line\r
-// So that the sources can be grepped for proper message useage\r
-\r
-class Message\r
-{\r
- public:\r
- Message();\r
-\r
- void* from;\r
- void* to;\r
- ULONG message;\r
- ULONG parameter;\r
- ULONG tag; // use this for identifying which object / question is being replied to\r
-\r
- const static ULONG QUESTION_YES = 1;\r
- const static ULONG CLOSE_ME = 2;\r
- const static ULONG PLAY_SELECTED_RECORDING = 3;\r
- const static ULONG DELETE_SELECTED_RECORDING = 4;\r
- const static ULONG SCREENSHOT = 5;\r
- const static ULONG CHANNEL_CHANGE = 6;\r
- const static ULONG RESUME_SELECTED_RECORDING = 7;\r
- const static ULONG STOP_PLAYBACK = 9;\r
- const static ULONG SERVER_SELECTED = 10;\r
- const static ULONG VDR_CONNECTED = 11;\r
- const static ULONG ADD_VIEW = 12;\r
- const static ULONG REDRAW_LANG = 14;\r
- const static ULONG EPG = 16;\r
- const static ULONG EPG_CLOSE = 17;\r
- const static ULONG CHANGED_OPTIONS = 18;\r
- const static ULONG CONNECTION_LOST = 19;\r
- const static ULONG MOVE_RECORDING = 20;\r
- const static ULONG UDP_BUTTON = 21;\r
- const static ULONG PLAYER_EVENT = 22;\r
- const static ULONG AUDIO_CHANGE_CHANNEL = 23;\r
- const static ULONG CHILD_CLOSE = 24;\r
- const static ULONG MOUSE_MOVE = 25;\r
- const static ULONG MOUSE_LBDOWN = 26;\r
- const static ULONG CHANGE_LANGUAGE = 27;\r
- const static ULONG LAST_VIEW_CLOSE = 28;\r
- const static ULONG CHANGED_REMOTECONTROL = 29;\r
- const static ULONG DELETE_SELECTED_TIMER = 30;\r
- const static ULONG CHANGED_DEVICEOPTIONS = 31;\r
- const static ULONG TELETEXTUPDATE = 32;\r
- const static ULONG TELETEXTUPDATEFIRSTLINE = 33;\r
- const static ULONG SUBTITLE_CHANGE_CHANNEL = 34;\r
- const static ULONG MOUSE_ANDROID_SCROLL = 35;\r
-};\r
-\r
-#endif\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.
+*/
+
+#ifndef MESSAGE_H
+#define MESSAGE_H
+
+#include <stdio.h>
+
+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
-OBJECTS1 = command.o tcp.o dsock.o thread.o timers.o i18n.o \\r
- message.o messagequeue.o udp.o wol.o audio.o video.o log.o mutex.o \\r
- vdr.o recman.o recording.o recinfo.o channel.o rectimer.o event.o \\r
- directory.o mark.o option.o \\r
- player.o playerradio.o vfeed.o afeed.o \\r
- demuxer.o demuxervdr.o demuxerts.o stream.o \\r
- region.o colour.o boxstack.o boxx.o tbboxx.o \\r
- vinfo.o vquestion.o vrecordinglist.o vrecording.o \\r
- vmute.o vvolume.o vtimerlist.o vtimeredit.o vrecordingmenu.o \\r
- vchannellist.o vwelcome.o vvideorec.o vepgsettimer.o \\r
- vchannelselect.o vserverselect.o vconnect.o vepg.o vrecmove.o \\r
- vradiorec.o vaudioselector.o vscreensaver.o vopts.o \\r
- wselectlist.o wjpeg.o wsymbol.o wbutton.o wtextbox.o \\r
- woptionpane.o woptionbox.o wremoteconfig.o wtabbar.o \\r
- fonts/helvB24.o fonts/helvB18.o \\r
- remote.o led.o mtd.o osd.o surface.o \\r
- media.o vpicturebanner.o \\r
- audioplayer.o demuxeraudio.o abstractoption.o \\r
- eventdispatcher.o vdrrequestpacket.o vdrresponsepacket.o \\r
- vvideolivetv.o vsleeptimer.o \\r
- playerlivetv.o playerliveradio.o \\r
- wprogressbar.o \\r
- bitmap.o dvbsubtitles.o \\r
- imagereader.o mediaoptions.o mediaplayer.o \\r
- serialize.o localmediafile.o playermedia.o \\r
- demuxermedia.o tfeed.o vteletextview.o teletextdecodervbiebu.o \\r
- teletxt/txtfont.o mediafile.o\r
-\r
+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 \
+ 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 \
+ serialize.o localmediafile.o playermedia.o \
+ demuxermedia.o tfeed.o vteletextview.o teletextdecodervbiebu.o \
+ teletxt/txtfont.o mediafile.o
+
-/*\r
- Copyright 2004-2005 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef OSD_H\r
-#define OSD_H\r
-\r
-#include <stdio.h>\r
-\r
-class Surface;\r
-\r
-class Osd\r
-{\r
- public:\r
- Osd();\r
- virtual ~Osd();\r
- static Osd* getInstance();\r
-\r
- virtual int init(void* device)=0;\r
- virtual int shutdown()=0;\r
- virtual int restore(){return 1;};\r
- virtual int stopUpdate() {return 1;};\r
-\r
- virtual Surface * createNewSurface()=0; // For Boxx\r
- virtual int charSet() {return 1;};\r
-\r
- bool isInitted() {return initted;};\r
-\r
- virtual int getFD()=0;\r
-\r
- virtual void screenShot(const char* fileName)=0;\r
-\r
- protected:\r
- static Osd* instance;\r
- int initted;\r
- Surface* screen;\r
- int fdOsd;\r
-};\r
-\r
-#endif\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.
+*/
+
+#ifndef OSD_H
+#define OSD_H
+
+#include <stdio.h>
+
+class Surface;
+
+class Osd
+{
+ public:
+ Osd();
+ virtual ~Osd();
+ static Osd* getInstance();
+
+ virtual int init(void* device)=0;
+ virtual int shutdown()=0;
+ virtual int restore(){return 1;};
+ virtual int stopUpdate() {return 1;};
+
+ virtual Surface * createNewSurface()=0; // For Boxx
+ virtual int charSet() {return 1;};
+
+ bool isInitted() {return initted;};
+
+ virtual int getFD()=0;
+
+ virtual void screenShot(const char* fileName)=0;
+
+ protected:
+ static Osd* instance;
+ int initted;
+ Surface* screen;
+ int fdOsd;
+};
+
+#endif
-/*\r
- Copyright 2004-2005 Chris Tallon, 2006,2011-2012 Marten Richter\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-\r
-#include "osdopengl.h"\r
-#include "mtd.h"\r
-#include "videoomx.h"\r
-#include "surfaceopengl.h"\r
-\r
-\r
-#include "message.h"\r
-#include "command.h"\r
-\r
-\r
-#define BACKBUFFER_WIDTH 1280\r
-#define BACKBUFFER_HEIGHT 720\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-OsdOpenGL::OsdOpenGL()\r
-{\r
- glmutex.Lock();\r
-\r
- external_driving=false;\r
-\r
- lastrendertime=getTimeMS();\r
- display_height=0;\r
- display_width=0;\r
- mode=0;\r
-\r
-#ifdef BENCHMARK_FPS\r
- last_benchmark_time=getTimeMS();\r
- num_benchmark_frames=0;\r
-#endif\r
-\r
- \r
-}\r
-\r
-OsdOpenGL::~OsdOpenGL()\r
-{\r
-\r
- if (initted) \r
- {\r
- threadStop();\r
- shutdown();\r
- }\r
-\r
-\r
- glmutex.Unlock();\r
-}\r
-\r
-int OsdOpenGL::getFD()\r
-{\r
- if (!initted) return 0;\r
- return fdOsd;\r
-}\r
-\r
-Surface * OsdOpenGL::createNewSurface() {\r
- return (Surface*)new SurfaceOpenGL();\r
-}\r
-\r
-int OsdOpenGL::init(void* device)\r
-{\r
- if (initted) return 0;\r
- Video* video = Video::getInstance();\r
- //window=*((HWND*)device);\r
- \r
- // May be this device specific part should go to a device specific child class\r
-\r
- //init broadcom chipset (Move to video?)\r
-\r
-\r
- //First get connection to egl\r
- egl_display=eglGetDisplay(EGL_DEFAULT_DISPLAY);\r
-\r
- if (egl_display==EGL_NO_DISPLAY) {\r
- Log::getInstance()->log("OSD", Log::WARN, "Could not get egl display! %x",eglGetError());\r
- glmutex.Unlock();\r
- return 0;\r
- }\r
-\r
-\r
-\r
- if (eglInitialize(egl_display, NULL, NULL)==EGL_FALSE) {\r
- Log::getInstance()->log("OSD", Log::WARN, "Initialising display failed! %x",eglGetError());\r
- glmutex.Unlock();\r
- return 0;\r
- }\r
-\r
- const char *query_str=eglQueryString(egl_display,EGL_CLIENT_APIS);\r
- if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);\r
- else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",eglGetError());\r
- query_str=eglQueryString(egl_display,EGL_EXTENSIONS);\r
- if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);\r
- else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",eglGetError());\r
-\r
- const EGLint attributs[]={\r
- EGL_RED_SIZE,8,EGL_GREEN_SIZE, 8,EGL_BLUE_SIZE, 8,EGL_ALPHA_SIZE, 8,\r
- EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT,\r
- EGL_CONFORMANT, EGL_OPENGL_ES2_BIT,\r
- EGL_NONE\r
- }; // Here, we might have to select the resolution!\r
-\r
-\r
- EGLint number;\r
-\r
- if (eglChooseConfig(egl_display, attributs, &egl_ourconfig, 1, &number)==EGL_FALSE) {\r
- Log::getInstance()->log("OSD", Log::WARN, "Choosing egl config failed! %x",eglGetError());\r
- glmutex.Unlock();\r
- return 0;\r
- }\r
-\r
- const EGLint attr_context[]={\r
- EGL_CONTEXT_CLIENT_VERSION,2,\r
- EGL_NONE\r
- };\r
-\r
- egl_context=eglCreateContext(egl_display,egl_ourconfig,EGL_NO_CONTEXT,attr_context);\r
- if (egl_context==EGL_NO_CONTEXT) {\r
- Log::getInstance()->log("OSD", Log::WARN, "Creating egl context failed! %x",eglGetError());\r
- glmutex.Unlock();\r
- return 0;\r
- }\r
-\r
- // warning broadcom specific, get display size!\r
- display_width=display_height=0;\r
- if (graphics_get_display_size(0, &display_width, &display_height)<0) {\r
- Log::getInstance()->log("OSD", Log::WARN, "Getting display size failed! (BCM API) ");\r
- glmutex.Unlock();\r
- return 0;\r
- }\r
- Log::getInstance()->log("OSD", Log::NOTICE, "Displaysize is %d x %d ",display_width, display_height);\r
- VC_RECT_T dst_rect ={0,0,display_width,display_height};\r
- VC_RECT_T src_rect={0,0,BACKBUFFER_WIDTH <<16,BACKBUFFER_HEIGHT<<16};\r
-\r
- DISPMANX_UPDATE_HANDLE_T bcm_update;\r
-\r
- bcm_display=vc_dispmanx_display_open(0);\r
- bcm_update=vc_dispmanx_update_start(0);\r
- bcm_element=vc_dispmanx_element_add(bcm_update,bcm_display,\r
- 2,&dst_rect, 0,\r
- &src_rect,DISPMANX_PROTECTION_NONE,0, 0, (DISPMANX_TRANSFORM_T) 0);\r
-\r
- vc_dispmanx_update_submit_sync(bcm_update);\r
- static EGL_DISPMANX_WINDOW_T nativewindow;\r
- nativewindow.element=bcm_element;\r
- nativewindow.height=BACKBUFFER_HEIGHT;\r
- nativewindow.width=BACKBUFFER_WIDTH;\r
-\r
- egl_surface = eglCreateWindowSurface(egl_display,egl_ourconfig, &nativewindow,NULL );\r
- if (egl_surface==EGL_NO_SURFACE) {\r
- Log::getInstance()->log("OSD", Log::WARN, "Creating egl window surface failed!");\r
- glmutex.Unlock();\r
- return 0;\r
- }\r
-\r
- if (eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context)== EGL_FALSE) {\r
- Log::getInstance()->log("OSD", Log::WARN, "Making egl Current failed");\r
- glmutex.Unlock();\r
- return 0;\r
- }\r
- // Test stuff\r
-\r
- query_str=(const char*)glGetString(GL_VERSION) ;\r
- if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);\r
- else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",glGetError());\r
-\r
- query_str=(const char*)glGetString(GL_VENDOR) ;\r
- if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);\r
- else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",glGetError());\r
-\r
- query_str=(const char*)glGetString(GL_RENDERER) ;\r
- if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);\r
- else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",glGetError());\r
-\r
- query_str=(const char*)glGetString(GL_EXTENSIONS) ;\r
- if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);\r
- else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",glGetError());\r
-\r
-\r
-\r
-\r
- //Now we will create the Screen\r
- screen = (Surface*) new SurfaceOpenGL(Surface::SCREEN);\r
-\r
- screen->create(video->getScreenWidth(), video->getScreenHeight());\r
- screen->display();\r
- initted = 1; // must set this here or create surface won't work\r
-\r
- //glGenBuffers(1, &vB);\r
- //glGenBuffers(1, &iB);\r
-\r
- //Preparing the Shaders\r
-\r
- if (!osd_shader.init()) {\r
- Log::getInstance()->log("OSD", Log::WARN, "Init Osd Shader failed");\r
- glmutex.Unlock();\r
- return 0;\r
- }\r
-\r
-\r
-\r
-\r
- glClearColor(0.0f,0.0f,0.0f,1.f);\r
- eglSwapInterval(egl_display, 1 );\r
-\r
- eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );\r
-\r
- if (((VideoOMX*)Video::getInstance())->initUsingOSDObjects()!=1) { //call Video for init opengl stuff\r
- return 0;\r
- }\r
-\r
-\r
- glmutex.Unlock();\r
- threadStart();\r
-\r
- return 1;\r
-}\r
- \r
-void OsdOpenGL::InitVertexBuffer(float scalex,float scaley)\r
-{\r
- Video* video=Video::getInstance();\r
- float texx=1.f;\r
- float texy=1.f;\r
- OSDCOLOR osdcolor={1.f,1.f,1.f,1.f};\r
-\r
- // osdvertices[0].c=osdcolor;\r
- osdvertices[0].x= (scalex);\r
- osdvertices[0].y=-scaley;\r
- osdvertices[0].z=0.5;\r
- osdvertices[0].u=texx;\r
- osdvertices[0].v=texy;\r
- // osdvertices[1].c=osdcolor;\r
- osdvertices[1].x=(scalex);\r
- osdvertices[1].y=(scaley);\r
- osdvertices[1].z=0.5f;\r
- osdvertices[1].u=texx;\r
- osdvertices[1].v=0.f;\r
- // osdvertices[0].c=osdcolor;\r
- osdvertices[2].x=(-scalex);\r
- osdvertices[2].y=-scaley;\r
- osdvertices[2].z=0.5f;\r
- osdvertices[2].u=0.f;\r
- osdvertices[2].v=texy;\r
- // osdvertices[3].c=osdcolor;\r
- osdvertices[3].x=-scalex;\r
- osdvertices[3].y=(scaley);\r
- osdvertices[3].z=0.5f;\r
- osdvertices[3].u=0.f;\r
- osdvertices[3].v=0.f;\r
- \r
- osdindices[0]=0;\r
- osdindices[1]=1;\r
- osdindices[2]=2;\r
- osdindices[3]=0;\r
- osdindices[4]=2;\r
- osdindices[5]=3;\r
-\r
- return;\r
-}\r
-\r
-int OsdOpenGL::shutdown()\r
-{\r
- if (!initted) return 0;\r
- glmutex.Lock();\r
- initted = 0;\r
- threadStop();\r
- delete screen;\r
- screen=NULL;\r
-\r
- (((VideoOMX*)Video::getInstance())->shutdownUsingOSDObjects());\r
-\r
-\r
- osd_shader.deinit();\r
-\r
- glClear(GL_COLOR_BUFFER_BIT);\r
- eglSwapBuffers(egl_display, egl_surface);\r
- eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );\r
- eglDestroySurface(egl_display,egl_surface);\r
- eglDestroyContext(egl_display,egl_context);\r
- eglTerminate(egl_display );\r
-\r
- DISPMANX_UPDATE_HANDLE_T bcm_update;\r
- bcm_update=vc_dispmanx_update_start(0);\r
-\r
- vc_dispmanx_element_remove(bcm_update,bcm_element);\r
- vc_dispmanx_update_submit_sync(bcm_update);\r
- vc_dispmanx_display_close(bcm_display);\r
-\r
-\r
- return 1;\r
-}\r
-\r
-void OsdOpenGL::screenShot(const char* fileName)\r
-{\r
- BeginPainting();\r
- screen->screenShot(fileName);\r
- EndPainting();\r
-}\r
-\r
-void OsdOpenGL::threadMethod()\r
-{\r
- // We have to claim the gl context for this thread\r
- //glmutex.Lock();\r
-\r
- //glmutex.Unlock();\r
- int ts=0;\r
- while (true)\r
- {\r
- ts=10;\r
- unsigned int waittime=10;\r
-\r
- if (initted) {\r
-\r
- long long time1 = getTimeMS();\r
- if ((time1 - lastrendertime) > 200) {//5 fps for OSD updates are enough, avoids tearing\r
- InternalRendering();\r
- lastrendertime = getTimeMS();\r
-\r
- }\r
- }\r
-\r
- threadCheckExit();\r
- if (ts!=0) {\r
- struct timespec target_time;\r
- clock_gettime(CLOCK_REALTIME,&target_time);\r
- target_time.tv_nsec+=1000000LL*ts;\r
- if (target_time.tv_nsec>999999999) {\r
- target_time.tv_nsec-=1000000000L;\r
- target_time.tv_sec+=1;\r
- }\r
- threadWaitForSignalTimed(&target_time);\r
- }\r
- //Sleep(1);\r
- }\r
- //eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );\r
-}\r
-\r
-\r
-void OsdOpenGL::threadPostStopCleanup()\r
-{\r
- //Doing nothing\r
- //goo;\r
-}\r
-\r
-\r
-\r
-void OsdOpenGL::InternalRendering(){\r
-\r
- BeginPainting();\r
-\r
-\r
-\r
-\r
- //InitVertexBuffer(display_width,display_height);\r
- InitVertexBuffer(1.f,1.f);\r
-\r
-\r
- glViewport(0, 0, BACKBUFFER_WIDTH ,BACKBUFFER_HEIGHT);\r
-\r
- glClearColor(0.0f,0.0f,0.0f,1.f);\r
- glClear(GL_COLOR_BUFFER_BIT);\r
-\r
- osd_shader.PrepareRendering(((SurfaceOpenGL*)screen)->getTexture());\r
-\r
- glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,sizeof(OSDVERTEX), osdvertices);\r
- glEnableVertexAttribArray(0);\r
- glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE,sizeof(OSDVERTEX), &(osdvertices[0].u));\r
- glEnableVertexAttribArray(1);\r
-\r
-\r
-\r
- glDisable(GL_BLEND);\r
-\r
-\r
-\r
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);\r
-\r
-\r
-\r
- //Show it to the user!\r
- eglSwapBuffers(egl_display, egl_surface);\r
-\r
- EndPainting();\r
-\r
-#ifdef BENCHMARK_FPS\r
- num_benchmark_frames++;\r
- if (getTimeMS()-last_benchmark_time>4000) {\r
- float fps=1000./(float)(getTimeMS()-last_benchmark_time);\r
- fps*=((float)num_benchmark_frames);\r
- num_benchmark_frames=0;\r
- Log::getInstance()->log("OSD", Log::NOTICE, "Current FPS %g", fps);\r
- last_benchmark_time=getTimeMS();\r
-\r
- }\r
-\r
-#endif\r
-\r
- \r
-\r
-}\r
-\r
-\r
-\r
-\r
-void OsdOpenGL::BeginPainting() {//We synchronize calls to d3d between different threads\r
- glmutex.Lock();\r
- if (initted) {\r
- if (eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context)== EGL_FALSE) {\r
- Log::getInstance()->log("OSD", Log::WARN, "Making egl Current failed in thread %d",eglGetError());\r
- return;\r
- }\r
- }\r
-}\r
-\r
-void OsdOpenGL::EndPainting() {\r
- eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );\r
- glmutex.Unlock();\r
-}\r
-\r
-\r
-\r
-void OsdOpenGL::Blank() {\r
- BeginPainting();\r
- glClearColor(0.15f, 1.f, 0.35f, 1.0f); // change this to black after testing\r
- glClear( GL_COLOR_BUFFER_BIT );\r
- glClear( GL_DEPTH_BUFFER_BIT );\r
- EndPainting();\r
-}\r
+/*
+ Copyright 2004-2005 Chris Tallon, 2006,2011-2012 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 "osdopengl.h"
+#include "mtd.h"
+#include "videoomx.h"
+#include "surfaceopengl.h"
+
+
+#include "message.h"
+#include "command.h"
+
+
+#define BACKBUFFER_WIDTH 1280
+#define BACKBUFFER_HEIGHT 720
+
+
+
+
+
+
+
+OsdOpenGL::OsdOpenGL()
+{
+ glmutex.Lock();
+
+ external_driving=false;
+
+ lastrendertime=getTimeMS();
+ display_height=0;
+ display_width=0;
+ mode=0;
+
+#ifdef BENCHMARK_FPS
+ last_benchmark_time=getTimeMS();
+ num_benchmark_frames=0;
+#endif
+
+
+}
+
+OsdOpenGL::~OsdOpenGL()
+{
+
+ if (initted)
+ {
+ threadStop();
+ shutdown();
+ }
+
+
+ glmutex.Unlock();
+}
+
+int OsdOpenGL::getFD()
+{
+ if (!initted) return 0;
+ return fdOsd;
+}
+
+Surface * OsdOpenGL::createNewSurface() {
+ return (Surface*)new SurfaceOpenGL();
+}
+
+int OsdOpenGL::init(void* device)
+{
+ if (initted) return 0;
+ Video* video = Video::getInstance();
+ //window=*((HWND*)device);
+
+ // May be this device specific part should go to a device specific child class
+
+ //init broadcom chipset (Move to video?)
+
+
+ //First get connection to egl
+ egl_display=eglGetDisplay(EGL_DEFAULT_DISPLAY);
+
+ if (egl_display==EGL_NO_DISPLAY) {
+ Log::getInstance()->log("OSD", Log::WARN, "Could not get egl display! %x",eglGetError());
+ glmutex.Unlock();
+ return 0;
+ }
+
+
+
+ if (eglInitialize(egl_display, NULL, NULL)==EGL_FALSE) {
+ Log::getInstance()->log("OSD", Log::WARN, "Initialising display failed! %x",eglGetError());
+ glmutex.Unlock();
+ return 0;
+ }
+
+ const char *query_str=eglQueryString(egl_display,EGL_CLIENT_APIS);
+ if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);
+ else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",eglGetError());
+ query_str=eglQueryString(egl_display,EGL_EXTENSIONS);
+ if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);
+ else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",eglGetError());
+
+ const EGLint attributs[]={
+ EGL_RED_SIZE,8,EGL_GREEN_SIZE, 8,EGL_BLUE_SIZE, 8,EGL_ALPHA_SIZE, 8,
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT,
+ EGL_CONFORMANT, EGL_OPENGL_ES2_BIT,
+ EGL_NONE
+ }; // Here, we might have to select the resolution!
+
+
+ EGLint number;
+
+ if (eglChooseConfig(egl_display, attributs, &egl_ourconfig, 1, &number)==EGL_FALSE) {
+ Log::getInstance()->log("OSD", Log::WARN, "Choosing egl config failed! %x",eglGetError());
+ glmutex.Unlock();
+ return 0;
+ }
+
+ const EGLint attr_context[]={
+ EGL_CONTEXT_CLIENT_VERSION,2,
+ EGL_NONE
+ };
+
+ egl_context=eglCreateContext(egl_display,egl_ourconfig,EGL_NO_CONTEXT,attr_context);
+ if (egl_context==EGL_NO_CONTEXT) {
+ Log::getInstance()->log("OSD", Log::WARN, "Creating egl context failed! %x",eglGetError());
+ glmutex.Unlock();
+ return 0;
+ }
+
+ // warning broadcom specific, get display size!
+ display_width=display_height=0;
+ if (graphics_get_display_size(0, &display_width, &display_height)<0) {
+ Log::getInstance()->log("OSD", Log::WARN, "Getting display size failed! (BCM API) ");
+ glmutex.Unlock();
+ return 0;
+ }
+ Log::getInstance()->log("OSD", Log::NOTICE, "Displaysize is %d x %d ",display_width, display_height);
+ VC_RECT_T dst_rect ={0,0,display_width,display_height};
+ VC_RECT_T src_rect={0,0,BACKBUFFER_WIDTH <<16,BACKBUFFER_HEIGHT<<16};
+
+ DISPMANX_UPDATE_HANDLE_T bcm_update;
+
+ bcm_display=vc_dispmanx_display_open(0);
+ bcm_update=vc_dispmanx_update_start(0);
+ bcm_element=vc_dispmanx_element_add(bcm_update,bcm_display,
+ 2,&dst_rect, 0,
+ &src_rect,DISPMANX_PROTECTION_NONE,0, 0, (DISPMANX_TRANSFORM_T) 0);
+
+ vc_dispmanx_update_submit_sync(bcm_update);
+ static EGL_DISPMANX_WINDOW_T nativewindow;
+ nativewindow.element=bcm_element;
+ nativewindow.height=BACKBUFFER_HEIGHT;
+ nativewindow.width=BACKBUFFER_WIDTH;
+
+ egl_surface = eglCreateWindowSurface(egl_display,egl_ourconfig, &nativewindow,NULL );
+ if (egl_surface==EGL_NO_SURFACE) {
+ Log::getInstance()->log("OSD", Log::WARN, "Creating egl window surface failed!");
+ glmutex.Unlock();
+ return 0;
+ }
+
+ if (eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context)== EGL_FALSE) {
+ Log::getInstance()->log("OSD", Log::WARN, "Making egl Current failed");
+ glmutex.Unlock();
+ return 0;
+ }
+ // Test stuff
+
+ query_str=(const char*)glGetString(GL_VERSION) ;
+ if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);
+ else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",glGetError());
+
+ query_str=(const char*)glGetString(GL_VENDOR) ;
+ if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);
+ else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",glGetError());
+
+ query_str=(const char*)glGetString(GL_RENDERER) ;
+ if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);
+ else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",glGetError());
+
+ query_str=(const char*)glGetString(GL_EXTENSIONS) ;
+ if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);
+ else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",glGetError());
+
+
+
+
+ //Now we will create the Screen
+ screen = (Surface*) new SurfaceOpenGL(Surface::SCREEN);
+
+ screen->create(video->getScreenWidth(), video->getScreenHeight());
+ screen->display();
+ initted = 1; // must set this here or create surface won't work
+
+ //glGenBuffers(1, &vB);
+ //glGenBuffers(1, &iB);
+
+ //Preparing the Shaders
+
+ if (!osd_shader.init()) {
+ Log::getInstance()->log("OSD", Log::WARN, "Init Osd Shader failed");
+ glmutex.Unlock();
+ return 0;
+ }
+
+
+
+
+ glClearColor(0.0f,0.0f,0.0f,1.f);
+ eglSwapInterval(egl_display, 1 );
+
+ eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );
+
+ if (((VideoOMX*)Video::getInstance())->initUsingOSDObjects()!=1) { //call Video for init opengl stuff
+ return 0;
+ }
+
+
+ glmutex.Unlock();
+ threadStart();
+
+ return 1;
+}
+
+void OsdOpenGL::InitVertexBuffer(float scalex,float scaley)
+{
+ Video* video=Video::getInstance();
+ float texx=1.f;
+ float texy=1.f;
+ OSDCOLOR osdcolor={1.f,1.f,1.f,1.f};
+
+ // osdvertices[0].c=osdcolor;
+ osdvertices[0].x= (scalex);
+ osdvertices[0].y=-scaley;
+ osdvertices[0].z=0.5;
+ osdvertices[0].u=texx;
+ osdvertices[0].v=texy;
+ // osdvertices[1].c=osdcolor;
+ osdvertices[1].x=(scalex);
+ osdvertices[1].y=(scaley);
+ osdvertices[1].z=0.5f;
+ osdvertices[1].u=texx;
+ osdvertices[1].v=0.f;
+ // osdvertices[0].c=osdcolor;
+ osdvertices[2].x=(-scalex);
+ osdvertices[2].y=-scaley;
+ osdvertices[2].z=0.5f;
+ osdvertices[2].u=0.f;
+ osdvertices[2].v=texy;
+ // osdvertices[3].c=osdcolor;
+ osdvertices[3].x=-scalex;
+ osdvertices[3].y=(scaley);
+ osdvertices[3].z=0.5f;
+ osdvertices[3].u=0.f;
+ osdvertices[3].v=0.f;
+
+ osdindices[0]=0;
+ osdindices[1]=1;
+ osdindices[2]=2;
+ osdindices[3]=0;
+ osdindices[4]=2;
+ osdindices[5]=3;
+
+ return;
+}
+
+int OsdOpenGL::shutdown()
+{
+ if (!initted) return 0;
+ glmutex.Lock();
+ initted = 0;
+ threadStop();
+ delete screen;
+ screen=NULL;
+
+ (((VideoOMX*)Video::getInstance())->shutdownUsingOSDObjects());
+
+
+ osd_shader.deinit();
+
+ glClear(GL_COLOR_BUFFER_BIT);
+ eglSwapBuffers(egl_display, egl_surface);
+ eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );
+ eglDestroySurface(egl_display,egl_surface);
+ eglDestroyContext(egl_display,egl_context);
+ eglTerminate(egl_display );
+
+ DISPMANX_UPDATE_HANDLE_T bcm_update;
+ bcm_update=vc_dispmanx_update_start(0);
+
+ vc_dispmanx_element_remove(bcm_update,bcm_element);
+ vc_dispmanx_update_submit_sync(bcm_update);
+ vc_dispmanx_display_close(bcm_display);
+
+
+ return 1;
+}
+
+void OsdOpenGL::screenShot(const char* fileName)
+{
+ BeginPainting();
+ screen->screenShot(fileName);
+ EndPainting();
+}
+
+void OsdOpenGL::threadMethod()
+{
+ // We have to claim the gl context for this thread
+ //glmutex.Lock();
+
+ //glmutex.Unlock();
+ int ts=0;
+ while (true)
+ {
+ ts=10;
+ unsigned int waittime=10;
+
+ if (initted) {
+
+ long long time1 = getTimeMS();
+ if ((time1 - lastrendertime) > 200) {//5 fps for OSD updates are enough, avoids tearing
+ InternalRendering();
+ lastrendertime = getTimeMS();
+
+ }
+ }
+
+ threadCheckExit();
+ if (ts!=0) {
+ struct timespec target_time;
+ clock_gettime(CLOCK_REALTIME,&target_time);
+ target_time.tv_nsec+=1000000LL*ts;
+ if (target_time.tv_nsec>999999999) {
+ target_time.tv_nsec-=1000000000L;
+ target_time.tv_sec+=1;
+ }
+ threadWaitForSignalTimed(&target_time);
+ }
+ //Sleep(1);
+ }
+ //eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );
+}
+
+
+void OsdOpenGL::threadPostStopCleanup()
+{
+ //Doing nothing
+ //goo;
+}
+
+
+
+void OsdOpenGL::InternalRendering(){
+
+ BeginPainting();
+
+
+
+
+ //InitVertexBuffer(display_width,display_height);
+ InitVertexBuffer(1.f,1.f);
+
+
+ glViewport(0, 0, BACKBUFFER_WIDTH ,BACKBUFFER_HEIGHT);
+
+ glClearColor(0.0f,0.0f,0.0f,1.f);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ osd_shader.PrepareRendering(((SurfaceOpenGL*)screen)->getTexture());
+
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,sizeof(OSDVERTEX), osdvertices);
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE,sizeof(OSDVERTEX), &(osdvertices[0].u));
+ glEnableVertexAttribArray(1);
+
+
+
+ glDisable(GL_BLEND);
+
+
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+
+
+ //Show it to the user!
+ eglSwapBuffers(egl_display, egl_surface);
+
+ EndPainting();
+
+#ifdef BENCHMARK_FPS
+ num_benchmark_frames++;
+ if (getTimeMS()-last_benchmark_time>4000) {
+ float fps=1000./(float)(getTimeMS()-last_benchmark_time);
+ fps*=((float)num_benchmark_frames);
+ num_benchmark_frames=0;
+ Log::getInstance()->log("OSD", Log::NOTICE, "Current FPS %g", fps);
+ last_benchmark_time=getTimeMS();
+
+ }
+
+#endif
+
+
+
+}
+
+
+
+
+void OsdOpenGL::BeginPainting() {//We synchronize calls to d3d between different threads
+ glmutex.Lock();
+ if (initted) {
+ if (eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context)== EGL_FALSE) {
+ Log::getInstance()->log("OSD", Log::WARN, "Making egl Current failed in thread %d",eglGetError());
+ return;
+ }
+ }
+}
+
+void OsdOpenGL::EndPainting() {
+ eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );
+ glmutex.Unlock();
+}
+
+
+
+void OsdOpenGL::Blank() {
+ BeginPainting();
+ glClearColor(0.15f, 1.f, 0.35f, 1.0f); // change this to black after testing
+ glClear( GL_COLOR_BUFFER_BIT );
+ glClear( GL_DEPTH_BUFFER_BIT );
+ EndPainting();
+}
-/*\r
- Copyright 2004-2005 Chris Tallon, 2006,2011-2012 Marten Richter\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef OSDOPENGL_H\r
-#define OSDOPENGL_H\r
-\r
-#include <stdio.h>\r
-\r
-\r
-\r
-#include <GLES2/gl2.h>\r
-#include <EGL/egl.h>\r
-#include <EGL/eglext.h>\r
-\r
-#include "osd.h"\r
-#include "defines.h"\r
-#include "log.h"\r
-#include "threadp.h"\r
-#include "mutex.h"\r
-#include "videoomx.h"\r
-\r
-#include "glosdshader.h"\r
-\r
-\r
-\r
-\r
-\r
-\r
-struct OSDCOLOR{\r
- GLfloat r;\r
- GLfloat g;\r
- GLfloat b;\r
- GLfloat a;\r
-};\r
-\r
-\r
-struct OSDVERTEX {\r
- GLfloat x;\r
- GLfloat y;\r
- GLfloat z;\r
-/* OSDCOLOR c;*/\r
- GLfloat u;\r
- GLfloat v;\r
-};\r
-\r
-\r
-\r
-\r
-\r
-\r
-class OsdOpenGL : public Osd, public Thread_TYPE\r
-{\r
- public:\r
- OsdOpenGL();\r
- virtual ~OsdOpenGL();\r
-\r
- int init(void* device);\r
- int shutdown();\r
-\r
- int getFD();\r
-\r
- void screenShot(const char* fileName);\r
-\r
- Surface * createNewSurface();\r
-\r
-\r
- void BeginPainting();\r
- void EndPainting();\r
-\r
- void Blank();\r
-\r
- void getEGLObjs(EGLDisplay *i_egl_display,EGLSurface *i_egl_surface,EGLContext *i_egl_context, EGLConfig *i_egl_config){\r
- *i_egl_display=egl_display;\r
- *i_egl_surface=egl_surface;\r
- *i_egl_context=egl_context;\r
- *i_egl_config=egl_ourconfig;\r
- };\r
-\r
- EGLConfig getEGLConfig() {return egl_ourconfig;};\r
-\r
- void AdviseAboutNewFrame() {threadSignal();};\r
-\r
-\r
-\r
-\r
-private:\r
-\r
- //Maybe move the following stuff to a generic opengl object also for boosting DCT etc.\r
-\r
-\r
-\r
- void threadMethod();\r
- void threadPostStopCleanup();\r
-\r
- // This indicates, that currently a video is played, thus the osd updates are driven by the Videosystem\r
- bool external_driving;\r
- Mutex glmutex;\r
- long long lastrendertime;\r
- void InternalRendering();\r
- void InitVertexBuffer(float scalex,float scaley);\r
- OSDVERTEX osdvertices[4];\r
- GLubyte osdindices[6];\r
-\r
- GLOsdShader osd_shader;\r
-\r
-\r
-\r
-\r
- /* BCM specific */\r
-\r
- uint32_t display_height;\r
- uint32_t display_width;\r
- DISPMANX_DISPLAY_HANDLE_T bcm_display;\r
- DISPMANX_ELEMENT_HANDLE_T bcm_element;\r
-\r
- uint32_t mode;\r
-\r
-\r
- EGLDisplay egl_display;\r
- EGLSurface egl_surface;\r
- EGLContext egl_context;\r
- EGLConfig egl_ourconfig;\r
-#ifdef BENCHMARK_FPS\r
- long long last_benchmark_time;\r
- unsigned int num_benchmark_frames;\r
-#endif\r
-\r
-};\r
-\r
-#endif\r
+/*
+ Copyright 2004-2005 Chris Tallon, 2006,2011-2012 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 OSDOPENGL_H
+#define OSDOPENGL_H
+
+#include <stdio.h>
+
+
+
+#include <GLES2/gl2.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include "osd.h"
+#include "defines.h"
+#include "log.h"
+#include "threadp.h"
+#include "mutex.h"
+#include "videoomx.h"
+
+#include "glosdshader.h"
+
+
+
+
+
+
+struct OSDCOLOR{
+ GLfloat r;
+ GLfloat g;
+ GLfloat b;
+ GLfloat a;
+};
+
+
+struct OSDVERTEX {
+ GLfloat x;
+ GLfloat y;
+ GLfloat z;
+/* OSDCOLOR c;*/
+ GLfloat u;
+ GLfloat v;
+};
+
+
+
+
+
+
+class OsdOpenGL : public Osd, public Thread_TYPE
+{
+ public:
+ OsdOpenGL();
+ virtual ~OsdOpenGL();
+
+ int init(void* device);
+ int shutdown();
+
+ int getFD();
+
+ void screenShot(const char* fileName);
+
+ Surface * createNewSurface();
+
+
+ void BeginPainting();
+ void EndPainting();
+
+ void Blank();
+
+ void getEGLObjs(EGLDisplay *i_egl_display,EGLSurface *i_egl_surface,EGLContext *i_egl_context, EGLConfig *i_egl_config){
+ *i_egl_display=egl_display;
+ *i_egl_surface=egl_surface;
+ *i_egl_context=egl_context;
+ *i_egl_config=egl_ourconfig;
+ };
+
+ EGLConfig getEGLConfig() {return egl_ourconfig;};
+
+ void AdviseAboutNewFrame() {threadSignal();};
+
+
+
+
+private:
+
+ //Maybe move the following stuff to a generic opengl object also for boosting DCT etc.
+
+
+
+ void threadMethod();
+ void threadPostStopCleanup();
+
+ // This indicates, that currently a video is played, thus the osd updates are driven by the Videosystem
+ bool external_driving;
+ Mutex glmutex;
+ long long lastrendertime;
+ void InternalRendering();
+ void InitVertexBuffer(float scalex,float scaley);
+ OSDVERTEX osdvertices[4];
+ GLubyte osdindices[6];
+
+ GLOsdShader osd_shader;
+
+
+
+
+ /* BCM specific */
+
+ uint32_t display_height;
+ uint32_t display_width;
+ DISPMANX_DISPLAY_HANDLE_T bcm_display;
+ DISPMANX_ELEMENT_HANDLE_T bcm_element;
+
+ uint32_t mode;
+
+
+ EGLDisplay egl_display;
+ EGLSurface egl_surface;
+ EGLContext egl_context;
+ EGLConfig egl_ourconfig;
+#ifdef BENCHMARK_FPS
+ long long last_benchmark_time;
+ unsigned int num_benchmark_frames;
+#endif
+
+};
+
+#endif
-/*\r
- Copyright 2004-2005 Chris Tallon, 2006,2011-2012 Marten Richter\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-\r
-#include "osdopenvg.h"\r
-#include "mtd.h"\r
-#include "videoomx.h"\r
-#include "surface.h"\r
-#include <Magick++.h>\r
-\r
-#include "message.h"\r
-#include "command.h"\r
-#include "teletxt/txtfont.h"\r
-\r
-#include <sys/syscall.h>\r
-#include <vector>\r
-#include <math.h>\r
-\r
-using namespace Magick;\r
-\r
-extern uint8_t font_data[] asm("_binary_fonts_sourcesans_ttf_start");\r
-extern uint8_t font_data_end[] asm("_binary_fonts_sourcesans_ttf_end");\r
-extern uint8_t vdr_data[] asm("_binary_other_vdrhires_jpg_start");\r
-extern uint8_t vdr_data_end[] asm("_binary_other_vdrhires_jpg_end");\r
-extern uint8_t wallpaper_data[] asm("_binary_other_wallpaper720p_jpg_start");\r
-extern uint8_t wallpaper_data_end[] asm("_binary_other_wallpaper720p_jpg_end");\r
-\r
-\r
-#define BACKBUFFER_WIDTH 1280\r
-#define BACKBUFFER_HEIGHT 720\r
-\r
-\r
-OsdOpenVG::OsdOpenVG()\r
-{\r
- vgmutex.Lock();\r
- taskmutex.Lock();\r
- lastrendertime=getTimeMS();\r
- display_height=0;\r
- display_width=0;\r
- mode=0;\r
- aspect_correction=1.;\r
-\r
- freetype_inited=false;\r
- wait_id=1;\r
-\r
-}\r
-\r
-OsdOpenVG::~OsdOpenVG()\r
-{\r
-\r
- if (initted)\r
- {\r
- shutdown();\r
- }\r
-\r
- if (freetype_inited) FT_Done_Face(ft_face);\r
-\r
- vgmutex.Unlock();\r
- taskmutex.Unlock();\r
-}\r
-\r
-\r
-\r
-int OsdOpenVG::init(void* device)\r
-{\r
- if (initted) return 0;\r
- Video* video = Video::getInstance();\r
- //window=*((HWND*)device);\r
-\r
- // May be this device specific part should go to a device specific child class\r
-\r
- //init broadcom chipset (Move to video?)\r
-\r
-\r
- //First get connection to egl\r
- egl_display=eglGetDisplay(EGL_DEFAULT_DISPLAY);\r
-\r
- if (egl_display==EGL_NO_DISPLAY) {\r
- Log::getInstance()->log("OSD", Log::WARN, "Could not get egl display! %x",eglGetError());\r
- vgmutex.Unlock();\r
- return 0;\r
- }\r
-\r
-\r
-\r
- if (eglInitialize(egl_display, NULL, NULL)==EGL_FALSE) {\r
- Log::getInstance()->log("OSD", Log::WARN, "Initialising display failed! %x",eglGetError());\r
- vgmutex.Unlock();\r
- return 0;\r
- }\r
-\r
-\r
- const char *query_str=eglQueryString(egl_display,EGL_CLIENT_APIS);\r
- if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);\r
- else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",eglGetError());\r
- query_str=eglQueryString(egl_display,EGL_EXTENSIONS);\r
- if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);\r
- else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",eglGetError());\r
-\r
- if (eglBindAPI(EGL_OPENVG_API)==EGL_FALSE) {\r
- Log::getInstance()->log("OSD", Log::WARN, "Binding openvg api failed! %x",eglGetError());\r
- vgmutex.Unlock();\r
- return 0;\r
- }\r
-\r
- const EGLint attributs[]={\r
- EGL_RED_SIZE,8,EGL_GREEN_SIZE, 8,EGL_BLUE_SIZE, 8,EGL_ALPHA_SIZE, 8,\r
- EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT,\r
- EGL_CONFORMANT, EGL_OPENVG_BIT,\r
- EGL_NONE\r
- }; // Here, we might have to select the resolution!\r
-\r
-\r
- EGLint number;\r
-\r
- if (eglChooseConfig(egl_display, attributs, &egl_ourconfig, 1, &number)==EGL_FALSE) {\r
- Log::getInstance()->log("OSD", Log::WARN, "Choosing egl config failed! %x",eglGetError());\r
- vgmutex.Unlock();\r
- return 0;\r
- }\r
-\r
-\r
-\r
- egl_context=eglCreateContext(egl_display,egl_ourconfig,NULL,NULL);\r
- if (egl_context==EGL_NO_CONTEXT) {\r
- Log::getInstance()->log("OSD", Log::WARN, "Creating egl context failed! %x",eglGetError());\r
- vgmutex.Unlock();\r
- return 0;\r
- }\r
-\r
- // warning broadcom specific, get display size!\r
- display_width=display_height=0;\r
- if (graphics_get_display_size(0, &display_width, &display_height)<0) {\r
- Log::getInstance()->log("OSD", Log::WARN, "Getting display size failed! (BCM API) ");\r
- vgmutex.Unlock();\r
- return 0;\r
- }\r
- Log::getInstance()->log("OSD", Log::NOTICE, "Displaysize is %d x %d ",display_width, display_height);\r
- VC_RECT_T dst_rect ={0,0,display_width,display_height};\r
- VC_RECT_T src_rect={0,0,BACKBUFFER_WIDTH <<16,BACKBUFFER_HEIGHT<<16};\r
- // VC_RECT_T src_rect_bg={0,0,1<<16,1<<16};\r
- // VC_RECT_T src_rect_im={0,0,1,1};\r
-\r
- /* uint32_t back_image_ptr;\r
- bcm_backres=vc_dispmanx_resource_create(VC_IMAGE_RGB888,1,1,&back_image_ptr);\r
- unsigned int color=0x00FF0000;\r
- vc_dispmanx_resource_write_data(bcm_backres,VC_IMAGE_RGB888,4,&color,&src_rect_im);*/\r
-\r
- DISPMANX_UPDATE_HANDLE_T bcm_update;\r
- bcm_display=vc_dispmanx_display_open(0);\r
- bcm_update=vc_dispmanx_update_start(0);\r
- bcm_element=vc_dispmanx_element_add(bcm_update,bcm_display,\r
- 2,&dst_rect, 0,\r
- &src_rect,DISPMANX_PROTECTION_NONE,0, 0, (DISPMANX_TRANSFORM_T) 0);\r
-\r
-\r
- /* bcm_background=vc_dispmanx_element_add(bcm_update,bcm_display,\r
- 0,&dst_rect,bcm_backres ,\r
- &src_rect_bg,DISPMANX_PROTECTION_NONE,0, 0, (DISPMANX_TRANSFORM_T) 0);*/\r
-\r
- vc_dispmanx_update_submit_sync(bcm_update);\r
-\r
-\r
-\r
- static EGL_DISPMANX_WINDOW_T nativewindow;\r
- nativewindow.element=bcm_element;\r
- nativewindow.height=BACKBUFFER_HEIGHT;\r
- nativewindow.width=BACKBUFFER_WIDTH;\r
-\r
- egl_surface = eglCreateWindowSurface(egl_display,egl_ourconfig, &nativewindow,NULL );\r
- if (egl_surface==EGL_NO_SURFACE) {\r
- Log::getInstance()->log("OSD", Log::WARN, "Creating egl window surface failed!");\r
- vgmutex.Unlock();\r
- return 0;\r
- }\r
- Log::getInstance()->log("OSD", Log::DEBUG, "Making egl current in1%d",syscall(SYS_gettid));\r
- if (eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context)== EGL_FALSE) {\r
- Log::getInstance()->log("OSD", Log::WARN, "Making egl Current failed");\r
- vgmutex.Unlock();\r
- return 0;\r
- }\r
- // Test stuff\r
-\r
- query_str=(const char*)vgGetString(VG_VERSION) ;\r
- if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);\r
- else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",vgGetError());\r
-\r
- query_str=(const char*)vgGetString(VG_VENDOR) ;\r
- if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);\r
- else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",vgGetError());\r
-\r
- query_str=(const char*)vgGetString(VG_RENDERER) ;\r
- if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);\r
- else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",vgGetError());\r
-\r
- query_str=(const char*)vgGetString(VG_EXTENSIONS) ;\r
- if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);\r
- else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",vgGetError());\r
-\r
- aspect_correction= ((float)BACKBUFFER_HEIGHT)/576.f/(((float)BACKBUFFER_WIDTH)/720.f);\r
- initPaths();\r
- if (!loadFont()) {\r
- return 0;\r
- }\r
- vgttfont=vgCreateFont(0);\r
- vgttpaint=vgCreatePaint();\r
- vgSetParameteri( vgttpaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);\r
- vgSetColor(vgttpaint,0xffffffff);\r
-\r
- eglSwapInterval(egl_display, 1 );\r
-\r
- Log::getInstance()->log("OSD", Log::DEBUG, "Making egl current out 1%d",syscall(SYS_gettid));\r
- eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );\r
- //Now we will create the Screen\r
- initted = 1; // must set this here or create surface won't work\r
-\r
-\r
- /*if (((VideoOMX*)Video::getInstance())->initUsingOSDObjects()!=1) { //call Video for init stuff\r
- return 0;\r
- }*/\r
- InitializeMagick("");\r
-\r
- pthread_cond_init(&vgtaskCond, NULL);\r
- pthread_mutex_init(&vgtaskCondMutex, NULL);\r
-\r
- threadStart();\r
- taskmutex.Unlock();\r
- vgmutex.Unlock();\r
-\r
-\r
- return 1;\r
-}\r
-\r
-\r
-void OsdOpenVG::initPaths()\r
-{\r
-\r
-\r
- VGPath current;\r
- current=vgCreatePath(VG_PATH_FORMAT_STANDARD,\r
- VG_PATH_DATATYPE_F,1.f,0.f,\r
- 0,0,VG_PATH_CAPABILITY_ALL);\r
-\r
- vguLine(current,0.f,0.f,1.f,0.f);\r
- // HorzLine\r
- std_paths[HorzLine]=current;\r
-\r
- current=vgCreatePath(VG_PATH_FORMAT_STANDARD,\r
- VG_PATH_DATATYPE_F,1.f,0.f,\r
- 0,0,VG_PATH_CAPABILITY_ALL);\r
- vguLine(current,0.f,0.f,0.f,1.f);\r
- // VertLine\r
- std_paths[VertLine]=current;\r
-\r
- current=vgCreatePath(VG_PATH_FORMAT_STANDARD,\r
- VG_PATH_DATATYPE_F,1.f,0.f,\r
- 0,0,VG_PATH_CAPABILITY_ALL);\r
- //vguRect(current,0.f,0.f,1.f,1.f);\r
- vguRect(current,0.f,0.f,1.f,1.f);\r
- // Rectabgle\r
- std_paths[Rectangle]=current;\r
-\r
- current=vgCreatePath(VG_PATH_FORMAT_STANDARD,\r
- VG_PATH_DATATYPE_F,1.f,0.f,\r
- 0,0,0);\r
- vguEllipse(current,0.f,0.f,1.f,1.f);\r
- // Point\r
- std_paths[Point]=current;\r
-\r
-}\r
-\r
-void OsdOpenVG::destroyPaths()\r
-{\r
- vgDestroyPath(std_paths[HorzLine]);\r
- vgDestroyPath(std_paths[VertLine]);\r
- vgDestroyPath(std_paths[Rectangle]);\r
- vgDestroyPath(std_paths[Point]);\r
-\r
-}\r
-\r
-int OsdOpenVG::stopUpdate()\r
-{\r
- threadStop();\r
- processTasks();\r
- return 1;\r
-}\r
-\r
-\r
-int OsdOpenVG::shutdown()\r
-{\r
- if (!initted) return 0;\r
-\r
- initted = 0;\r
- Log::getInstance()->log("OSD", Log::DEBUG, "shutdown mark1");\r
- threadStop();\r
- Log::getInstance()->log("OSD", Log::DEBUG, "shutdown mark2");\r
- processTasks();\r
- Log::getInstance()->log("OSD", Log::DEBUG, "shutdown mark3");\r
-\r
- taskmutex.Lock();\r
- vgmutex.Lock();\r
- //(((VideoOMX*)Video::getInstance())->shutdownUsingOSDObjects());\r
-\r
-\r
-\r
- vgDestroyFont(vgfont);\r
- vgDestroyFont(vgttfont);\r
- vgDestroyPaint(vgttpaint);\r
- destroyPaths();\r
- vgClear(0,0,BACKBUFFER_WIDTH,BACKBUFFER_HEIGHT);\r
- eglSwapBuffers(egl_display, egl_surface);\r
- Log::getInstance()->log("OSD", Log::DEBUG, "Making egl current out final");\r
- eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );\r
- eglDestroySurface(egl_display,egl_surface);\r
- eglDestroyContext(egl_display,egl_context);\r
- eglTerminate(egl_display );\r
-\r
- DISPMANX_UPDATE_HANDLE_T bcm_update;\r
- bcm_update=vc_dispmanx_update_start(0);\r
-\r
- vc_dispmanx_element_remove(bcm_update,bcm_element);\r
-// vc_dispmanx_element_remove(bcm_update,bcm_background);\r
- vc_dispmanx_update_submit_sync(bcm_update);\r
-// vc_dispmanx_resource_delete(bcm_backres);\r
- vc_dispmanx_display_close(bcm_display);\r
-\r
-\r
-\r
- return 1;\r
-}\r
-\r
-\r
-\r
-\r
-void OsdOpenVG::threadMethod()\r
-{\r
- // We have to claim the egl context for this thread\r
-\r
- if (eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context)== EGL_FALSE) {\r
- Log::getInstance()->log("OSD", Log::WARN, "Making egl Current failed in thread %x",eglGetError());\r
- return;\r
- }\r
- int ts=0;\r
- while (true)\r
- {\r
- ts=1;\r
- unsigned int waittime=1;\r
-\r
- if (initted) {\r
-\r
- long long time1 = getTimeMS();\r
- if ((time1 - lastrendertime) > 200) {//5 fps for OSD updates are enough, avoids tearing\r
- InternalRendering();\r
- lastrendertime = getTimeMS();\r
-\r
- }\r
- if (processTasks()) ts=0;\r
- }\r
- threadCheckExit();\r
- if (ts!=0) {\r
- struct timespec target_time;\r
- clock_gettime(CLOCK_REALTIME,&target_time);\r
- target_time.tv_nsec+=1000000LL*ts;\r
- if (target_time.tv_nsec>999999999) {\r
- target_time.tv_nsec-=1000000000L;\r
- target_time.tv_sec+=1;\r
- }\r
- threadLock();\r
- threadWaitForSignalTimed(&target_time);\r
- threadUnlock();\r
- }\r
- //Sleep(1);\r
- }\r
- //eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );\r
-}\r
-\r
-\r
-void OsdOpenVG::threadPostStopCleanup()\r
-{\r
- //Doing nothing\r
- //goo;\r
-}\r
-\r
-\r
-\r
-\r
-\r
-\r
-void OsdOpenVG::InternalRendering(){\r
- vgmutex.Lock();\r
- float colclear[]={1.f,1.0f,1.f,1.f};\r
- vgSetfv(VG_CLEAR_COLOR,4,colclear);\r
- vgClear(0,0,BACKBUFFER_WIDTH,BACKBUFFER_HEIGHT);\r
- vgSeti(VG_BLEND_MODE, VG_BLEND_SRC);\r
-\r
-\r
- drawSurfaces(); //iterate through and draws all commands\r
-\r
- //Show it to the user!\r
- eglSwapBuffers(egl_display, egl_surface);\r
- vgmutex.Unlock();\r
-\r
-}\r
-\r
-/*font stuff*/\r
-\r
-float OsdOpenVG::getFontHeight()\r
-{\r
- return font_height; //dummy\r
-}\r
-float OsdOpenVG::getCharWidth(wchar_t c)\r
-{\r
- unsigned int glyph_index=FT_Get_Char_Index(ft_face,c);\r
- return font_exp_x[glyph_index];\r
-}\r
-\r
-unsigned int OsdOpenVG::loadTTchar(cTeletextChar c)\r
-{\r
- unsigned int glyph_index=c.getGlyphIndex();\r
- if (tt_font_chars.find(glyph_index)!=tt_font_chars.end())\r
- {\r
- return glyph_index;\r
- }\r
-\r
- unsigned int buffer[10];\r
- const VGfloat glyphOrigin[] = { 0.f, 0.f };\r
- const VGfloat escapement[] = { 12.f, 0.f };\r
- unsigned int * charmap = GetFontChar(c, buffer);\r
- if (!charmap) { //invalid char\r
- return 0;\r
- }\r
- for (int i=0;i<10;i++) {\r
- buffer[i]=charmap[i]>>4;\r
- }\r
-\r
- VGImage handle = vgCreateImage(\r
- VG_A_8,\r
- 12,\r
- 10,\r
- VG_IMAGE_QUALITY_NONANTIALIASED | VG_IMAGE_QUALITY_FASTER\r
- | VG_IMAGE_QUALITY_BETTER);\r
- vgImageSubData(handle, buffer, 4, VG_A_1, 0, 0, 12, 10);\r
- vgSetGlyphToImage(\r
- vgttfont,\r
- glyph_index,\r
- handle, glyphOrigin, escapement);\r
- vgDestroyImage(handle);\r
- tt_font_chars[glyph_index]=1;\r
-\r
- return glyph_index;\r
-}\r
-\r
-int OsdOpenVG::loadFont()\r
-{\r
- int error;\r
- float font_size=16.f;\r
- if (!freetype_inited) {\r
- error=FT_Init_FreeType(&ft_library);\r
- if (error)\r
- {\r
- Log::getInstance()->log("OSD", Log::WARN, "Could not load freetype %x",error);\r
- return 0;\r
- }\r
-\r
- error=FT_New_Memory_Face(ft_library,font_data,font_data_end-font_data,0,&ft_face );\r
- if (error) {\r
- Log::getInstance()->log("OSD", Log::WARN, "Could not load font face %x",error);\r
- return 0;\r
- }\r
- error=FT_Set_Char_Size(ft_face,0,font_size*64,0,0 /*dpi*/);\r
- if (error) {\r
- FT_Done_Face(ft_face);\r
- Log::getInstance()->log("OSD", Log::WARN, "Could not set face size %x",error);\r
- return 0;\r
- }\r
- freetype_inited=true;\r
- }\r
- vgfont=vgCreateFont(0);\r
- FT_ULong cur_char;\r
- FT_UInt glyph;\r
- font_height=ft_face->size->metrics.height/64.f;\r
- cur_char = FT_Get_First_Char(ft_face,&glyph);\r
- vector<VGubyte> segments;\r
- vector<VGfloat> coord;\r
- segments.reserve(256);\r
- coord.reserve(1024);\r
- //Log::getInstance()->log("OSD", Log::DEBUG, "Create Glyph test %d %x %x %d",cur_char,font_data_end,font_data,glyph);\r
- while (glyph !=0)\r
- {\r
- error=FT_Load_Glyph(ft_face,glyph,FT_LOAD_DEFAULT);\r
- if (error){\r
- FT_Done_Face(ft_face);\r
- Log::getInstance()->log("OSD", Log::WARN, "Could not load glyph %x",error);\r
- return 0;\r
- }\r
- VGPath path;\r
- FT_Outline ot=ft_face->glyph->outline;\r
- segments.clear();\r
- coord.clear();\r
-\r
- if (ot.n_contours ==0) {\r
- path=VG_INVALID_HANDLE;\r
- } else {\r
- path=vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,\r
- 1.0f,0.f,0,0,VG_PATH_CAPABILITY_ALL);\r
-\r
- /* convert glyph */\r
- FT_Vector *pt=ot.points;\r
- const char *tags=ot.tags;\r
- const short* cont=ot.contours;\r
- short n_cont=ot.n_contours;\r
- short n_point=ot.n_points;\r
- short last_cont=0;\r
- for (short point=0;n_cont!=0;cont++,n_cont--) {\r
- short next_cont=*cont+1;\r
- bool first=true;\r
- char last_tag=0;\r
- short first_point=point;\r
- //Log::getInstance()->log("OSD", Log::DEBUG, "runs %d",*cont);\r
- for (;point<next_cont;point++) {\r
- char tag=tags[point];\r
- FT_Vector fpoint=pt[point];\r
- // Log::getInstance()->log("OSD", Log::DEBUG, "tag %d point %d %d: %d %d",tag,fpoint.x,fpoint.y,point,n_point);\r
- if (first) {\r
- segments.push_back(VG_MOVE_TO);\r
- first=false;\r
- } else if (tag &0x1) { //on curve\r
- if (last_tag &0x1) {\r
- segments.push_back(VG_LINE_TO);\r
- } else if (last_tag &0x2){\r
- segments.push_back(VG_CUBIC_TO);\r
- } else {\r
- segments.push_back(VG_QUAD_TO);\r
- }\r
-\r
- } else {\r
- if (!(tag &0x2)){\r
- if (!(last_tag &0x1)) {\r
- segments.push_back(VG_QUAD_TO);\r
- int coord_size=coord.size();\r
- VGfloat x=(coord[coord_size-2]+ ((float)fpoint.x)/64.f)*0.5f*aspect_correction;\r
- VGfloat y=(coord[coord_size-1]+(font_size- ((float)fpoint.y)/64.f))*0.5f;\r
- coord.push_back(x);\r
- coord.push_back(y);\r
- }\r
- }\r
-\r
-\r
- }\r
- last_tag=tag;\r
- coord.push_back(((float)fpoint.x)*aspect_correction/64.);\r
- coord.push_back(font_size-((float)fpoint.y)/64.);\r
- //Log::getInstance()->log("OSD", Log::DEBUG, "Create APD Glyph coord %d %d %g %g",fpoint.x,fpoint.y,coord[coord.size()-2],coord[coord.size()-1]);\r
- }\r
- if (!(last_tag &0x1)) {\r
- if (last_tag &0x2) {\r
- segments.push_back(VG_CUBIC_TO);\r
- } else {\r
- segments.push_back(VG_QUAD_TO);\r
- }\r
- coord.push_back(((float)pt[first_point].x)*aspect_correction/64.);\r
- coord.push_back(font_size-((float)pt[first_point].y)/64.);\r
- }\r
- //segments.push_back(VG_CLOSE_PATH);\r
-\r
-\r
- }\r
- vgAppendPathData(path,segments.size(),&segments[0],&coord[0]);\r
- int n=0;\r
- /* for (int m=0;m<segments.size();m++) {\r
- switch (segments[m])\r
- {\r
- case VG_MOVE_TO:\r
- Log::getInstance()->log("OSD", Log::DEBUG, "Move To %g %g",coord[n],coord[n+1]);n+=2; break;\r
- case VG_LINE_TO:\r
- Log::getInstance()->log("OSD", Log::DEBUG, "Line To %g %g",coord[n],coord[n+1]);n+=2; break;\r
- case VG_CUBIC_TO:\r
- Log::getInstance()->log("OSD", Log::DEBUG, "Cubic To %g %g %g %g %g %g",coord[n],coord[n+1],coord[n+2],coord[n+3],coord[n+4],coord[n+5]);n+=6; break;\r
- case VG_QUAD_TO:\r
- Log::getInstance()->log("OSD", Log::DEBUG, "Quad To %g %g %g %g",coord[n],coord[n+1],coord[n+2],coord[n+3]);n+=4; break;\r
- case VG_CLOSE_PATH:\r
- Log::getInstance()->log("OSD", Log::DEBUG, "Close Path"); break;\r
- }\r
-\r
- }*/\r
- //vguRect(path,0.f,0.f,1.f,1.f);\r
- //Log::getInstance()->log("OSD", Log::DEBUG, "Create APD Glyph %d %x",segments.size(),vgGetError());\r
- }\r
- VGfloat ori[]={0.f,0.f};\r
- VGfloat esp[]={ft_face->glyph->advance.x/64.f*aspect_correction,ft_face->glyph->advance.y/64.f};\r
- font_exp_x[glyph]=ft_face->glyph->advance.x/64.f*aspect_correction; //recalculate\r
-\r
- vgSetGlyphToPath(vgfont,glyph,path,VG_FALSE,ori,esp);\r
- //Log::getInstance()->log("OSD", Log::DEBUG, "Create Glyph %d %d %x",path,glyph,vgGetError());\r
- if (path!=VG_INVALID_HANDLE) {\r
- vgDestroyPath(path);\r
- }\r
- cur_char = FT_Get_Next_Char(ft_face,cur_char,&glyph);\r
- }\r
- return 1;\r
-}\r
-\r
-\r
-void OsdOpenVG::drawSetTrans(SurfaceCommands & sc)\r
-{\r
- vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);\r
- vgLoadIdentity();\r
- vgScale(((float)BACKBUFFER_WIDTH)/720.f, -((float)BACKBUFFER_HEIGHT)/576.f);\r
- vgTranslate(0.f+sc.x,-576.f+sc.y);\r
-\r
- vgSeti(VG_MATRIX_MODE, VG_MATRIX_GLYPH_USER_TO_SURFACE);\r
- vgLoadIdentity();\r
- vgScale(((float)BACKBUFFER_WIDTH)/720.f, -((float)BACKBUFFER_HEIGHT)/576.f);\r
- vgTranslate(0.f+sc.x,-576.f+sc.y);\r
-\r
- vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);\r
- vgLoadIdentity();\r
- vgScale(((float)BACKBUFFER_WIDTH)/720.f, -((float)BACKBUFFER_HEIGHT)/576.f);\r
- vgTranslate(0.f+sc.x,-576.f+sc.y);\r
-\r
-\r
-\r
- //vgTranslate(0.f+sc.x,576.f-sc.y);\r
- //Log::getInstance()->log("OSD", Log::DEBUG, "Draw Translate %g %g",sc.x,sc.y);\r
-\r
-}\r
-void OsdOpenVG::executeDrawCommand(SVGCommand & command)\r
-{\r
-\r
- VGfloat save_matrix[9];\r
- switch (command.instr) {\r
- case DrawPath: {\r
- vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);\r
- // VGuint rgba;\r
- // rgba = vgGetColor((VGPaint) command.reference);\r
- //Log::getInstance()->log("OSD", Log::DEBUG, "Draw Path %d %x %g %g %g %g",command.reference,command.target.path_index,command.x,command.y,command.w,command.h);\r
- //vgSeti(VG_FILL_RULE,);\r
-\r
- vgGetMatrix(save_matrix);\r
- vgSetPaint((VGPaint) command.reference,VG_FILL_PATH);\r
- vgSetPaint((VGPaint) command.reference,VG_STROKE_PATH);\r
- vgTranslate(command.x,command.y);\r
- vgScale(command.w,command.h);\r
- vgDrawPath(std_paths[command.target.path_index],VG_FILL_PATH);\r
- vgLoadMatrix(save_matrix);\r
- } break;\r
- case DrawImage: {\r
- vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);\r
- vgGetMatrix(save_matrix);\r
- vgTranslate(command.x,command.y);\r
- //vgScale(command.w,command.h);\r
- if (command.reference) { //special behaviout for bw images they act as a mask on the current paint\r
- vgSetPaint((VGPaint) command.reference,VG_FILL_PATH);\r
- vgSetPaint((VGPaint) command.reference,VG_STROKE_PATH);\r
- vgSeti(VG_IMAGE_MODE,VG_DRAW_IMAGE_STENCIL);\r
- vgSeti(VG_BLEND_MODE, VG_BLEND_SRC_OVER);\r
- vgScale(aspect_correction,1.f);\r
- } else {\r
- VGfloat imagewidth=vgGetParameteri((VGImage) command.target.image, VG_IMAGE_WIDTH);\r
- VGfloat imageheight=vgGetParameteri((VGImage) command.target.image, VG_IMAGE_HEIGHT);\r
- //vgScale(720.f/((float)BACKBUFFER_WIDTH), 576.f/((float)BACKBUFFER_HEIGHT));\r
- float scalex=command.w/imagewidth;\r
- float scaley=command.h/imageheight;\r
- //vgScale(command.w/imagewidth,command.h/imageheight);\r
- vgScale(scalex,scaley);\r
- vgSeti(VG_IMAGE_MODE,VG_DRAW_IMAGE_NORMAL);\r
- //Log::getInstance()->log("OSD", Log::DEBUG, "Draw Image Scale %g %g %g %g %g %g",command.w,imagewidth,command.h,imageheight,scalex,scaley);\r
- }\r
-\r
-\r
- //vgLoadIdentity();\r
- //vgTranslate(200.f,500.f);\r
- //vgScale(100.f,100.f);\r
-\r
- vgDrawImage((VGImage) command.target.image);\r
- //Log::getInstance()->log("OSD", Log::DEBUG, "Draw Image %d %x %g %g %g %g %x",command.reference,command.target.image,command.x,command.y,command.w,command.h,\r
- // vgGetError());\r
- if (command.reference) {\r
- vgSeti(VG_IMAGE_MODE,VG_DRAW_IMAGE_NORMAL);\r
- vgSeti(VG_BLEND_MODE, VG_BLEND_SRC);\r
- }\r
- vgLoadMatrix(save_matrix);\r
- } break;\r
- case DrawGlyph: {\r
- vgSeti(VG_MATRIX_MODE, VG_MATRIX_GLYPH_USER_TO_SURFACE);\r
- vgGetMatrix(save_matrix);\r
- vgSetPaint((VGPaint) command.reference,VG_FILL_PATH);\r
- vgSetPaint((VGPaint) command.reference,VG_STROKE_PATH);\r
- vgTranslate(command.x,command.y);\r
- VGfloat gori[]={0.,0.};\r
- vgSetfv(VG_GLYPH_ORIGIN,2,gori);\r
-\r
- unsigned int glyph_index=FT_Get_Char_Index(ft_face,command.target.textchar);\r
- vgDrawGlyph(vgfont,glyph_index,VG_FILL_PATH,VG_FALSE);\r
- //vgDrawPath(std_paths[Rectangle],VG_FILL_PATH);\r
- /* Log::getInstance()->log("OSD", Log::DEBUG, "Draw Glyph %d %c %d %g %g %x",command.reference,command.target.textchar,glyph_index,command.x,command.y,\r
- vgGetError());*/\r
- vgLoadMatrix(save_matrix);\r
- vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);\r
- } break;\r
- case DrawTTchar:{\r
- cTeletextChar tchar;\r
- tchar.setInternal(command.target.ttchar);\r
- vgSeti(VG_MATRIX_MODE, VG_MATRIX_GLYPH_USER_TO_SURFACE);\r
- vgGetMatrix(save_matrix);\r
- enumTeletextColor ttforegcolour=tchar.GetFGColor();\r
- enumTeletextColor ttbackgcolour=tchar.GetBGColor();\r
- if (tchar.GetBoxedOut()) {\r
- ttforegcolour=ttcTransparent;\r
- ttbackgcolour=ttcTransparent;\r
- }\r
- vgSeti(VG_IMAGE_MODE,VG_DRAW_IMAGE_STENCIL);\r
- vgSeti(VG_BLEND_MODE, VG_BLEND_SRC_OVER);\r
-\r
-\r
- vgTranslate(command.x+command.w*11.85f*1.4f,command.y+command.h*19.75f);\r
- VGfloat gori[]={0.,0.};\r
- vgSetfv(VG_GLYPH_ORIGIN,2,gori);\r
-\r
- vgScale(-1.4f,2.f);\r
- unsigned int color=Surface::enumTeletextColorToCoulour(ttbackgcolour).rgba();\r
- color=color<<8 | (color &0xff000000)>>24;\r
- vgSetColor(vgttpaint,color);\r
- vgSetPaint((VGPaint) vgttpaint,VG_FILL_PATH);\r
- vgSetPaint((VGPaint) vgttpaint,VG_STROKE_PATH);\r
- cTeletextChar filled;\r
- filled.setInternal(0x187f);\r
- unsigned int glyph_index=loadTTchar(filled);\r
- vgDrawGlyph(vgttfont,glyph_index,VG_FILL_PATH,VG_FALSE);\r
-\r
- color=Surface::enumTeletextColorToCoulour(ttforegcolour).rgba();\r
- color=color<<8 | (color &0xff000000)>>24;\r
- vgSetColor(vgttpaint,color);\r
- vgSetPaint((VGPaint) vgttpaint,VG_FILL_PATH);\r
- vgSetPaint((VGPaint) vgttpaint,VG_STROKE_PATH);\r
- glyph_index=loadTTchar(tchar);\r
- vgDrawGlyph(vgttfont,glyph_index,VG_FILL_PATH,VG_FALSE);\r
-\r
- /* Log::getInstance()->log("OSD", Log::DEBUG, "Draw TTchar %x %x %x %x",glyph_index,ttforegcolour,Surface::enumTeletextColorToCoulour(ttforegcolour).rgba(),\r
- vgGetColor(vgttpaint));*/\r
-\r
-\r
- vgLoadMatrix(save_matrix);\r
- vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);\r
- vgSeti(VG_IMAGE_MODE,VG_DRAW_IMAGE_NORMAL);\r
- vgSeti(VG_BLEND_MODE, VG_BLEND_SRC);\r
-\r
- }break;\r
- }\r
-}\r
-\r
-unsigned int OsdOpenVG::handleTask(OpenVGCommand& command)\r
-{\r
- switch (command.task){\r
- case OVGdestroyImageRef: {\r
- vgDestroyImage((VGImage)command.param1);\r
- return 0;\r
- } break;\r
- case OVGdestroyPaint: {\r
- Log::getInstance()->log("OSD", Log::DEBUG, "Draw Paint Destroy %d ",command.param1);\r
- vgDestroyPaint((VGPaint)command.param1);\r
- return 0;\r
- } break;\r
- case OVGcreateImagePalette: {\r
- VGImage input=vgCreateImage(VG_A_8,command.param1, command.param2,\r
- VG_IMAGE_QUALITY_NONANTIALIASED|\r
- VG_IMAGE_QUALITY_FASTER|VG_IMAGE_QUALITY_BETTER);\r
- vgImageSubData(input,command.data,command.param1,\r
- VG_A_8,0,0,command.param1, command.param2); // upload palettized image data\r
- VGImage handle=vgCreateImage(VG_sRGBA_8888,command.param1, command.param2,\r
- VG_IMAGE_QUALITY_NONANTIALIASED|\r
- VG_IMAGE_QUALITY_FASTER|VG_IMAGE_QUALITY_BETTER);\r
- VGuint *palette=(VGuint*)malloc(256*sizeof(VGuint));\r
- VGuint *in_palette=(VGuint*)command.data2;\r
- for (int i=0;i<256;i++) {\r
- VGuint color=in_palette[i];\r
- palette[i]=color<<8 | (color &0xff000000)>>24;\r
- }\r
-\r
- vgLookupSingle(handle,input,palette,VG_ALPHA,VG_FALSE,VG_FALSE);\r
- free(palette);\r
- vgDestroyImage(input);\r
-\r
- return handle;\r
- } break;\r
- case OVGcreateMonoBitmap: {\r
- VGImage handle=vgCreateImage(VG_A_1,command.param1, command.param2,\r
- VG_IMAGE_QUALITY_NONANTIALIASED|\r
- VG_IMAGE_QUALITY_FASTER|VG_IMAGE_QUALITY_BETTER);\r
- //Log::getInstance()->log("OSD", Log::DEBUG, "Draw create mono %d %d %x %d",command.param1,command.param2,vgGetError(),handle);\r
- unsigned int buffer_len=(command.param1*command.param2)>>3;\r
- unsigned char * buffer=(unsigned char*)malloc(buffer_len);\r
- unsigned char * r_buffer1=buffer;\r
- const unsigned char * r_buffer2=(const unsigned char *)command.data;\r
- unsigned char *buffer_end=buffer+buffer_len;\r
- while (r_buffer1!=buffer_end) {\r
- unsigned char byte=*r_buffer2;\r
- *r_buffer1=((byte * 0x0802LU & 0x22110LU) | (byte * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16;\r
- r_buffer1++;r_buffer2++;\r
- }\r
-\r
-\r
- vgImageSubData(handle,buffer,command.param1>>3,\r
- VG_A_1,0,0,command.param1, command.param2);\r
- free(buffer);\r
- // Log::getInstance()->log("OSD", Log::DEBUG, "Draw create mono2 %d %d %x %d",command.param1,command.param2,vgGetError(),handle);\r
- return handle;\r
- } break;\r
- case OVGcreateImageFile: {\r
- VGImage handle;\r
- try{\r
- Image *imagefile=(Image*)command.data;\r
- Blob imageblob;\r
- imagefile->write(&imageblob,"RGBA");\r
-\r
-\r
- handle=vgCreateImage(VG_sXBGR_8888,imagefile->columns(),imagefile->rows(),\r
- VG_IMAGE_QUALITY_NONANTIALIASED|\r
- VG_IMAGE_QUALITY_FASTER|VG_IMAGE_QUALITY_BETTER);\r
- // Log::getInstance()->log("OSD", Log::DEBUG, "Draw create image details %d %d %x mark1",imagefile->columns(),imagefile->rows(),*(unsigned int*)imageblob.data());\r
- vgImageSubData(handle,imageblob.data(),imagefile->columns()*4,\r
- VG_sXBGR_8888,0,0,imagefile->columns(),imagefile->rows());\r
- // Log::getInstance()->log("OSD", Log::DEBUG, "Draw create image details %d %d %x mark2",imagefile->columns(),imagefile->rows(),*(unsigned int*)imageblob.data());\r
- delete imagefile;\r
- }catch( Exception &error_ )\r
- {\r
- Log::getInstance()->log("OSD", Log::DEBUG, "Libmagick hT: %s",error_.what());\r
-\r
- return 0;\r
- }\r
-\r
- //Log::getInstance()->log("OSD", Log::DEBUG, "Draw create file %d %d %x %d",command.param1,command.param2,vgGetError(),handle);\r
- return handle;\r
- } break;\r
- case OVGcreateColorRef :{\r
- VGPaint handle;\r
- handle=vgCreatePaint();\r
- vgSetParameteri(handle, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);\r
- vgSetColor(handle,command.param1);\r
- VGuint rgba;\r
- rgba = vgGetColor((VGPaint)handle);\r
- Log::getInstance()->log("OSD", Log::DEBUG, "Draw Paint %d %x %x",handle,command.param1,rgba);\r
- return handle;\r
- } break;\r
- case OVGimageUploadLine: {\r
- vgImageSubData((VGImage)command.param1,command.data,0,VG_sARGB_8888,0,command.param2,command.param3,1);\r
- return 0;\r
- } break;\r
-\r
- }\r
-}\r
-\r
-bool OsdOpenVG::processTasks()\r
-{\r
- bool worked=false;\r
- taskmutex.Lock();\r
- vgmutex.Lock();\r
- while (vgcommands.size()>0)\r
- {\r
- OpenVGCommand &comm=vgcommands.front();\r
- OpenVGResponse resp;\r
- resp.result=handleTask(comm);\r
- resp.id=comm.id;\r
- if (comm.id) {\r
- vgresponses.push_back(resp);\r
- }\r
- vgcommands.pop_front();\r
- taskmutex.Unlock();\r
- vgmutex.Unlock();\r
- //threadCheckExit();\r
- pthread_mutex_lock(&vgtaskCondMutex);\r
- pthread_cond_signal(&vgtaskCond);\r
- pthread_mutex_unlock(&vgtaskCondMutex);\r
- taskmutex.Lock();\r
- vgmutex.Lock();\r
- worked=true;\r
- }\r
- taskmutex.Unlock();\r
- vgmutex.Unlock();\r
-\r
- return worked;\r
-}\r
-\r
-bool OsdOpenVG::haveOpenVGResponse(unsigned int id,unsigned int * resp)\r
-{\r
- taskmutex.Lock();\r
- if (vgresponses.size()>0)\r
- {\r
- deque<OpenVGResponse>::iterator itty=vgresponses.begin();\r
- while (itty!=vgresponses.end())\r
- {\r
- if ((*itty).id==id) {\r
- *resp=(*itty).result;\r
- taskmutex.Unlock();\r
- return true;\r
- }\r
- itty++;\r
- }\r
- }\r
- taskmutex.Unlock();\r
- return false;\r
-}\r
-\r
-\r
-unsigned int OsdOpenVG::putOpenVGCommand(OpenVGCommand& comm,bool wait)\r
-{\r
- taskmutex.Lock();\r
- if (wait){\r
- comm.id=wait_id;\r
- wait_id++;\r
- } else {\r
- comm.id=0; // we are not waiting\r
- }\r
- vgcommands.push_back(comm);\r
- taskmutex.Unlock();\r
- threadSignal();\r
- while (wait) {\r
- unsigned int resp;\r
- if (!haveOpenVGResponse(comm.id,&resp)) {\r
- struct timespec target_time;\r
- clock_gettime(CLOCK_REALTIME,&target_time);\r
- target_time.tv_nsec+=1000000LL*100;\r
- if (target_time.tv_nsec>999999999) {\r
- target_time.tv_nsec-=1000000000L;\r
- target_time.tv_sec+=1;\r
- }\r
- pthread_mutex_lock(&vgtaskCondMutex);\r
- pthread_cond_timedwait(&vgtaskCond, &vgtaskCondMutex,&target_time);\r
- pthread_mutex_unlock(&vgtaskCondMutex);\r
- } else {\r
- return resp;\r
- }\r
- }\r
- return 0;\r
-}\r
-\r
-void OsdOpenVG::imageUploadLine(ImageIndex index,unsigned int j,unsigned int width,void *data)\r
-{\r
- vgImageSubData((VGImage)index,data,0,VG_sARGB_8888,0,j,width,1);\r
-\r
- struct OpenVGCommand comm;\r
- comm.task=OVGimageUploadLine;\r
- comm.param1=index;\r
- comm.param2=j;\r
- comm.param3=width;\r
- comm.data=data;\r
- putOpenVGCommand(comm,true);\r
-}\r
-\r
-void OsdOpenVG::destroyImageRef(ImageIndex index)\r
-{\r
- struct OpenVGCommand comm;\r
- comm.task=OVGdestroyImageRef;\r
- comm.param1=index;\r
- putOpenVGCommand(comm,false);\r
-}\r
-\r
-ImageIndex OsdOpenVG::createJpeg(const char* fileName, int *width,int *height)\r
-{\r
- Image* magicimage=NULL;\r
- bool mem=false;\r
- struct OpenVGCommand comm;\r
- comm.task=OVGcreateImageFile;\r
-\r
- try{\r
- // Now figure out, if it is a special case\r
- if (strcmp(fileName,"/vdr.jpg")==0) {\r
- magicimage=new Image(Blob(vdr_data,vdr_data_end-vdr_data));\r
- *height=100; // this is faked so that the system does use the old coordinate system\r
- *width=ceil(190.f*aspect_correction);\r
- Log::getInstance()->log("OSD", Log::DEBUG, "LoadIm vdr.jpg");\r
- } else if (strcmp(fileName,"/wallpaperPAL.jpg")==0) {\r
- magicimage=new Image(Blob(wallpaper_data,wallpaper_data_end-wallpaper_data));\r
- *width=720; // this is faked so that the system does use the old coordinate system\r
- *height=576;\r
- Log::getInstance()->log("OSD", Log::DEBUG, "LoadIm wallpaperPAL.jpg");\r
- } else {\r
- magicimage=new Image();\r
- magicimage->read(fileName);\r
- Log::getInstance()->log("OSD", Log::DEBUG, "LoadIm file: %s",fileName);\r
- *width=ceil(magicimage->baseColumns()*aspect_correction); // this is faked so that the system does use the old coordinate system\r
- *height=magicimage->baseRows();\r
- }\r
-\r
- }catch( Exception &error_ )\r
- {\r
- Log::getInstance()->log("OSD", Log::DEBUG, "Libmagick: %s",error_.what());\r
-\r
- return 0;\r
- }\r
- comm.data=magicimage;\r
- return putOpenVGCommand(comm,true);\r
-}\r
-\r
-ImageIndex OsdOpenVG::createMonoBitmap(void *base,int width,int height)\r
-{\r
- struct OpenVGCommand comm;\r
- comm.task=OVGcreateMonoBitmap;\r
- comm.param1=width;\r
- comm.param2=height;\r
- comm.data=base;\r
- return putOpenVGCommand(comm,true);\r
-}\r
-\r
-ImageIndex OsdOpenVG::createImagePalette(int width,int height,const unsigned char *image_data,const unsigned int*palette_data)\r
-{\r
- struct OpenVGCommand comm;\r
- comm.task=OVGcreateImagePalette;\r
- comm.param1=width;\r
- comm.param2=height;\r
- comm.data=image_data;\r
- comm.data2=palette_data;\r
- return putOpenVGCommand(comm,true);\r
-}\r
-\r
-void OsdOpenVG::destroyStyleRef(unsigned int index)\r
-{\r
- struct OpenVGCommand comm;\r
- comm.task=OVGdestroyPaint;\r
- comm.param1=index;\r
- putOpenVGCommand(comm,false);\r
-}\r
-\r
-unsigned int OsdOpenVG::createStyleRef(const DrawStyle &c)\r
-{\r
- unsigned int col=c.rgba();\r
- struct OpenVGCommand comm;\r
- comm.task=OVGcreateColorRef;\r
- comm.param1=col<<8 | (col &0xff000000)>>24;\r
- comm.data=&c;\r
- return putOpenVGCommand(comm,true);\r
-}\r
-\r
-unsigned int OsdOpenVG::createColorRef(const Colour &c)\r
-{\r
- unsigned int col=c.rgba();\r
- struct OpenVGCommand comm;\r
- comm.task=OVGcreateColorRef;\r
- comm.param1=col<<8 | (col &0xff000000)>>24;\r
- comm.data=&c;\r
- return putOpenVGCommand(comm,true);\r
-}\r
-\r
-\r
+/*
+ Copyright 2004-2005 Chris Tallon, 2006,2011-2012 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 "osdopenvg.h"
+#include "mtd.h"
+#include "videoomx.h"
+#include "surface.h"
+#include <Magick++.h>
+
+#include "message.h"
+#include "command.h"
+#include "teletxt/txtfont.h"
+
+#include <sys/syscall.h>
+#include <vector>
+#include <math.h>
+
+using namespace Magick;
+
+extern uint8_t font_data[] asm("_binary_fonts_sourcesans_ttf_start");
+extern uint8_t font_data_end[] asm("_binary_fonts_sourcesans_ttf_end");
+extern uint8_t vdr_data[] asm("_binary_other_vdrhires_jpg_start");
+extern uint8_t vdr_data_end[] asm("_binary_other_vdrhires_jpg_end");
+extern uint8_t wallpaper_data[] asm("_binary_other_wallpaper720p_jpg_start");
+extern uint8_t wallpaper_data_end[] asm("_binary_other_wallpaper720p_jpg_end");
+
+
+#define BACKBUFFER_WIDTH 1280
+#define BACKBUFFER_HEIGHT 720
+
+
+OsdOpenVG::OsdOpenVG()
+{
+ vgmutex.Lock();
+ taskmutex.Lock();
+ lastrendertime=getTimeMS();
+ display_height=0;
+ display_width=0;
+ mode=0;
+ aspect_correction=1.;
+
+ freetype_inited=false;
+ wait_id=1;
+
+}
+
+OsdOpenVG::~OsdOpenVG()
+{
+
+ if (initted)
+ {
+ shutdown();
+ }
+
+ if (freetype_inited) FT_Done_Face(ft_face);
+
+ vgmutex.Unlock();
+ taskmutex.Unlock();
+}
+
+
+
+int OsdOpenVG::init(void* device)
+{
+ if (initted) return 0;
+ Video* video = Video::getInstance();
+ //window=*((HWND*)device);
+
+ // May be this device specific part should go to a device specific child class
+
+ //init broadcom chipset (Move to video?)
+
+
+ //First get connection to egl
+ egl_display=eglGetDisplay(EGL_DEFAULT_DISPLAY);
+
+ if (egl_display==EGL_NO_DISPLAY) {
+ Log::getInstance()->log("OSD", Log::WARN, "Could not get egl display! %x",eglGetError());
+ vgmutex.Unlock();
+ return 0;
+ }
+
+
+
+ if (eglInitialize(egl_display, NULL, NULL)==EGL_FALSE) {
+ Log::getInstance()->log("OSD", Log::WARN, "Initialising display failed! %x",eglGetError());
+ vgmutex.Unlock();
+ return 0;
+ }
+
+
+ const char *query_str=eglQueryString(egl_display,EGL_CLIENT_APIS);
+ if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);
+ else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",eglGetError());
+ query_str=eglQueryString(egl_display,EGL_EXTENSIONS);
+ if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);
+ else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",eglGetError());
+
+ if (eglBindAPI(EGL_OPENVG_API)==EGL_FALSE) {
+ Log::getInstance()->log("OSD", Log::WARN, "Binding openvg api failed! %x",eglGetError());
+ vgmutex.Unlock();
+ return 0;
+ }
+
+ const EGLint attributs[]={
+ EGL_RED_SIZE,8,EGL_GREEN_SIZE, 8,EGL_BLUE_SIZE, 8,EGL_ALPHA_SIZE, 8,
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT,
+ EGL_CONFORMANT, EGL_OPENVG_BIT,
+ EGL_NONE
+ }; // Here, we might have to select the resolution!
+
+
+ EGLint number;
+
+ if (eglChooseConfig(egl_display, attributs, &egl_ourconfig, 1, &number)==EGL_FALSE) {
+ Log::getInstance()->log("OSD", Log::WARN, "Choosing egl config failed! %x",eglGetError());
+ vgmutex.Unlock();
+ return 0;
+ }
+
+
+
+ egl_context=eglCreateContext(egl_display,egl_ourconfig,NULL,NULL);
+ if (egl_context==EGL_NO_CONTEXT) {
+ Log::getInstance()->log("OSD", Log::WARN, "Creating egl context failed! %x",eglGetError());
+ vgmutex.Unlock();
+ return 0;
+ }
+
+ // warning broadcom specific, get display size!
+ display_width=display_height=0;
+ if (graphics_get_display_size(0, &display_width, &display_height)<0) {
+ Log::getInstance()->log("OSD", Log::WARN, "Getting display size failed! (BCM API) ");
+ vgmutex.Unlock();
+ return 0;
+ }
+ Log::getInstance()->log("OSD", Log::NOTICE, "Displaysize is %d x %d ",display_width, display_height);
+ VC_RECT_T dst_rect ={0,0,display_width,display_height};
+ VC_RECT_T src_rect={0,0,BACKBUFFER_WIDTH <<16,BACKBUFFER_HEIGHT<<16};
+ // VC_RECT_T src_rect_bg={0,0,1<<16,1<<16};
+ // VC_RECT_T src_rect_im={0,0,1,1};
+
+ /* uint32_t back_image_ptr;
+ bcm_backres=vc_dispmanx_resource_create(VC_IMAGE_RGB888,1,1,&back_image_ptr);
+ unsigned int color=0x00FF0000;
+ vc_dispmanx_resource_write_data(bcm_backres,VC_IMAGE_RGB888,4,&color,&src_rect_im);*/
+
+ DISPMANX_UPDATE_HANDLE_T bcm_update;
+ bcm_display=vc_dispmanx_display_open(0);
+ bcm_update=vc_dispmanx_update_start(0);
+ bcm_element=vc_dispmanx_element_add(bcm_update,bcm_display,
+ 2,&dst_rect, 0,
+ &src_rect,DISPMANX_PROTECTION_NONE,0, 0, (DISPMANX_TRANSFORM_T) 0);
+
+
+ /* bcm_background=vc_dispmanx_element_add(bcm_update,bcm_display,
+ 0,&dst_rect,bcm_backres ,
+ &src_rect_bg,DISPMANX_PROTECTION_NONE,0, 0, (DISPMANX_TRANSFORM_T) 0);*/
+
+ vc_dispmanx_update_submit_sync(bcm_update);
+
+
+
+ static EGL_DISPMANX_WINDOW_T nativewindow;
+ nativewindow.element=bcm_element;
+ nativewindow.height=BACKBUFFER_HEIGHT;
+ nativewindow.width=BACKBUFFER_WIDTH;
+
+ egl_surface = eglCreateWindowSurface(egl_display,egl_ourconfig, &nativewindow,NULL );
+ if (egl_surface==EGL_NO_SURFACE) {
+ Log::getInstance()->log("OSD", Log::WARN, "Creating egl window surface failed!");
+ vgmutex.Unlock();
+ return 0;
+ }
+ Log::getInstance()->log("OSD", Log::DEBUG, "Making egl current in1%d",syscall(SYS_gettid));
+ if (eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context)== EGL_FALSE) {
+ Log::getInstance()->log("OSD", Log::WARN, "Making egl Current failed");
+ vgmutex.Unlock();
+ return 0;
+ }
+ // Test stuff
+
+ query_str=(const char*)vgGetString(VG_VERSION) ;
+ if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);
+ else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",vgGetError());
+
+ query_str=(const char*)vgGetString(VG_VENDOR) ;
+ if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);
+ else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",vgGetError());
+
+ query_str=(const char*)vgGetString(VG_RENDERER) ;
+ if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);
+ else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",vgGetError());
+
+ query_str=(const char*)vgGetString(VG_EXTENSIONS) ;
+ if (query_str) Log::getInstance()->logLongString("OSD", Log::NOTICE, query_str);
+ else Log::getInstance()->log("OSD", Log::WARN, "Could not query display %x",vgGetError());
+
+ aspect_correction= ((float)BACKBUFFER_HEIGHT)/576.f/(((float)BACKBUFFER_WIDTH)/720.f);
+ initPaths();
+ if (!loadFont()) {
+ return 0;
+ }
+ vgttfont=vgCreateFont(0);
+ vgttpaint=vgCreatePaint();
+ vgSetParameteri( vgttpaint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
+ vgSetColor(vgttpaint,0xffffffff);
+
+ eglSwapInterval(egl_display, 1 );
+
+ Log::getInstance()->log("OSD", Log::DEBUG, "Making egl current out 1%d",syscall(SYS_gettid));
+ eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );
+ //Now we will create the Screen
+ initted = 1; // must set this here or create surface won't work
+
+
+ /*if (((VideoOMX*)Video::getInstance())->initUsingOSDObjects()!=1) { //call Video for init stuff
+ return 0;
+ }*/
+ InitializeMagick("");
+
+ pthread_cond_init(&vgtaskCond, NULL);
+ pthread_mutex_init(&vgtaskCondMutex, NULL);
+
+ threadStart();
+ taskmutex.Unlock();
+ vgmutex.Unlock();
+
+
+ return 1;
+}
+
+
+void OsdOpenVG::initPaths()
+{
+
+
+ VGPath current;
+ current=vgCreatePath(VG_PATH_FORMAT_STANDARD,
+ VG_PATH_DATATYPE_F,1.f,0.f,
+ 0,0,VG_PATH_CAPABILITY_ALL);
+
+ vguLine(current,0.f,0.f,1.f,0.f);
+ // HorzLine
+ std_paths[HorzLine]=current;
+
+ current=vgCreatePath(VG_PATH_FORMAT_STANDARD,
+ VG_PATH_DATATYPE_F,1.f,0.f,
+ 0,0,VG_PATH_CAPABILITY_ALL);
+ vguLine(current,0.f,0.f,0.f,1.f);
+ // VertLine
+ std_paths[VertLine]=current;
+
+ current=vgCreatePath(VG_PATH_FORMAT_STANDARD,
+ VG_PATH_DATATYPE_F,1.f,0.f,
+ 0,0,VG_PATH_CAPABILITY_ALL);
+ //vguRect(current,0.f,0.f,1.f,1.f);
+ vguRect(current,0.f,0.f,1.f,1.f);
+ // Rectabgle
+ std_paths[Rectangle]=current;
+
+ current=vgCreatePath(VG_PATH_FORMAT_STANDARD,
+ VG_PATH_DATATYPE_F,1.f,0.f,
+ 0,0,0);
+ vguEllipse(current,0.f,0.f,1.f,1.f);
+ // Point
+ std_paths[Point]=current;
+
+}
+
+void OsdOpenVG::destroyPaths()
+{
+ vgDestroyPath(std_paths[HorzLine]);
+ vgDestroyPath(std_paths[VertLine]);
+ vgDestroyPath(std_paths[Rectangle]);
+ vgDestroyPath(std_paths[Point]);
+
+}
+
+int OsdOpenVG::stopUpdate()
+{
+ threadStop();
+ processTasks();
+ return 1;
+}
+
+
+int OsdOpenVG::shutdown()
+{
+ if (!initted) return 0;
+
+ initted = 0;
+ Log::getInstance()->log("OSD", Log::DEBUG, "shutdown mark1");
+ threadStop();
+ Log::getInstance()->log("OSD", Log::DEBUG, "shutdown mark2");
+ processTasks();
+ Log::getInstance()->log("OSD", Log::DEBUG, "shutdown mark3");
+
+ taskmutex.Lock();
+ vgmutex.Lock();
+ //(((VideoOMX*)Video::getInstance())->shutdownUsingOSDObjects());
+
+
+
+ vgDestroyFont(vgfont);
+ vgDestroyFont(vgttfont);
+ vgDestroyPaint(vgttpaint);
+ destroyPaths();
+ vgClear(0,0,BACKBUFFER_WIDTH,BACKBUFFER_HEIGHT);
+ eglSwapBuffers(egl_display, egl_surface);
+ Log::getInstance()->log("OSD", Log::DEBUG, "Making egl current out final");
+ eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );
+ eglDestroySurface(egl_display,egl_surface);
+ eglDestroyContext(egl_display,egl_context);
+ eglTerminate(egl_display );
+
+ DISPMANX_UPDATE_HANDLE_T bcm_update;
+ bcm_update=vc_dispmanx_update_start(0);
+
+ vc_dispmanx_element_remove(bcm_update,bcm_element);
+// vc_dispmanx_element_remove(bcm_update,bcm_background);
+ vc_dispmanx_update_submit_sync(bcm_update);
+// vc_dispmanx_resource_delete(bcm_backres);
+ vc_dispmanx_display_close(bcm_display);
+
+
+
+ return 1;
+}
+
+
+
+
+void OsdOpenVG::threadMethod()
+{
+ // We have to claim the egl context for this thread
+
+ if (eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context)== EGL_FALSE) {
+ Log::getInstance()->log("OSD", Log::WARN, "Making egl Current failed in thread %x",eglGetError());
+ return;
+ }
+ int ts=0;
+ while (true)
+ {
+ ts=1;
+ unsigned int waittime=1;
+
+ if (initted) {
+
+ long long time1 = getTimeMS();
+ if ((time1 - lastrendertime) > 200) {//5 fps for OSD updates are enough, avoids tearing
+ InternalRendering();
+ lastrendertime = getTimeMS();
+
+ }
+ if (processTasks()) ts=0;
+ }
+ threadCheckExit();
+ if (ts!=0) {
+ struct timespec target_time;
+ clock_gettime(CLOCK_REALTIME,&target_time);
+ target_time.tv_nsec+=1000000LL*ts;
+ if (target_time.tv_nsec>999999999) {
+ target_time.tv_nsec-=1000000000L;
+ target_time.tv_sec+=1;
+ }
+ threadLock();
+ threadWaitForSignalTimed(&target_time);
+ threadUnlock();
+ }
+ //Sleep(1);
+ }
+ //eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );
+}
+
+
+void OsdOpenVG::threadPostStopCleanup()
+{
+ //Doing nothing
+ //goo;
+}
+
+
+
+
+
+
+void OsdOpenVG::InternalRendering(){
+ vgmutex.Lock();
+ float colclear[]={1.f,1.0f,1.f,1.f};
+ vgSetfv(VG_CLEAR_COLOR,4,colclear);
+ vgClear(0,0,BACKBUFFER_WIDTH,BACKBUFFER_HEIGHT);
+ vgSeti(VG_BLEND_MODE, VG_BLEND_SRC);
+
+
+ drawSurfaces(); //iterate through and draws all commands
+
+ //Show it to the user!
+ eglSwapBuffers(egl_display, egl_surface);
+ vgmutex.Unlock();
+
+}
+
+/*font stuff*/
+
+float OsdOpenVG::getFontHeight()
+{
+ return font_height; //dummy
+}
+float OsdOpenVG::getCharWidth(wchar_t c)
+{
+ unsigned int glyph_index=FT_Get_Char_Index(ft_face,c);
+ return font_exp_x[glyph_index];
+}
+
+unsigned int OsdOpenVG::loadTTchar(cTeletextChar c)
+{
+ unsigned int glyph_index=c.getGlyphIndex();
+ if (tt_font_chars.find(glyph_index)!=tt_font_chars.end())
+ {
+ return glyph_index;
+ }
+
+ unsigned int buffer[10];
+ const VGfloat glyphOrigin[] = { 0.f, 0.f };
+ const VGfloat escapement[] = { 12.f, 0.f };
+ unsigned int * charmap = GetFontChar(c, buffer);
+ if (!charmap) { //invalid char
+ return 0;
+ }
+ for (int i=0;i<10;i++) {
+ buffer[i]=charmap[i]>>4;
+ }
+
+ VGImage handle = vgCreateImage(
+ VG_A_8,
+ 12,
+ 10,
+ VG_IMAGE_QUALITY_NONANTIALIASED | VG_IMAGE_QUALITY_FASTER
+ | VG_IMAGE_QUALITY_BETTER);
+ vgImageSubData(handle, buffer, 4, VG_A_1, 0, 0, 12, 10);
+ vgSetGlyphToImage(
+ vgttfont,
+ glyph_index,
+ handle, glyphOrigin, escapement);
+ vgDestroyImage(handle);
+ tt_font_chars[glyph_index]=1;
+
+ return glyph_index;
+}
+
+int OsdOpenVG::loadFont()
+{
+ int error;
+ float font_size=16.f;
+ if (!freetype_inited) {
+ error=FT_Init_FreeType(&ft_library);
+ if (error)
+ {
+ Log::getInstance()->log("OSD", Log::WARN, "Could not load freetype %x",error);
+ return 0;
+ }
+
+ error=FT_New_Memory_Face(ft_library,font_data,font_data_end-font_data,0,&ft_face );
+ if (error) {
+ Log::getInstance()->log("OSD", Log::WARN, "Could not load font face %x",error);
+ return 0;
+ }
+ error=FT_Set_Char_Size(ft_face,0,font_size*64,0,0 /*dpi*/);
+ if (error) {
+ FT_Done_Face(ft_face);
+ Log::getInstance()->log("OSD", Log::WARN, "Could not set face size %x",error);
+ return 0;
+ }
+ freetype_inited=true;
+ }
+ vgfont=vgCreateFont(0);
+ FT_ULong cur_char;
+ FT_UInt glyph;
+ font_height=ft_face->size->metrics.height/64.f;
+ cur_char = FT_Get_First_Char(ft_face,&glyph);
+ vector<VGubyte> segments;
+ vector<VGfloat> coord;
+ segments.reserve(256);
+ coord.reserve(1024);
+ //Log::getInstance()->log("OSD", Log::DEBUG, "Create Glyph test %d %x %x %d",cur_char,font_data_end,font_data,glyph);
+ while (glyph !=0)
+ {
+ error=FT_Load_Glyph(ft_face,glyph,FT_LOAD_DEFAULT);
+ if (error){
+ FT_Done_Face(ft_face);
+ Log::getInstance()->log("OSD", Log::WARN, "Could not load glyph %x",error);
+ return 0;
+ }
+ VGPath path;
+ FT_Outline ot=ft_face->glyph->outline;
+ segments.clear();
+ coord.clear();
+
+ if (ot.n_contours ==0) {
+ path=VG_INVALID_HANDLE;
+ } else {
+ path=vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
+ 1.0f,0.f,0,0,VG_PATH_CAPABILITY_ALL);
+
+ /* convert glyph */
+ FT_Vector *pt=ot.points;
+ const char *tags=ot.tags;
+ const short* cont=ot.contours;
+ short n_cont=ot.n_contours;
+ short n_point=ot.n_points;
+ short last_cont=0;
+ for (short point=0;n_cont!=0;cont++,n_cont--) {
+ short next_cont=*cont+1;
+ bool first=true;
+ char last_tag=0;
+ short first_point=point;
+ //Log::getInstance()->log("OSD", Log::DEBUG, "runs %d",*cont);
+ for (;point<next_cont;point++) {
+ char tag=tags[point];
+ FT_Vector fpoint=pt[point];
+ // Log::getInstance()->log("OSD", Log::DEBUG, "tag %d point %d %d: %d %d",tag,fpoint.x,fpoint.y,point,n_point);
+ if (first) {
+ segments.push_back(VG_MOVE_TO);
+ first=false;
+ } else if (tag &0x1) { //on curve
+ if (last_tag &0x1) {
+ segments.push_back(VG_LINE_TO);
+ } else if (last_tag &0x2){
+ segments.push_back(VG_CUBIC_TO);
+ } else {
+ segments.push_back(VG_QUAD_TO);
+ }
+
+ } else {
+ if (!(tag &0x2)){
+ if (!(last_tag &0x1)) {
+ segments.push_back(VG_QUAD_TO);
+ int coord_size=coord.size();
+ VGfloat x=(coord[coord_size-2]+ ((float)fpoint.x)/64.f)*0.5f*aspect_correction;
+ VGfloat y=(coord[coord_size-1]+(font_size- ((float)fpoint.y)/64.f))*0.5f;
+ coord.push_back(x);
+ coord.push_back(y);
+ }
+ }
+
+
+ }
+ last_tag=tag;
+ coord.push_back(((float)fpoint.x)*aspect_correction/64.);
+ coord.push_back(font_size-((float)fpoint.y)/64.);
+ //Log::getInstance()->log("OSD", Log::DEBUG, "Create APD Glyph coord %d %d %g %g",fpoint.x,fpoint.y,coord[coord.size()-2],coord[coord.size()-1]);
+ }
+ if (!(last_tag &0x1)) {
+ if (last_tag &0x2) {
+ segments.push_back(VG_CUBIC_TO);
+ } else {
+ segments.push_back(VG_QUAD_TO);
+ }
+ coord.push_back(((float)pt[first_point].x)*aspect_correction/64.);
+ coord.push_back(font_size-((float)pt[first_point].y)/64.);
+ }
+ //segments.push_back(VG_CLOSE_PATH);
+
+
+ }
+ vgAppendPathData(path,segments.size(),&segments[0],&coord[0]);
+ int n=0;
+ /* for (int m=0;m<segments.size();m++) {
+ switch (segments[m])
+ {
+ case VG_MOVE_TO:
+ Log::getInstance()->log("OSD", Log::DEBUG, "Move To %g %g",coord[n],coord[n+1]);n+=2; break;
+ case VG_LINE_TO:
+ Log::getInstance()->log("OSD", Log::DEBUG, "Line To %g %g",coord[n],coord[n+1]);n+=2; break;
+ case VG_CUBIC_TO:
+ Log::getInstance()->log("OSD", Log::DEBUG, "Cubic To %g %g %g %g %g %g",coord[n],coord[n+1],coord[n+2],coord[n+3],coord[n+4],coord[n+5]);n+=6; break;
+ case VG_QUAD_TO:
+ Log::getInstance()->log("OSD", Log::DEBUG, "Quad To %g %g %g %g",coord[n],coord[n+1],coord[n+2],coord[n+3]);n+=4; break;
+ case VG_CLOSE_PATH:
+ Log::getInstance()->log("OSD", Log::DEBUG, "Close Path"); break;
+ }
+
+ }*/
+ //vguRect(path,0.f,0.f,1.f,1.f);
+ //Log::getInstance()->log("OSD", Log::DEBUG, "Create APD Glyph %d %x",segments.size(),vgGetError());
+ }
+ VGfloat ori[]={0.f,0.f};
+ VGfloat esp[]={ft_face->glyph->advance.x/64.f*aspect_correction,ft_face->glyph->advance.y/64.f};
+ font_exp_x[glyph]=ft_face->glyph->advance.x/64.f*aspect_correction; //recalculate
+
+ vgSetGlyphToPath(vgfont,glyph,path,VG_FALSE,ori,esp);
+ //Log::getInstance()->log("OSD", Log::DEBUG, "Create Glyph %d %d %x",path,glyph,vgGetError());
+ if (path!=VG_INVALID_HANDLE) {
+ vgDestroyPath(path);
+ }
+ cur_char = FT_Get_Next_Char(ft_face,cur_char,&glyph);
+ }
+ return 1;
+}
+
+
+void OsdOpenVG::drawSetTrans(SurfaceCommands & sc)
+{
+ vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
+ vgLoadIdentity();
+ vgScale(((float)BACKBUFFER_WIDTH)/720.f, -((float)BACKBUFFER_HEIGHT)/576.f);
+ vgTranslate(0.f+sc.x,-576.f+sc.y);
+
+ vgSeti(VG_MATRIX_MODE, VG_MATRIX_GLYPH_USER_TO_SURFACE);
+ vgLoadIdentity();
+ vgScale(((float)BACKBUFFER_WIDTH)/720.f, -((float)BACKBUFFER_HEIGHT)/576.f);
+ vgTranslate(0.f+sc.x,-576.f+sc.y);
+
+ vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);
+ vgLoadIdentity();
+ vgScale(((float)BACKBUFFER_WIDTH)/720.f, -((float)BACKBUFFER_HEIGHT)/576.f);
+ vgTranslate(0.f+sc.x,-576.f+sc.y);
+
+
+
+ //vgTranslate(0.f+sc.x,576.f-sc.y);
+ //Log::getInstance()->log("OSD", Log::DEBUG, "Draw Translate %g %g",sc.x,sc.y);
+
+}
+void OsdOpenVG::executeDrawCommand(SVGCommand & command)
+{
+
+ VGfloat save_matrix[9];
+ switch (command.instr) {
+ case DrawPath: {
+ vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
+ // VGuint rgba;
+ // rgba = vgGetColor((VGPaint) command.reference);
+ //Log::getInstance()->log("OSD", Log::DEBUG, "Draw Path %d %x %g %g %g %g",command.reference,command.target.path_index,command.x,command.y,command.w,command.h);
+ //vgSeti(VG_FILL_RULE,);
+
+ vgGetMatrix(save_matrix);
+ vgSetPaint((VGPaint) command.reference,VG_FILL_PATH);
+ vgSetPaint((VGPaint) command.reference,VG_STROKE_PATH);
+ vgTranslate(command.x,command.y);
+ vgScale(command.w,command.h);
+ vgDrawPath(std_paths[command.target.path_index],VG_FILL_PATH);
+ vgLoadMatrix(save_matrix);
+ } break;
+ case DrawImage: {
+ vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);
+ vgGetMatrix(save_matrix);
+ vgTranslate(command.x,command.y);
+ //vgScale(command.w,command.h);
+ if (command.reference) { //special behaviout for bw images they act as a mask on the current paint
+ vgSetPaint((VGPaint) command.reference,VG_FILL_PATH);
+ vgSetPaint((VGPaint) command.reference,VG_STROKE_PATH);
+ vgSeti(VG_IMAGE_MODE,VG_DRAW_IMAGE_STENCIL);
+ vgSeti(VG_BLEND_MODE, VG_BLEND_SRC_OVER);
+ vgScale(aspect_correction,1.f);
+ } else {
+ VGfloat imagewidth=vgGetParameteri((VGImage) command.target.image, VG_IMAGE_WIDTH);
+ VGfloat imageheight=vgGetParameteri((VGImage) command.target.image, VG_IMAGE_HEIGHT);
+ //vgScale(720.f/((float)BACKBUFFER_WIDTH), 576.f/((float)BACKBUFFER_HEIGHT));
+ float scalex=command.w/imagewidth;
+ float scaley=command.h/imageheight;
+ //vgScale(command.w/imagewidth,command.h/imageheight);
+ vgScale(scalex,scaley);
+ vgSeti(VG_IMAGE_MODE,VG_DRAW_IMAGE_NORMAL);
+ //Log::getInstance()->log("OSD", Log::DEBUG, "Draw Image Scale %g %g %g %g %g %g",command.w,imagewidth,command.h,imageheight,scalex,scaley);
+ }
+
+
+ //vgLoadIdentity();
+ //vgTranslate(200.f,500.f);
+ //vgScale(100.f,100.f);
+
+ vgDrawImage((VGImage) command.target.image);
+ //Log::getInstance()->log("OSD", Log::DEBUG, "Draw Image %d %x %g %g %g %g %x",command.reference,command.target.image,command.x,command.y,command.w,command.h,
+ // vgGetError());
+ if (command.reference) {
+ vgSeti(VG_IMAGE_MODE,VG_DRAW_IMAGE_NORMAL);
+ vgSeti(VG_BLEND_MODE, VG_BLEND_SRC);
+ }
+ vgLoadMatrix(save_matrix);
+ } break;
+ case DrawGlyph: {
+ vgSeti(VG_MATRIX_MODE, VG_MATRIX_GLYPH_USER_TO_SURFACE);
+ vgGetMatrix(save_matrix);
+ vgSetPaint((VGPaint) command.reference,VG_FILL_PATH);
+ vgSetPaint((VGPaint) command.reference,VG_STROKE_PATH);
+ vgTranslate(command.x,command.y);
+ VGfloat gori[]={0.,0.};
+ vgSetfv(VG_GLYPH_ORIGIN,2,gori);
+
+ unsigned int glyph_index=FT_Get_Char_Index(ft_face,command.target.textchar);
+ vgDrawGlyph(vgfont,glyph_index,VG_FILL_PATH,VG_FALSE);
+ //vgDrawPath(std_paths[Rectangle],VG_FILL_PATH);
+ /* Log::getInstance()->log("OSD", Log::DEBUG, "Draw Glyph %d %c %d %g %g %x",command.reference,command.target.textchar,glyph_index,command.x,command.y,
+ vgGetError());*/
+ vgLoadMatrix(save_matrix);
+ vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
+ } break;
+ case DrawTTchar:{
+ cTeletextChar tchar;
+ tchar.setInternal(command.target.ttchar);
+ vgSeti(VG_MATRIX_MODE, VG_MATRIX_GLYPH_USER_TO_SURFACE);
+ vgGetMatrix(save_matrix);
+ enumTeletextColor ttforegcolour=tchar.GetFGColor();
+ enumTeletextColor ttbackgcolour=tchar.GetBGColor();
+ if (tchar.GetBoxedOut()) {
+ ttforegcolour=ttcTransparent;
+ ttbackgcolour=ttcTransparent;
+ }
+ vgSeti(VG_IMAGE_MODE,VG_DRAW_IMAGE_STENCIL);
+ vgSeti(VG_BLEND_MODE, VG_BLEND_SRC_OVER);
+
+
+ vgTranslate(command.x+command.w*11.85f*1.4f,command.y+command.h*19.75f);
+ VGfloat gori[]={0.,0.};
+ vgSetfv(VG_GLYPH_ORIGIN,2,gori);
+
+ vgScale(-1.4f,2.f);
+ unsigned int color=Surface::enumTeletextColorToCoulour(ttbackgcolour).rgba();
+ color=color<<8 | (color &0xff000000)>>24;
+ vgSetColor(vgttpaint,color);
+ vgSetPaint((VGPaint) vgttpaint,VG_FILL_PATH);
+ vgSetPaint((VGPaint) vgttpaint,VG_STROKE_PATH);
+ cTeletextChar filled;
+ filled.setInternal(0x187f);
+ unsigned int glyph_index=loadTTchar(filled);
+ vgDrawGlyph(vgttfont,glyph_index,VG_FILL_PATH,VG_FALSE);
+
+ color=Surface::enumTeletextColorToCoulour(ttforegcolour).rgba();
+ color=color<<8 | (color &0xff000000)>>24;
+ vgSetColor(vgttpaint,color);
+ vgSetPaint((VGPaint) vgttpaint,VG_FILL_PATH);
+ vgSetPaint((VGPaint) vgttpaint,VG_STROKE_PATH);
+ glyph_index=loadTTchar(tchar);
+ vgDrawGlyph(vgttfont,glyph_index,VG_FILL_PATH,VG_FALSE);
+
+ /* Log::getInstance()->log("OSD", Log::DEBUG, "Draw TTchar %x %x %x %x",glyph_index,ttforegcolour,Surface::enumTeletextColorToCoulour(ttforegcolour).rgba(),
+ vgGetColor(vgttpaint));*/
+
+
+ vgLoadMatrix(save_matrix);
+ vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
+ vgSeti(VG_IMAGE_MODE,VG_DRAW_IMAGE_NORMAL);
+ vgSeti(VG_BLEND_MODE, VG_BLEND_SRC);
+
+ }break;
+ }
+}
+
+unsigned int OsdOpenVG::handleTask(OpenVGCommand& command)
+{
+ switch (command.task){
+ case OVGdestroyImageRef: {
+ vgDestroyImage((VGImage)command.param1);
+ return 0;
+ } break;
+ case OVGdestroyPaint: {
+ Log::getInstance()->log("OSD", Log::DEBUG, "Draw Paint Destroy %d ",command.param1);
+ vgDestroyPaint((VGPaint)command.param1);
+ return 0;
+ } break;
+ case OVGcreateImagePalette: {
+ VGImage input=vgCreateImage(VG_A_8,command.param1, command.param2,
+ VG_IMAGE_QUALITY_NONANTIALIASED|
+ VG_IMAGE_QUALITY_FASTER|VG_IMAGE_QUALITY_BETTER);
+ vgImageSubData(input,command.data,command.param1,
+ VG_A_8,0,0,command.param1, command.param2); // upload palettized image data
+ VGImage handle=vgCreateImage(VG_sRGBA_8888,command.param1, command.param2,
+ VG_IMAGE_QUALITY_NONANTIALIASED|
+ VG_IMAGE_QUALITY_FASTER|VG_IMAGE_QUALITY_BETTER);
+ VGuint *palette=(VGuint*)malloc(256*sizeof(VGuint));
+ VGuint *in_palette=(VGuint*)command.data2;
+ for (int i=0;i<256;i++) {
+ VGuint color=in_palette[i];
+ palette[i]=color<<8 | (color &0xff000000)>>24;
+ }
+
+ vgLookupSingle(handle,input,palette,VG_ALPHA,VG_FALSE,VG_FALSE);
+ free(palette);
+ vgDestroyImage(input);
+
+ return handle;
+ } break;
+ case OVGcreateMonoBitmap: {
+ VGImage handle=vgCreateImage(VG_A_1,command.param1, command.param2,
+ VG_IMAGE_QUALITY_NONANTIALIASED|
+ VG_IMAGE_QUALITY_FASTER|VG_IMAGE_QUALITY_BETTER);
+ //Log::getInstance()->log("OSD", Log::DEBUG, "Draw create mono %d %d %x %d",command.param1,command.param2,vgGetError(),handle);
+ unsigned int buffer_len=(command.param1*command.param2)>>3;
+ unsigned char * buffer=(unsigned char*)malloc(buffer_len);
+ unsigned char * r_buffer1=buffer;
+ const unsigned char * r_buffer2=(const unsigned char *)command.data;
+ unsigned char *buffer_end=buffer+buffer_len;
+ while (r_buffer1!=buffer_end) {
+ unsigned char byte=*r_buffer2;
+ *r_buffer1=((byte * 0x0802LU & 0x22110LU) | (byte * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16;
+ r_buffer1++;r_buffer2++;
+ }
+
+
+ vgImageSubData(handle,buffer,command.param1>>3,
+ VG_A_1,0,0,command.param1, command.param2);
+ free(buffer);
+ // Log::getInstance()->log("OSD", Log::DEBUG, "Draw create mono2 %d %d %x %d",command.param1,command.param2,vgGetError(),handle);
+ return handle;
+ } break;
+ case OVGcreateImageFile: {
+ VGImage handle;
+ try{
+ Image *imagefile=(Image*)command.data;
+ Blob imageblob;
+ imagefile->write(&imageblob,"RGBA");
+
+
+ handle=vgCreateImage(VG_sXBGR_8888,imagefile->columns(),imagefile->rows(),
+ VG_IMAGE_QUALITY_NONANTIALIASED|
+ VG_IMAGE_QUALITY_FASTER|VG_IMAGE_QUALITY_BETTER);
+ // Log::getInstance()->log("OSD", Log::DEBUG, "Draw create image details %d %d %x mark1",imagefile->columns(),imagefile->rows(),*(unsigned int*)imageblob.data());
+ vgImageSubData(handle,imageblob.data(),imagefile->columns()*4,
+ VG_sXBGR_8888,0,0,imagefile->columns(),imagefile->rows());
+ // Log::getInstance()->log("OSD", Log::DEBUG, "Draw create image details %d %d %x mark2",imagefile->columns(),imagefile->rows(),*(unsigned int*)imageblob.data());
+ delete imagefile;
+ }catch( Exception &error_ )
+ {
+ Log::getInstance()->log("OSD", Log::DEBUG, "Libmagick hT: %s",error_.what());
+
+ return 0;
+ }
+
+ //Log::getInstance()->log("OSD", Log::DEBUG, "Draw create file %d %d %x %d",command.param1,command.param2,vgGetError(),handle);
+ return handle;
+ } break;
+ case OVGcreateColorRef :{
+ VGPaint handle;
+ handle=vgCreatePaint();
+ vgSetParameteri(handle, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
+ vgSetColor(handle,command.param1);
+ VGuint rgba;
+ rgba = vgGetColor((VGPaint)handle);
+ Log::getInstance()->log("OSD", Log::DEBUG, "Draw Paint %d %x %x",handle,command.param1,rgba);
+ return handle;
+ } break;
+ case OVGimageUploadLine: {
+ vgImageSubData((VGImage)command.param1,command.data,0,VG_sARGB_8888,0,command.param2,command.param3,1);
+ return 0;
+ } break;
+
+ }
+}
+
+bool OsdOpenVG::processTasks()
+{
+ bool worked=false;
+ taskmutex.Lock();
+ vgmutex.Lock();
+ while (vgcommands.size()>0)
+ {
+ OpenVGCommand &comm=vgcommands.front();
+ OpenVGResponse resp;
+ resp.result=handleTask(comm);
+ resp.id=comm.id;
+ if (comm.id) {
+ vgresponses.push_back(resp);
+ }
+ vgcommands.pop_front();
+ taskmutex.Unlock();
+ vgmutex.Unlock();
+ //threadCheckExit();
+ pthread_mutex_lock(&vgtaskCondMutex);
+ pthread_cond_signal(&vgtaskCond);
+ pthread_mutex_unlock(&vgtaskCondMutex);
+ taskmutex.Lock();
+ vgmutex.Lock();
+ worked=true;
+ }
+ taskmutex.Unlock();
+ vgmutex.Unlock();
+
+ return worked;
+}
+
+bool OsdOpenVG::haveOpenVGResponse(unsigned int id,unsigned int * resp)
+{
+ taskmutex.Lock();
+ if (vgresponses.size()>0)
+ {
+ deque<OpenVGResponse>::iterator itty=vgresponses.begin();
+ while (itty!=vgresponses.end())
+ {
+ if ((*itty).id==id) {
+ *resp=(*itty).result;
+ taskmutex.Unlock();
+ return true;
+ }
+ itty++;
+ }
+ }
+ taskmutex.Unlock();
+ return false;
+}
+
+
+unsigned int OsdOpenVG::putOpenVGCommand(OpenVGCommand& comm,bool wait)
+{
+ taskmutex.Lock();
+ if (wait){
+ comm.id=wait_id;
+ wait_id++;
+ } else {
+ comm.id=0; // we are not waiting
+ }
+ vgcommands.push_back(comm);
+ taskmutex.Unlock();
+ threadSignal();
+ while (wait) {
+ unsigned int resp;
+ if (!haveOpenVGResponse(comm.id,&resp)) {
+ struct timespec target_time;
+ clock_gettime(CLOCK_REALTIME,&target_time);
+ target_time.tv_nsec+=1000000LL*100;
+ if (target_time.tv_nsec>999999999) {
+ target_time.tv_nsec-=1000000000L;
+ target_time.tv_sec+=1;
+ }
+ pthread_mutex_lock(&vgtaskCondMutex);
+ pthread_cond_timedwait(&vgtaskCond, &vgtaskCondMutex,&target_time);
+ pthread_mutex_unlock(&vgtaskCondMutex);
+ } else {
+ return resp;
+ }
+ }
+ return 0;
+}
+
+void OsdOpenVG::imageUploadLine(ImageIndex index,unsigned int j,unsigned int width,void *data)
+{
+ vgImageSubData((VGImage)index,data,0,VG_sARGB_8888,0,j,width,1);
+
+ struct OpenVGCommand comm;
+ comm.task=OVGimageUploadLine;
+ comm.param1=index;
+ comm.param2=j;
+ comm.param3=width;
+ comm.data=data;
+ putOpenVGCommand(comm,true);
+}
+
+void OsdOpenVG::destroyImageRef(ImageIndex index)
+{
+ struct OpenVGCommand comm;
+ comm.task=OVGdestroyImageRef;
+ comm.param1=index;
+ putOpenVGCommand(comm,false);
+}
+
+ImageIndex OsdOpenVG::createJpeg(const char* fileName, int *width,int *height)
+{
+ Image* magicimage=NULL;
+ bool mem=false;
+ struct OpenVGCommand comm;
+ comm.task=OVGcreateImageFile;
+
+ try{
+ // Now figure out, if it is a special case
+ if (strcmp(fileName,"/vdr.jpg")==0) {
+ magicimage=new Image(Blob(vdr_data,vdr_data_end-vdr_data));
+ *height=100; // this is faked so that the system does use the old coordinate system
+ *width=ceil(190.f*aspect_correction);
+ Log::getInstance()->log("OSD", Log::DEBUG, "LoadIm vdr.jpg");
+ } else if (strcmp(fileName,"/wallpaperPAL.jpg")==0) {
+ magicimage=new Image(Blob(wallpaper_data,wallpaper_data_end-wallpaper_data));
+ *width=720; // this is faked so that the system does use the old coordinate system
+ *height=576;
+ Log::getInstance()->log("OSD", Log::DEBUG, "LoadIm wallpaperPAL.jpg");
+ } else {
+ magicimage=new Image();
+ magicimage->read(fileName);
+ Log::getInstance()->log("OSD", Log::DEBUG, "LoadIm file: %s",fileName);
+ *width=ceil(magicimage->baseColumns()*aspect_correction); // this is faked so that the system does use the old coordinate system
+ *height=magicimage->baseRows();
+ }
+
+ }catch( Exception &error_ )
+ {
+ Log::getInstance()->log("OSD", Log::DEBUG, "Libmagick: %s",error_.what());
+
+ return 0;
+ }
+ comm.data=magicimage;
+ return putOpenVGCommand(comm,true);
+}
+
+ImageIndex OsdOpenVG::createMonoBitmap(void *base,int width,int height)
+{
+ struct OpenVGCommand comm;
+ comm.task=OVGcreateMonoBitmap;
+ comm.param1=width;
+ comm.param2=height;
+ comm.data=base;
+ return putOpenVGCommand(comm,true);
+}
+
+ImageIndex OsdOpenVG::createImagePalette(int width,int height,const unsigned char *image_data,const unsigned int*palette_data)
+{
+ struct OpenVGCommand comm;
+ comm.task=OVGcreateImagePalette;
+ comm.param1=width;
+ comm.param2=height;
+ comm.data=image_data;
+ comm.data2=palette_data;
+ return putOpenVGCommand(comm,true);
+}
+
+void OsdOpenVG::destroyStyleRef(unsigned int index)
+{
+ struct OpenVGCommand comm;
+ comm.task=OVGdestroyPaint;
+ comm.param1=index;
+ putOpenVGCommand(comm,false);
+}
+
+unsigned int OsdOpenVG::createStyleRef(const DrawStyle &c)
+{
+ unsigned int col=c.rgba();
+ struct OpenVGCommand comm;
+ comm.task=OVGcreateColorRef;
+ comm.param1=col<<8 | (col &0xff000000)>>24;
+ comm.data=&c;
+ return putOpenVGCommand(comm,true);
+}
+
+unsigned int OsdOpenVG::createColorRef(const Colour &c)
+{
+ unsigned int col=c.rgba();
+ struct OpenVGCommand comm;
+ comm.task=OVGcreateColorRef;
+ comm.param1=col<<8 | (col &0xff000000)>>24;
+ comm.data=&c;
+ return putOpenVGCommand(comm,true);
+}
+
+
-/*\r
- Copyright 2004-2005 Chris Tallon, 2006,2011-2012 Marten Richter\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef OSDOPENVG_H\r
-#define OSDOPENVG_H\r
-\r
-#include <stdio.h>\r
-\r
-\r
-#include <EGL/egl.h>\r
-#include <EGL/eglext.h>\r
-#include <VG/openvg.h>\r
-#include <VG/vgu.h>\r
-\r
-\r
-\r
-#include "osdvector.h"\r
-#include "defines.h"\r
-#include "log.h"\r
-#include "threadp.h"\r
-#include "mutex.h"\r
-#include "videoomx.h"\r
-\r
-#include <deque>\r
-\r
-#include <ft2build.h>\r
-#include FT_FREETYPE_H\r
-\r
-enum OpenVGTask {\r
- OVGdestroyImageRef,\r
- OVGdestroyPaint,\r
- OVGcreateImagePalette,\r
- OVGcreateMonoBitmap,\r
- OVGcreateColorRef,\r
- OVGimageUploadLine,\r
- OVGcreateImageFile\r
-};\r
-\r
-struct OpenVGCommand\r
-{\r
- enum OpenVGTask task;\r
- const void *data;\r
- const void *data2;\r
- unsigned int param1,param2,param3;\r
- unsigned int id; //only set an id if you are waiting\r
-};\r
-\r
-struct OpenVGResponse{\r
- unsigned int id;\r
- unsigned int result;\r
-};\r
-\r
-\r
-class OsdOpenVG : public OsdVector, public Thread_TYPE\r
-{\r
- public:\r
- OsdOpenVG();\r
- virtual ~OsdOpenVG();\r
-\r
- int init(void* device);\r
- int shutdown();\r
- int stopUpdate();\r
-\r
-\r
-\r
-\r
- float getFontHeight();\r
- float getCharWidth(wchar_t c);\r
- void imageUploadLine(ImageIndex index,unsigned int j,unsigned int width,void *data);\r
-\r
-\r
-protected:\r
- /*osd vector implementation*/\r
- void destroyImageRef(ImageIndex index);\r
- ImageIndex createJpeg(const char* fileName, int *width,int *height);\r
- ImageIndex createMonoBitmap(void *base,int width,int height);\r
- ImageIndex createImagePalette(int width,int height,const unsigned char *image_data,const unsigned int*palette_data);\r
- void destroyStyleRef(unsigned int index);\r
- unsigned int createStyleRef(const DrawStyle &c);\r
- unsigned int createColorRef(const Colour &c);\r
-\r
- void drawSetTrans(SurfaceCommands & sc);\r
- void executeDrawCommand(SVGCommand & command);\r
-\r
- void initPaths();\r
- void destroyPaths();\r
- VGPath std_paths[Point+1];\r
- long long lastrendertime;\r
- void InternalRendering();\r
-\r
-\r
-\r
- Mutex vgmutex;\r
- Mutex taskmutex;\r
- pthread_cond_t vgtaskCond;\r
- pthread_mutex_t vgtaskCondMutex;\r
- deque<OpenVGCommand> vgcommands;\r
- deque<OpenVGResponse> vgresponses;\r
- bool processTasks();\r
- bool haveOpenVGResponse(unsigned int id,unsigned int * resp);\r
- unsigned int putOpenVGCommand(OpenVGCommand& comm,bool wait);\r
- unsigned int handleTask(OpenVGCommand& command);\r
- unsigned int wait_id;\r
-\r
- FT_Library ft_library;\r
- FT_Face ft_face;\r
- VGFont vgfont;\r
- VGFont vgttfont;\r
- VGPaint vgttpaint;\r
- int loadFont();\r
- map<unsigned int,float> font_exp_x;\r
-\r
- unsigned int loadTTchar(cTeletextChar c);\r
- map<unsigned int,int> tt_font_chars;\r
-\r
-\r
-\r
- void threadMethod();\r
- void threadPostStopCleanup();\r
-\r
-\r
- /* BCM specific */\r
-\r
- uint32_t display_height;\r
- uint32_t display_width;\r
- DISPMANX_DISPLAY_HANDLE_T bcm_display;\r
- DISPMANX_ELEMENT_HANDLE_T bcm_element;\r
-// DISPMANX_ELEMENT_HANDLE_T bcm_background;\r
-// DISPMANX_RESOURCE_HANDLE_T bcm_backres;\r
-\r
- uint32_t mode;\r
-\r
-\r
- EGLDisplay egl_display;\r
- EGLSurface egl_surface;\r
- EGLContext egl_context;\r
- EGLConfig egl_ourconfig;\r
- float font_height;\r
- float aspect_correction;\r
- bool freetype_inited;\r
-\r
-\r
-};\r
-\r
-#endif\r
+/*
+ Copyright 2004-2005 Chris Tallon, 2006,2011-2012 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 OSDOPENVG_H
+#define OSDOPENVG_H
+
+#include <stdio.h>
+
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <VG/openvg.h>
+#include <VG/vgu.h>
+
+
+
+#include "osdvector.h"
+#include "defines.h"
+#include "log.h"
+#include "threadp.h"
+#include "mutex.h"
+#include "videoomx.h"
+
+#include <deque>
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+enum OpenVGTask {
+ OVGdestroyImageRef,
+ OVGdestroyPaint,
+ OVGcreateImagePalette,
+ OVGcreateMonoBitmap,
+ OVGcreateColorRef,
+ OVGimageUploadLine,
+ OVGcreateImageFile
+};
+
+struct OpenVGCommand
+{
+ enum OpenVGTask task;
+ const void *data;
+ const void *data2;
+ unsigned int param1,param2,param3;
+ unsigned int id; //only set an id if you are waiting
+};
+
+struct OpenVGResponse{
+ unsigned int id;
+ unsigned int result;
+};
+
+
+class OsdOpenVG : public OsdVector, public Thread_TYPE
+{
+ public:
+ OsdOpenVG();
+ virtual ~OsdOpenVG();
+
+ int init(void* device);
+ int shutdown();
+ int stopUpdate();
+
+
+
+
+ float getFontHeight();
+ float getCharWidth(wchar_t c);
+ void imageUploadLine(ImageIndex index,unsigned int j,unsigned int width,void *data);
+
+
+protected:
+ /*osd vector implementation*/
+ void destroyImageRef(ImageIndex index);
+ ImageIndex createJpeg(const char* fileName, int *width,int *height);
+ ImageIndex createMonoBitmap(void *base,int width,int height);
+ ImageIndex createImagePalette(int width,int height,const unsigned char *image_data,const unsigned int*palette_data);
+ void destroyStyleRef(unsigned int index);
+ unsigned int createStyleRef(const DrawStyle &c);
+ unsigned int createColorRef(const Colour &c);
+
+ void drawSetTrans(SurfaceCommands & sc);
+ void executeDrawCommand(SVGCommand & command);
+
+ void initPaths();
+ void destroyPaths();
+ VGPath std_paths[Point+1];
+ long long lastrendertime;
+ void InternalRendering();
+
+
+
+ Mutex vgmutex;
+ Mutex taskmutex;
+ pthread_cond_t vgtaskCond;
+ pthread_mutex_t vgtaskCondMutex;
+ deque<OpenVGCommand> vgcommands;
+ deque<OpenVGResponse> vgresponses;
+ bool processTasks();
+ bool haveOpenVGResponse(unsigned int id,unsigned int * resp);
+ unsigned int putOpenVGCommand(OpenVGCommand& comm,bool wait);
+ unsigned int handleTask(OpenVGCommand& command);
+ unsigned int wait_id;
+
+ FT_Library ft_library;
+ FT_Face ft_face;
+ VGFont vgfont;
+ VGFont vgttfont;
+ VGPaint vgttpaint;
+ int loadFont();
+ map<unsigned int,float> font_exp_x;
+
+ unsigned int loadTTchar(cTeletextChar c);
+ map<unsigned int,int> tt_font_chars;
+
+
+
+ void threadMethod();
+ void threadPostStopCleanup();
+
+
+ /* BCM specific */
+
+ uint32_t display_height;
+ uint32_t display_width;
+ DISPMANX_DISPLAY_HANDLE_T bcm_display;
+ DISPMANX_ELEMENT_HANDLE_T bcm_element;
+// DISPMANX_ELEMENT_HANDLE_T bcm_background;
+// DISPMANX_RESOURCE_HANDLE_T bcm_backres;
+
+ uint32_t mode;
+
+
+ EGLDisplay egl_display;
+ EGLSurface egl_surface;
+ EGLContext egl_context;
+ EGLConfig egl_ourconfig;
+ float font_height;
+ float aspect_correction;
+ bool freetype_inited;
+
+
+};
+
+#endif
-/*\r
- Copyright 2012 Marten Richter\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#include "osdvector.h"\r
-#include "surfacevector.h"\r
-\r
-OsdVector::OsdVector()\r
-{\r
- setlocale(LC_CTYPE,"C.UTF-8");\r
-}\r
-\r
-OsdVector::~OsdVector()\r
-{\r
-\r
-}\r
-\r
-\r
-int OsdVector::getFD()\r
-{\r
- return 0;\r
-}\r
-\r
-void OsdVector::screenShot(const char* fileName)\r
-{\r
- //Do nothing,\r
-}\r
-\r
-Surface * OsdVector::createNewSurface()\r
-{\r
- return new SurfaceVector(this);\r
-}\r
-\r
-void OsdVector::BeginPainting()\r
-{\r
- surfaces_mutex.Lock();\r
-}\r
-void OsdVector::EndPainting()\r
-{\r
- surfaces_mutex.Unlock();\r
-}\r
-\r
-void OsdVector::Blank()\r
-{\r
- // do nothing? remove this one?\r
-}\r
-\r
-int OsdVector::restore()\r
-{\r
- // First clear the contents of all registered surfaces\r
- surfaces_mutex.Lock();\r
-\r
- //Now go through all surfaces and draw them\r
- list<SurfaceCommands>::iterator curdraw=scommands.begin();\r
- while (curdraw!=scommands.end()) {\r
- (*curdraw).commands.clear();\r
- curdraw++;\r
- }\r
- //also clear all handles, they are now invalid, no need to release them\r
- images_ref.clear();;\r
- monobitmaps.clear();\r
- jpegs.clear();\r
- styles.clear();\r
- styles_ref.clear();\r
-\r
- surfaces_mutex.Unlock();\r
- return 1;\r
-}\r
-\r
-void OsdVector::drawSurfaces()\r
-{\r
- surfaces_mutex.Lock();\r
- list<SurfaceCommands*> todraw; //First figure out if a surfaces is below another surface\r
- list<SurfaceCommands>::iterator itty1=scommands.begin();\r
- while (itty1!=scommands.end()) {\r
- list<SurfaceCommands>::iterator itty2=itty1;\r
- itty2++;\r
- bool hidden=false;\r
- while (itty2!=scommands.end()) {\r
- SurfaceCommands & ref1=*itty1;\r
- SurfaceCommands & ref2=*itty2;\r
- if (ref1.x>=ref2.x && ref1.y>=ref2.y\r
- && (ref1.x+ref1.w) <= (ref2.x+ref2.w)\r
- && (ref1.y+ref1.h) <= (ref2.y+ref2.h) ) {\r
- hidden=true;\r
- break;\r
- }\r
- itty2++;\r
- }\r
- if (!hidden) { // we are not hidden, perfect\r
- todraw.push_back(&(*itty1));\r
- }\r
- itty1++;\r
- }\r
- //Now go through all surfaces and draw them\r
- list<SurfaceCommands*>::iterator curdraw=todraw.begin();\r
- while (curdraw!=todraw.end()) {\r
- drawSetTrans(*(*curdraw));\r
- list<SVGCommand>::iterator commands=(*(*curdraw)).commands.begin();\r
- list<SVGCommand>::iterator end=(*(*curdraw)).commands.end();\r
- while (commands!=end) {\r
- executeDrawCommand(*commands);\r
- commands++;\r
- }\r
- curdraw++;\r
- }\r
-\r
- surfaces_mutex.Unlock();\r
-}\r
-\r
-\r
-void OsdVector::updateOrAddSurface(const SurfaceVector *surf,float x,float y,float height,float width,\r
- list<SVGCommand>& commands)\r
-{\r
- surfaces_mutex.Lock();\r
- //First determine it is already in our system\r
- list<SurfaceCommands>::iterator itty=scommands.begin();\r
- while (itty!=scommands.end()) {\r
- if ((*itty).surf==surf) {\r
- //decrease the references\r
- dereferenceSVGCommand((*itty).commands);\r
- break;\r
- }\r
- itty++;\r
- }\r
- // if not insert it\r
- if (itty==scommands.end()) {\r
- SurfaceCommands new_sc;\r
- new_sc.surf=surf;\r
- new_sc.x=x;\r
- new_sc.y=y;\r
- new_sc.w=width;\r
- new_sc.h=height;\r
- itty=scommands.insert(itty,new_sc);\r
- }\r
- // then clear and copy\r
- (*itty).commands.clear();\r
- (*itty).commands=commands;\r
- //increase the references\r
- list<SVGCommand>::iterator sitty=(*itty).commands.begin();\r
- while (sitty!=(*itty).commands.end())\r
- {\r
- incStyleRef((*sitty).getRef());\r
- ImageIndex ii=(*sitty).getImageIndex();\r
- if (ii) incImageRef(ii);\r
- sitty++;\r
- }\r
- cleanupOrphanedRefs();\r
-\r
- surfaces_mutex.Unlock();\r
-}\r
-\r
-void OsdVector::removeSurface(const SurfaceVector *surf)\r
-{\r
- surfaces_mutex.Lock();\r
- //First determine it is already in our system\r
- list<SurfaceCommands>::iterator itty=scommands.begin();\r
- while (itty!=scommands.end()) {\r
- if ((*itty).surf==surf) {\r
- dereferenceSVGCommand((*itty).commands);\r
- (*itty).commands.clear();\r
- scommands.erase(itty);\r
- break;\r
- }\r
- itty++;\r
- }\r
- surfaces_mutex.Unlock();\r
-\r
-}\r
-\r
-void OsdVector::dereferenceSVGCommand(list<SVGCommand>& commands )\r
-{\r
-\r
- list<SVGCommand>::iterator sitty = commands.begin();\r
- while (sitty != commands.end()) {\r
- removeStyleRef((*sitty).getRef());\r
- ImageIndex ii = (*sitty).getImageIndex();\r
- if (ii) removeImageRef(ii);\r
- sitty++;\r
- }\r
-}\r
-\r
-void OsdVector::referenceSVGCommand(list<SVGCommand>& commands )\r
-{\r
- list<SVGCommand>::iterator sitty=commands.begin();\r
- while (sitty!=commands.end())\r
- {\r
- incStyleRef((*sitty).getRef());\r
- ImageIndex ii=(*sitty).getImageIndex();\r
- if (ii) incImageRef(ii);\r
- sitty++;\r
- }\r
-}\r
-\r
-void OsdVector::incImageRef(ImageIndex index)\r
-{\r
- if (images_ref.find(index)==images_ref.end()) {\r
- images_ref[index]=1;\r
- } else {\r
- images_ref[index]++;\r
- }\r
-}\r
-\r
-void OsdVector::removeImageRef(const ImageIndex ref)\r
-{\r
- images_ref[ref]--;\r
-}\r
-\r
-void OsdVector::cleanupOrphanedRefs()\r
-{ // Do some garbage collection\r
-\r
- map<void *,ImageIndex>::iterator mitty=monobitmaps.begin();\r
- while (mitty!=monobitmaps.end()) {\r
- map<ImageIndex,unsigned int>::iterator curitty=images_ref.find((*mitty).second);\r
- int count=(*curitty).second;\r
- if (count==0) {\r
- ImageIndex ref=(*curitty).first;\r
- monobitmaps.erase(mitty++);\r
- images_ref.erase(curitty++);\r
- destroyImageRef(ref);\r
- } else ++mitty;\r
- }\r
-\r
- map<string,ImageIndex>::iterator jitty=jpegs.begin();\r
- while (jitty!=jpegs.end()) {\r
- map<ImageIndex,unsigned int>::iterator curitty=images_ref.find((*jitty).second);\r
- int count=(*curitty).second;\r
- if (count==0) {\r
- ImageIndex ref=(*curitty).first;\r
- jpegs.erase(jitty++);\r
- images_ref.erase(curitty++);\r
- destroyImageRef(ref);\r
- } else ++jitty;\r
- }\r
-\r
-\r
- map<pair<Colour*,unsigned int>,unsigned int>::iterator sitty=styles.begin();\r
- while (sitty!=styles.end()) {\r
- map<unsigned int,unsigned int>::iterator curitty=styles_ref.find((*sitty).second);\r
- int count=(*curitty).second;\r
- if (count==0) {\r
- unsigned int ref=(*curitty).first;\r
- styles.erase(sitty++);\r
- styles_ref.erase(curitty++);\r
- destroyStyleRef(ref);\r
-\r
- } else ++sitty;\r
-\r
- }\r
-}\r
-\r
-\r
-unsigned int OsdVector::getImageRef(ImageIndex index)\r
-{\r
- if (images_ref.find(index)==images_ref.end()) {\r
- return -1;\r
- } else {\r
- return images_ref[index];\r
- }\r
-}\r
-\r
-void OsdVector::incStyleRef(unsigned int index)\r
-{\r
- if (styles_ref.find(index)==styles_ref.end()) {\r
- styles_ref[index]=1;\r
- } else {\r
- styles_ref[index]++;\r
- }\r
-}\r
-\r
-void OsdVector::removeStyleRef(unsigned int index)\r
-{\r
- styles_ref[index]--;\r
-}\r
-\r
-unsigned int OsdVector::getStyleRef(const DrawStyle &c)\r
-{\r
- unsigned int style_handle=0;\r
- if (styles.find(pair<Colour*,unsigned int>((Colour*)&c,c.rgba()))==styles.end())\r
- {\r
- style_handle=styles[pair<Colour*,unsigned int>((Colour*)&c,c.rgba())]=createStyleRef(c);\r
- } else {\r
- style_handle=styles[pair<Colour*,unsigned int>((Colour*)&c,c.rgba())];\r
- //Now check if the handle is valid\r
- if (styles_ref.find(style_handle)==styles_ref.end()) {\r
- //invalid handle recreate\r
- style_handle=styles[pair<Colour*,unsigned int>((Colour*)&c,c.rgba())]=createStyleRef(c);\r
- }\r
- }\r
- incStyleRef(style_handle);\r
- return style_handle;\r
-}\r
-\r
-unsigned int OsdVector::getColorRef(const Colour &c)\r
-{\r
- unsigned int style_handle=0;\r
- if (styles.find(pair<Colour*,unsigned int>((Colour*)&c,c.rgba()))==styles.end())\r
- {\r
- style_handle=styles[pair<Colour*,unsigned int>((Colour*)&c,c.rgba())]=createColorRef(c);\r
- } else {\r
- style_handle=styles[pair<Colour*,unsigned int>((Colour*)&c,c.rgba())];\r
- if (styles_ref.find(style_handle)==styles_ref.end()) {\r
- //invalid handle recreate\r
- style_handle=styles[pair<Colour*,unsigned int>((Colour*)&c,c.rgba())]=createColorRef(c);\r
- }\r
- }\r
- incStyleRef(style_handle);\r
- return style_handle;\r
-}\r
-\r
-unsigned int OsdVector::getStyleRef(unsigned int index)\r
-{\r
- if (styles_ref.find(index)==styles_ref.end()) {\r
- return -1;\r
- } else {\r
- return styles_ref[index];\r
- }\r
-}\r
-\r
-ImageIndex OsdVector::getJpegRef(const char* fileName, int *width,int *height)\r
-{\r
- ImageIndex image_handle=0;\r
- if (jpegs.find(fileName)==jpegs.end())\r
- {\r
- image_handle=jpegs[fileName]=createJpeg(fileName,width,height);\r
- } else {\r
- image_handle=jpegs[fileName];\r
- *width=0;\r
- *height=0;\r
- if (images_ref.find(image_handle)==images_ref.end()) {\r
- //invalid handle recreate\r
- image_handle=jpegs[fileName]=createJpeg(fileName,width,height);\r
- }\r
- }\r
- incImageRef(image_handle);\r
- return image_handle;\r
-}\r
-\r
-ImageIndex OsdVector::getMonoBitmapRef(void *base,int width,int height)\r
-{\r
- ImageIndex image_handle;\r
- if (monobitmaps.find(base)==monobitmaps.end())\r
- {\r
- image_handle=monobitmaps[base]=createMonoBitmap(base,width,height);\r
- } else {\r
- image_handle=monobitmaps[base];\r
- if (images_ref.find(image_handle)==images_ref.end()) {\r
- //invalid handle recreate\r
- image_handle=monobitmaps[base]=createMonoBitmap(base,width,height);\r
- }\r
- }\r
- incImageRef(image_handle);\r
- return image_handle;\r
-}\r
-\r
-ImageIndex OsdVector::getImagePalette(int width,int height,const unsigned char *image_data,const unsigned int*palette_data)\r
-{\r
- ImageIndex image_handle;\r
- image_handle=createImagePalette(width,height,image_data,palette_data);\r
- incImageRef(image_handle);\r
- return image_handle;\r
-}\r
+/*
+ Copyright 2012 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 "osdvector.h"
+#include "surfacevector.h"
+
+OsdVector::OsdVector()
+{
+ setlocale(LC_CTYPE,"C.UTF-8");
+}
+
+OsdVector::~OsdVector()
+{
+
+}
+
+
+int OsdVector::getFD()
+{
+ return 0;
+}
+
+void OsdVector::screenShot(const char* fileName)
+{
+ //Do nothing,
+}
+
+Surface * OsdVector::createNewSurface()
+{
+ return new SurfaceVector(this);
+}
+
+void OsdVector::BeginPainting()
+{
+ surfaces_mutex.Lock();
+}
+void OsdVector::EndPainting()
+{
+ surfaces_mutex.Unlock();
+}
+
+void OsdVector::Blank()
+{
+ // do nothing? remove this one?
+}
+
+int OsdVector::restore()
+{
+ // First clear the contents of all registered surfaces
+ surfaces_mutex.Lock();
+
+ //Now go through all surfaces and draw them
+ list<SurfaceCommands>::iterator curdraw=scommands.begin();
+ while (curdraw!=scommands.end()) {
+ (*curdraw).commands.clear();
+ curdraw++;
+ }
+ //also clear all handles, they are now invalid, no need to release them
+ images_ref.clear();;
+ monobitmaps.clear();
+ jpegs.clear();
+ styles.clear();
+ styles_ref.clear();
+
+ surfaces_mutex.Unlock();
+ return 1;
+}
+
+void OsdVector::drawSurfaces()
+{
+ surfaces_mutex.Lock();
+ list<SurfaceCommands*> todraw; //First figure out if a surfaces is below another surface
+ list<SurfaceCommands>::iterator itty1=scommands.begin();
+ while (itty1!=scommands.end()) {
+ list<SurfaceCommands>::iterator itty2=itty1;
+ itty2++;
+ bool hidden=false;
+ while (itty2!=scommands.end()) {
+ SurfaceCommands & ref1=*itty1;
+ SurfaceCommands & ref2=*itty2;
+ if (ref1.x>=ref2.x && ref1.y>=ref2.y
+ && (ref1.x+ref1.w) <= (ref2.x+ref2.w)
+ && (ref1.y+ref1.h) <= (ref2.y+ref2.h) ) {
+ hidden=true;
+ break;
+ }
+ itty2++;
+ }
+ if (!hidden) { // we are not hidden, perfect
+ todraw.push_back(&(*itty1));
+ }
+ itty1++;
+ }
+ //Now go through all surfaces and draw them
+ list<SurfaceCommands*>::iterator curdraw=todraw.begin();
+ while (curdraw!=todraw.end()) {
+ drawSetTrans(*(*curdraw));
+ list<SVGCommand>::iterator commands=(*(*curdraw)).commands.begin();
+ list<SVGCommand>::iterator end=(*(*curdraw)).commands.end();
+ while (commands!=end) {
+ executeDrawCommand(*commands);
+ commands++;
+ }
+ curdraw++;
+ }
+
+ surfaces_mutex.Unlock();
+}
+
+
+void OsdVector::updateOrAddSurface(const SurfaceVector *surf,float x,float y,float height,float width,
+ list<SVGCommand>& commands)
+{
+ surfaces_mutex.Lock();
+ //First determine it is already in our system
+ list<SurfaceCommands>::iterator itty=scommands.begin();
+ while (itty!=scommands.end()) {
+ if ((*itty).surf==surf) {
+ //decrease the references
+ dereferenceSVGCommand((*itty).commands);
+ break;
+ }
+ itty++;
+ }
+ // if not insert it
+ if (itty==scommands.end()) {
+ SurfaceCommands new_sc;
+ new_sc.surf=surf;
+ new_sc.x=x;
+ new_sc.y=y;
+ new_sc.w=width;
+ new_sc.h=height;
+ itty=scommands.insert(itty,new_sc);
+ }
+ // then clear and copy
+ (*itty).commands.clear();
+ (*itty).commands=commands;
+ //increase the references
+ list<SVGCommand>::iterator sitty=(*itty).commands.begin();
+ while (sitty!=(*itty).commands.end())
+ {
+ incStyleRef((*sitty).getRef());
+ ImageIndex ii=(*sitty).getImageIndex();
+ if (ii) incImageRef(ii);
+ sitty++;
+ }
+ cleanupOrphanedRefs();
+
+ surfaces_mutex.Unlock();
+}
+
+void OsdVector::removeSurface(const SurfaceVector *surf)
+{
+ surfaces_mutex.Lock();
+ //First determine it is already in our system
+ list<SurfaceCommands>::iterator itty=scommands.begin();
+ while (itty!=scommands.end()) {
+ if ((*itty).surf==surf) {
+ dereferenceSVGCommand((*itty).commands);
+ (*itty).commands.clear();
+ scommands.erase(itty);
+ break;
+ }
+ itty++;
+ }
+ surfaces_mutex.Unlock();
+
+}
+
+void OsdVector::dereferenceSVGCommand(list<SVGCommand>& commands )
+{
+
+ list<SVGCommand>::iterator sitty = commands.begin();
+ while (sitty != commands.end()) {
+ removeStyleRef((*sitty).getRef());
+ ImageIndex ii = (*sitty).getImageIndex();
+ if (ii) removeImageRef(ii);
+ sitty++;
+ }
+}
+
+void OsdVector::referenceSVGCommand(list<SVGCommand>& commands )
+{
+ list<SVGCommand>::iterator sitty=commands.begin();
+ while (sitty!=commands.end())
+ {
+ incStyleRef((*sitty).getRef());
+ ImageIndex ii=(*sitty).getImageIndex();
+ if (ii) incImageRef(ii);
+ sitty++;
+ }
+}
+
+void OsdVector::incImageRef(ImageIndex index)
+{
+ if (images_ref.find(index)==images_ref.end()) {
+ images_ref[index]=1;
+ } else {
+ images_ref[index]++;
+ }
+}
+
+void OsdVector::removeImageRef(const ImageIndex ref)
+{
+ images_ref[ref]--;
+}
+
+void OsdVector::cleanupOrphanedRefs()
+{ // Do some garbage collection
+
+ map<void *,ImageIndex>::iterator mitty=monobitmaps.begin();
+ while (mitty!=monobitmaps.end()) {
+ map<ImageIndex,unsigned int>::iterator curitty=images_ref.find((*mitty).second);
+ int count=(*curitty).second;
+ if (count==0) {
+ ImageIndex ref=(*curitty).first;
+ monobitmaps.erase(mitty++);
+ images_ref.erase(curitty++);
+ destroyImageRef(ref);
+ } else ++mitty;
+ }
+
+ map<string,ImageIndex>::iterator jitty=jpegs.begin();
+ while (jitty!=jpegs.end()) {
+ map<ImageIndex,unsigned int>::iterator curitty=images_ref.find((*jitty).second);
+ int count=(*curitty).second;
+ if (count==0) {
+ ImageIndex ref=(*curitty).first;
+ jpegs.erase(jitty++);
+ images_ref.erase(curitty++);
+ destroyImageRef(ref);
+ } else ++jitty;
+ }
+
+
+ map<pair<Colour*,unsigned int>,unsigned int>::iterator sitty=styles.begin();
+ while (sitty!=styles.end()) {
+ map<unsigned int,unsigned int>::iterator curitty=styles_ref.find((*sitty).second);
+ int count=(*curitty).second;
+ if (count==0) {
+ unsigned int ref=(*curitty).first;
+ styles.erase(sitty++);
+ styles_ref.erase(curitty++);
+ destroyStyleRef(ref);
+
+ } else ++sitty;
+
+ }
+}
+
+
+unsigned int OsdVector::getImageRef(ImageIndex index)
+{
+ if (images_ref.find(index)==images_ref.end()) {
+ return -1;
+ } else {
+ return images_ref[index];
+ }
+}
+
+void OsdVector::incStyleRef(unsigned int index)
+{
+ if (styles_ref.find(index)==styles_ref.end()) {
+ styles_ref[index]=1;
+ } else {
+ styles_ref[index]++;
+ }
+}
+
+void OsdVector::removeStyleRef(unsigned int index)
+{
+ styles_ref[index]--;
+}
+
+unsigned int OsdVector::getStyleRef(const DrawStyle &c)
+{
+ unsigned int style_handle=0;
+ if (styles.find(pair<Colour*,unsigned int>((Colour*)&c,c.rgba()))==styles.end())
+ {
+ style_handle=styles[pair<Colour*,unsigned int>((Colour*)&c,c.rgba())]=createStyleRef(c);
+ } else {
+ style_handle=styles[pair<Colour*,unsigned int>((Colour*)&c,c.rgba())];
+ //Now check if the handle is valid
+ if (styles_ref.find(style_handle)==styles_ref.end()) {
+ //invalid handle recreate
+ style_handle=styles[pair<Colour*,unsigned int>((Colour*)&c,c.rgba())]=createStyleRef(c);
+ }
+ }
+ incStyleRef(style_handle);
+ return style_handle;
+}
+
+unsigned int OsdVector::getColorRef(const Colour &c)
+{
+ unsigned int style_handle=0;
+ if (styles.find(pair<Colour*,unsigned int>((Colour*)&c,c.rgba()))==styles.end())
+ {
+ style_handle=styles[pair<Colour*,unsigned int>((Colour*)&c,c.rgba())]=createColorRef(c);
+ } else {
+ style_handle=styles[pair<Colour*,unsigned int>((Colour*)&c,c.rgba())];
+ if (styles_ref.find(style_handle)==styles_ref.end()) {
+ //invalid handle recreate
+ style_handle=styles[pair<Colour*,unsigned int>((Colour*)&c,c.rgba())]=createColorRef(c);
+ }
+ }
+ incStyleRef(style_handle);
+ return style_handle;
+}
+
+unsigned int OsdVector::getStyleRef(unsigned int index)
+{
+ if (styles_ref.find(index)==styles_ref.end()) {
+ return -1;
+ } else {
+ return styles_ref[index];
+ }
+}
+
+ImageIndex OsdVector::getJpegRef(const char* fileName, int *width,int *height)
+{
+ ImageIndex image_handle=0;
+ if (jpegs.find(fileName)==jpegs.end())
+ {
+ image_handle=jpegs[fileName]=createJpeg(fileName,width,height);
+ } else {
+ image_handle=jpegs[fileName];
+ *width=0;
+ *height=0;
+ if (images_ref.find(image_handle)==images_ref.end()) {
+ //invalid handle recreate
+ image_handle=jpegs[fileName]=createJpeg(fileName,width,height);
+ }
+ }
+ incImageRef(image_handle);
+ return image_handle;
+}
+
+ImageIndex OsdVector::getMonoBitmapRef(void *base,int width,int height)
+{
+ ImageIndex image_handle;
+ if (monobitmaps.find(base)==monobitmaps.end())
+ {
+ image_handle=monobitmaps[base]=createMonoBitmap(base,width,height);
+ } else {
+ image_handle=monobitmaps[base];
+ if (images_ref.find(image_handle)==images_ref.end()) {
+ //invalid handle recreate
+ image_handle=monobitmaps[base]=createMonoBitmap(base,width,height);
+ }
+ }
+ incImageRef(image_handle);
+ return image_handle;
+}
+
+ImageIndex OsdVector::getImagePalette(int width,int height,const unsigned char *image_data,const unsigned int*palette_data)
+{
+ ImageIndex image_handle;
+ image_handle=createImagePalette(width,height,image_data,palette_data);
+ incImageRef(image_handle);
+ return image_handle;
+}
-/*\r
- Copyright 2004-2005 Chris Tallon, 2006,2011-2012 Marten Richter\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef OSDVECTOR_H\r
-#define OSDVECTOR_H\r
-#include "osd.h"\r
-#include "mutex.h"\r
-#include "colour.h"\r
-#include <list>\r
-#include <map>\r
-#include <string>\r
-\r
-#include "teletextdecodervbiebu.h"\r
-\r
-enum SVGCommandInstr {\r
- DrawPath,\r
- DrawGlyph,\r
- DrawImage,\r
- DrawTTchar\r
-};\r
-enum PathIndex {\r
- HorzLine,\r
- VertLine,\r
- Rectangle,\r
- Point\r
-};\r
-\r
-typedef unsigned int ImageIndex;\r
-\r
-class SVGCommand\r
-{\r
-public:\r
- SVGCommand(float ix, float iy,float iw,float ih,PathIndex path,unsigned int ref)\r
- {\r
- instr=DrawPath;\r
- x=ix;\r
- y=iy;\r
- w=iw;\r
- h=ih;\r
- target.path_index=path;\r
- reference=ref;\r
- };\r
- SVGCommand(float ix, float iy,float iw,float ih,ImageIndex image_in,unsigned int ref)\r
- {\r
- instr=DrawImage;\r
- x=ix;\r
- y=iy;\r
- w=iw;\r
- h=ih;\r
- target.image=image_in;\r
- reference=ref;\r
- };\r
- SVGCommand(float ix, float iy,float iw,float ih,unsigned int ttchar_in)\r
- {\r
- instr=DrawTTchar;\r
- x=ix;\r
- y=iy;\r
- w=iw;\r
- h=ih;\r
- reference=0;\r
- target.ttchar=ttchar_in;\r
- };\r
- SVGCommand(float ix, float iy,wchar_t char_in,unsigned int ref)\r
- {\r
- instr=DrawGlyph;\r
- x=ix;\r
- y=iy;\r
- w=0;\r
- h=0;\r
- reference=ref;\r
- target.textchar=char_in;\r
- };\r
-\r
- bool Test(float tx,float ty,float tw, float th)\r
- {\r
- return (x>=tx) && (y>=ty) && ((x+w)<=(tx+tw)) && ((y+h)<=(ty+th));\r
- }\r
- bool TTTest(float tox,float toy,float tx, float ty)\r
- {\r
- return (x==tox) && (toy==y) && (w==tx) && (h==ty);\r
- }\r
- unsigned int getRef(){return reference;};\r
- ImageIndex getImageIndex() {\r
- if (instr!=DrawImage) return 0;\r
- else return target.image;\r
- };\r
- SVGCommandInstr instr;\r
- float x,y,w,h;\r
- unsigned int reference;\r
- union {\r
- PathIndex path_index; //path_index\r
- wchar_t textchar;\r
- ImageIndex image;\r
- unsigned int ttchar;\r
- } target;\r
-};\r
-\r
-class SurfaceVector;\r
-\r
-struct SurfaceCommands{\r
- const SurfaceVector* surf;\r
- list<SVGCommand> commands;\r
- float x,y,w,h;\r
-};\r
-\r
-\r
-class OsdVector : public Osd\r
-{\r
- public:\r
- OsdVector();\r
- virtual ~OsdVector();\r
-\r
-\r
- int restore();\r
-\r
- int getFD();\r
-\r
- void screenShot(const char* fileName);\r
-\r
- Surface * createNewSurface();\r
- void BeginPainting();\r
- void EndPainting();\r
-\r
- void Blank();\r
-\r
- void updateOrAddSurface(const SurfaceVector *surf,float x,float y,float height,float width,\r
- list<SVGCommand>& commands);\r
- void removeSurface(const SurfaceVector *surf);\r
-\r
- virtual float getFontHeight()=0;\r
- virtual float getCharWidth(wchar_t c)=0;\r
-\r
- virtual ImageIndex getJpegRef(const char* fileName, int *width,int *height);\r
- virtual ImageIndex getMonoBitmapRef(void *base,int width,int height);\r
- virtual ImageIndex getImagePalette(int width,int height,const unsigned char *image_data,const unsigned int*palette_data);\r
- virtual void imageUploadLine(ImageIndex index,unsigned int j,unsigned int width,void *data)=0;\r
- void removeImageRef(const ImageIndex ref);\r
- unsigned int getColorRef(const Colour &c); //internally this is the same as getStyleRef\r
- unsigned int getStyleRef(const DrawStyle &c);\r
- virtual void removeStyleRef(unsigned int ref);\r
-\r
-\r
- int charSet() {return 2;}; //UTF-8\r
-\r
-\r
-\r
-protected:\r
-\r
- void incImageRef(ImageIndex index);\r
- unsigned int getImageRef(ImageIndex index);\r
- virtual void destroyImageRef(ImageIndex index)=0;\r
- virtual ImageIndex createJpeg(const char* fileName, int *width,int *height)=0;\r
- virtual ImageIndex createMonoBitmap(void *base,int width,int height)=0;\r
- virtual ImageIndex createImagePalette(int width,int height,const unsigned char *image_data,const unsigned int*palette_data)=0;\r
-\r
- map<ImageIndex,unsigned int> images_ref;\r
- map<void *,ImageIndex> monobitmaps;\r
- map<string,ImageIndex> jpegs;\r
-\r
- void incStyleRef(unsigned int index);\r
- unsigned int getStyleRef(ImageIndex index);\r
- virtual void destroyStyleRef(unsigned int index)=0;\r
- map<pair<Colour*,unsigned int>,unsigned int> styles;\r
- map<unsigned int,unsigned int> styles_ref;\r
- virtual unsigned int createStyleRef(const DrawStyle &c)=0;\r
- virtual unsigned int createColorRef(const Colour &c)=0;\r
-\r
- void dereferenceSVGCommand(list<SVGCommand>& commands );\r
- void referenceSVGCommand(list<SVGCommand>& commands );\r
- void cleanupOrphanedRefs();\r
-\r
-\r
-\r
- virtual void drawSetTrans(SurfaceCommands & sc)=0;\r
- virtual void executeDrawCommand(SVGCommand & command)=0;\r
-\r
-\r
-\r
-\r
- list<SurfaceCommands> scommands;\r
-\r
- Mutex surfaces_mutex;\r
-\r
- void drawSurfaces();\r
-};\r
-\r
-\r
-\r
-\r
-#endif\r
+/*
+ Copyright 2004-2005 Chris Tallon, 2006,2011-2012 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 OSDVECTOR_H
+#define OSDVECTOR_H
+#include "osd.h"
+#include "mutex.h"
+#include "colour.h"
+#include <list>
+#include <map>
+#include <string>
+
+#include "teletextdecodervbiebu.h"
+
+enum SVGCommandInstr {
+ DrawPath,
+ DrawGlyph,
+ DrawImage,
+ DrawTTchar
+};
+enum PathIndex {
+ HorzLine,
+ VertLine,
+ Rectangle,
+ Point
+};
+
+typedef unsigned int ImageIndex;
+
+class SVGCommand
+{
+public:
+ SVGCommand(float ix, float iy,float iw,float ih,PathIndex path,unsigned int ref)
+ {
+ instr=DrawPath;
+ x=ix;
+ y=iy;
+ w=iw;
+ h=ih;
+ target.path_index=path;
+ reference=ref;
+ };
+ SVGCommand(float ix, float iy,float iw,float ih,ImageIndex image_in,unsigned int ref)
+ {
+ instr=DrawImage;
+ x=ix;
+ y=iy;
+ w=iw;
+ h=ih;
+ target.image=image_in;
+ reference=ref;
+ };
+ SVGCommand(float ix, float iy,float iw,float ih,unsigned int ttchar_in)
+ {
+ instr=DrawTTchar;
+ x=ix;
+ y=iy;
+ w=iw;
+ h=ih;
+ reference=0;
+ target.ttchar=ttchar_in;
+ };
+ SVGCommand(float ix, float iy,wchar_t char_in,unsigned int ref)
+ {
+ instr=DrawGlyph;
+ x=ix;
+ y=iy;
+ w=0;
+ h=0;
+ reference=ref;
+ target.textchar=char_in;
+ };
+
+ bool Test(float tx,float ty,float tw, float th)
+ {
+ return (x>=tx) && (y>=ty) && ((x+w)<=(tx+tw)) && ((y+h)<=(ty+th));
+ }
+ bool TTTest(float tox,float toy,float tx, float ty)
+ {
+ return (x==tox) && (toy==y) && (w==tx) && (h==ty);
+ }
+ unsigned int getRef(){return reference;};
+ ImageIndex getImageIndex() {
+ if (instr!=DrawImage) return 0;
+ else return target.image;
+ };
+ SVGCommandInstr instr;
+ float x,y,w,h;
+ unsigned int reference;
+ union {
+ PathIndex path_index; //path_index
+ wchar_t textchar;
+ ImageIndex image;
+ unsigned int ttchar;
+ } target;
+};
+
+class SurfaceVector;
+
+struct SurfaceCommands{
+ const SurfaceVector* surf;
+ list<SVGCommand> commands;
+ float x,y,w,h;
+};
+
+
+class OsdVector : public Osd
+{
+ public:
+ OsdVector();
+ virtual ~OsdVector();
+
+
+ int restore();
+
+ int getFD();
+
+ void screenShot(const char* fileName);
+
+ Surface * createNewSurface();
+ void BeginPainting();
+ void EndPainting();
+
+ void Blank();
+
+ void updateOrAddSurface(const SurfaceVector *surf,float x,float y,float height,float width,
+ list<SVGCommand>& commands);
+ void removeSurface(const SurfaceVector *surf);
+
+ virtual float getFontHeight()=0;
+ virtual float getCharWidth(wchar_t c)=0;
+
+ virtual ImageIndex getJpegRef(const char* fileName, int *width,int *height);
+ virtual ImageIndex getMonoBitmapRef(void *base,int width,int height);
+ virtual ImageIndex getImagePalette(int width,int height,const unsigned char *image_data,const unsigned int*palette_data);
+ virtual void imageUploadLine(ImageIndex index,unsigned int j,unsigned int width,void *data)=0;
+ void removeImageRef(const ImageIndex ref);
+ unsigned int getColorRef(const Colour &c); //internally this is the same as getStyleRef
+ unsigned int getStyleRef(const DrawStyle &c);
+ virtual void removeStyleRef(unsigned int ref);
+
+
+ int charSet() {return 2;}; //UTF-8
+
+
+
+protected:
+
+ void incImageRef(ImageIndex index);
+ unsigned int getImageRef(ImageIndex index);
+ virtual void destroyImageRef(ImageIndex index)=0;
+ virtual ImageIndex createJpeg(const char* fileName, int *width,int *height)=0;
+ virtual ImageIndex createMonoBitmap(void *base,int width,int height)=0;
+ virtual ImageIndex createImagePalette(int width,int height,const unsigned char *image_data,const unsigned int*palette_data)=0;
+
+ map<ImageIndex,unsigned int> images_ref;
+ map<void *,ImageIndex> monobitmaps;
+ map<string,ImageIndex> jpegs;
+
+ void incStyleRef(unsigned int index);
+ unsigned int getStyleRef(ImageIndex index);
+ virtual void destroyStyleRef(unsigned int index)=0;
+ map<pair<Colour*,unsigned int>,unsigned int> styles;
+ map<unsigned int,unsigned int> styles_ref;
+ virtual unsigned int createStyleRef(const DrawStyle &c)=0;
+ virtual unsigned int createColorRef(const Colour &c)=0;
+
+ void dereferenceSVGCommand(list<SVGCommand>& commands );
+ void referenceSVGCommand(list<SVGCommand>& commands );
+ void cleanupOrphanedRefs();
+
+
+
+ virtual void drawSetTrans(SurfaceCommands & sc)=0;
+ virtual void executeDrawCommand(SVGCommand & command)=0;
+
+
+
+
+ list<SurfaceCommands> scommands;
+
+ Mutex surfaces_mutex;
+
+ void drawSurfaces();
+};
+
+
+
+
+#endif
-/*\r
- Copyright 2004-2005 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-\r
-#include "osdwin.h"\r
-#include "mtd.h"\r
-#include "videowin.h"\r
-#include "surfacewin.h"\r
-\r
-#include "dsallocator.h"\r
-#include "message.h"\r
-#include "command.h"\r
-\r
-#define BACKBUFFER_WIDTH 1280\r
-#define BACKBUFFER_HEIGHT 720\r
-\r
-\r
-\r
-typedef HRESULT (__stdcall *FCT_DXVA2CreateDirect3DDeviceManager9)(UINT* pResetToken, IDirect3DDeviceManager9** ppDeviceManager);\r
-typedef HRESULT (__stdcall *FCT_MFCreateVideoSampleFromSurface)(IUnknown* pUnkSurface, IMFSample** ppSample);\r
-\r
-FCT_DXVA2CreateDirect3DDeviceManager9 ptrDXVA2CreateDirect3DDeviceManager9=NULL;\r
-FCT_MFCreateVideoSampleFromSurface ptrMFCreateVideoSampleFromSurface=NULL;\r
-\r
-//This is stuff for rendering the OSD\r
-\r
-\r
-OsdWin::OsdWin()\r
-{\r
- d3d=NULL;\r
- d3ddevice=NULL;\r
- d3drtsurf=NULL;\r
- swappy=NULL;\r
- swapsurf=NULL;\r
- evrstate=EVR_pres_off;\r
- window=NULL;\r
-\r
- external_driving=false;\r
- dsallocator=NULL;\r
- filter_type=D3DTEXF_FORCE_DWORD;\r
- lastrendertime=timeGetTime();\r
- event = CreateEvent(NULL,/*FALSE*/TRUE,FALSE,NULL);\r
- d3dmutex = CreateMutex(NULL,FALSE,NULL);\r
- /*EVR stuff*/\r
- dxvadevicehandle=NULL;\r
- evrsupported=true;\r
- HMODULE hlib=NULL;\r
- hlib=LoadLibrary("dxva2.dll");\r
- if (!hlib) {\r
- evrsupported=false;\r
- return;\r
- }\r
- ptrDXVA2CreateDirect3DDeviceManager9=(FCT_DXVA2CreateDirect3DDeviceManager9)GetProcAddress(hlib, "DXVA2CreateDirect3DDeviceManager9");\r
- if (!ptrDXVA2CreateDirect3DDeviceManager9){\r
- evrsupported=false;\r
- return;\r
- }\r
-\r
- hlib=LoadLibrary("evr.dll");\r
- if (!hlib) {\r
- evrsupported=false;\r
- return;\r
- }\r
- \r
- ptrMFCreateVideoSampleFromSurface = (FCT_MFCreateVideoSampleFromSurface)GetProcAddress(hlib,"MFCreateVideoSampleFromSurface");\r
- if (!ptrMFCreateVideoSampleFromSurface){\r
- evrsupported=false;\r
- return;\r
- }\r
- \r
-}\r
-\r
-OsdWin::~OsdWin()\r
-{\r
-\r
- if (initted) \r
- {\r
- threadStop();\r
- shutdown();\r
- }\r
- CloseHandle(event);\r
- CloseHandle(d3dmutex);\r
-}\r
-\r
-int OsdWin::getFD()\r
-{\r
- if (!initted) return 0;\r
- return fdOsd;\r
-}\r
-\r
-Surface * OsdWin::createNewSurface(){\r
- return (Surface*)new SurfaceWin();\r
-}\r
-\r
-int OsdWin::init(void* device)\r
-{\r
- if (initted) return 0;\r
- Video* video = Video::getInstance();\r
- window=*((HWND*)device);\r
- //First Create Direct 3D Object\r
- d3d=Direct3DCreate9(D3D_SDK_VERSION);\r
- if (!d3d) \r
- {\r
- Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 object!");\r
- return 0;\r
- }\r
- // then create the Device\r
- D3DPRESENT_PARAMETERS d3dparas;\r
- ZeroMemory(&d3dparas,sizeof(d3dparas));\r
- d3dparas.BackBufferWidth=BACKBUFFER_WIDTH;\r
- d3dparas.BackBufferHeight=BACKBUFFER_HEIGHT;\r
- d3dparas.Windowed=TRUE;\r
- d3dparas.SwapEffect=D3DSWAPEFFECT_COPY;\r
- if (d3d->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,*((HWND*) device),\r
- D3DCREATE_SOFTWARE_VERTEXPROCESSING |D3DCREATE_MULTITHREADED,&d3dparas,&d3ddevice)!=D3D_OK) {\r
- Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 device!");\r
- return 0;\r
- }\r
- d3ddevice->GetRenderTarget(0,&d3drtsurf);\r
-\r
- /*\r
- if (!InitVertexBuffer()) {\r
- Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 vertex buf!");\r
- return 0;\r
- }*/\r
- /* We have to determine which kind of filtering is supported*/\r
- D3DCAPS9 caps;\r
- d3ddevice->GetDeviceCaps(&caps);\r
- if ( ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFLINEAR)!=0)\r
- && ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFLINEAR)!=0)) {\r
- if (filter_type==D3DTEXF_FORCE_DWORD) {\r
- filter_type=D3DTEXF_LINEAR;\r
- }\r
- } else {\r
- if (filter_type==D3DTEXF_LINEAR)\r
- {\r
- filter_type=D3DTEXF_POINT;\r
- }\r
- }\r
- \r
- if ( ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFPOINT)!=0)\r
- && ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFPOINT)!=0)) {\r
- if (filter_type==D3DTEXF_FORCE_DWORD) \r
- {\r
- filter_type=D3DTEXF_POINT;\r
- }\r
- } else {\r
- if (filter_type==D3DTEXF_POINT) {\r
- filter_type=D3DTEXF_NONE;\r
- }\r
- }\r
- if (filter_type==D3DTEXF_FORCE_DWORD) {\r
- filter_type=D3DTEXF_NONE;\r
- }\r
-\r
- if (evrsupported)\r
- {\r
- if (ptrDXVA2CreateDirect3DDeviceManager9(&dxvatoken,&d3ddevman)!=S_OK) evrsupported=false;\r
- else {\r
- d3ddevman->ResetDevice(d3ddevice,dxvatoken);\r
- }\r
- }\r
-\r
-\r
-\r
-\r
- //Now we will create the Screen\r
- screen = new SurfaceWin(Surface::SCREEN);\r
- SetEvent(event);//Now all devices are ready\r
- screen->create(video->getScreenWidth(), video->getScreenHeight());\r
- screen->display();\r
- initted = 1; // must set this here or create surface won't work\r
- threadStart(); \r
-\r
- return 1;\r
-}\r
-\r
-void OsdWin::LockDevice()\r
-{\r
- if (!evrsupported) return;\r
- if (!dxvadevicehandle) \r
- {\r
- d3ddevman->OpenDeviceHandle(&dxvadevicehandle);\r
- }\r
- IDirect3DDevice9 *temp;\r
- d3ddevman->LockDevice(dxvadevicehandle,&temp,TRUE);\r
-\r
-}\r
-\r
-void OsdWin::UnlockDevice()\r
-{\r
- if (!evrsupported) return;\r
- if (!initted) return;\r
- d3ddevman->UnlockDevice(dxvadevicehandle,TRUE);\r
- if (dxvadevicehandle) \r
- {\r
- d3ddevman->CloseDeviceHandle(dxvadevicehandle);\r
- dxvadevicehandle=NULL;\r
- }\r
-\r
-}\r
-\r
-DWORD OsdWin::getFilterCaps()\r
-{\r
- if (!initted) return NULL;\r
- D3DCAPS9 caps;\r
- d3ddevice->GetDeviceCaps(&caps);\r
- return caps.StretchRectFilterCaps;\r
-}\r
- \r
-LPDIRECT3DVERTEXBUFFER9 OsdWin::InitVertexBuffer(DWORD width, DWORD height)\r
-{\r
- LPDIRECT3DVERTEXBUFFER9 ret =NULL;\r
- Video* video=Video::getInstance();\r
- FLOAT texx=((float)video->getScreenWidth())/1024.f;\r
- FLOAT texy=((float)video->getScreenHeight())/1024.f;\r
- D3DCOLOR osdcolor=D3DCOLOR_RGBA(255,255,255,255);\r
- osdvertices[0].c=osdcolor;\r
- osdvertices[0].x=0.f-0.5f;\r
- osdvertices[0].y=0.f-0.5f;\r
- osdvertices[0].z=0.5f;\r
- osdvertices[0].rhw=1.f;\r
- osdvertices[0].u=0.f;\r
- osdvertices[0].v=0.f;\r
- osdvertices[1].c=osdcolor;\r
- osdvertices[1].x=((float)width)-0.5f;\r
- osdvertices[1].y=0.f-0.5f;\r
- osdvertices[1].z=0.5f;\r
- osdvertices[1].u=texx;\r
- osdvertices[1].v=0.f;\r
- osdvertices[1].rhw=1.f;\r
- osdvertices[2].c=osdcolor;\r
- osdvertices[2].x=((float)width)-0.5f;\r
- osdvertices[2].y=((float)height)-0.5f;\r
- osdvertices[2].z=0.5f;\r
- osdvertices[2].rhw=1.f;\r
- osdvertices[2].u=texx;\r
- osdvertices[2].v=texy;\r
- osdvertices[3].c=osdcolor;\r
- osdvertices[3].x=0.f-0.5f;\r
- osdvertices[3].y=((float)height)-0.5f;\r
- osdvertices[3].z=0.5f;\r
- osdvertices[3].rhw=1.f;\r
- osdvertices[3].u=0.f;\r
- osdvertices[3].v=texy;\r
- \r
- if (d3ddevice->CreateVertexBuffer(4*sizeof(OSDVERTEX),0,D3DFVF_OSDVERTEX,D3DPOOL_MANAGED,\r
- &ret,NULL)!=D3D_OK) {\r
- return NULL;\r
- }\r
- void *pvertex=NULL;\r
- if (ret->Lock(0,sizeof(osdvertices),&pvertex,0)!=D3D_OK) {\r
- return NULL;\r
- }\r
- memcpy(pvertex,osdvertices,sizeof(osdvertices));\r
- ret->Unlock();\r
- return ret;\r
-}\r
-\r
-int OsdWin::shutdown()\r
-{\r
- if (!initted) return 0;\r
- initted = 0;\r
- evrsupported=0;\r
- if (d3ddevman) d3ddevman->Release();\r
- d3drtsurf->Release();\r
- d3ddevice->Release();\r
- d3d->Release();\r
- if (swapsurf) swapsurf->Release();\r
- if (swappy) swappy->Release();\r
-\r
- return 1;\r
-}\r
-\r
-void OsdWin::screenShot(const char* fileName)\r
-{\r
- screen->screenShot(fileName);\r
-}\r
-\r
-void OsdWin::threadMethod()\r
-{\r
- while (true)\r
- {\r
- DWORD waittime=10;\r
- if (initted){\r
- if (evrstate==EVR_pres_off || evrstate==EVR_pres_pause) \r
- {\r
- Render();\r
- } else if (evrstate==EVR_pres_started)\r
- {\r
- LPDIRECT3DSURFACE9 surf;\r
- if (dsallocator) dsallocator->GetNextSurface(&surf,&waittime);\r
- if (surf==NULL)\r
- {\r
- Render();\r
- }\r
- else\r
- {\r
- RenderDS(surf);\r
- surf->Release();\r
- if (dsallocator) dsallocator->DiscardSurfaceandgetWait(&waittime);\r
- }\r
- }\r
- }\r
- threadCheckExit();\r
- if (waittime!=0) Sleep(min(10,waittime));\r
- //Sleep(1);\r
- }\r
-}\r
-\r
-\r
-void OsdWin::threadPostStopCleanup()\r
-{\r
- //Doing nothing\r
- //goo;\r
-}\r
-\r
-\r
-// This function is called from the WinMain function in order to get Screen updates\r
-void OsdWin::Render()\r
-{\r
- if (!initted) return ;\r
- if (external_driving) {\r
- DWORD time1=timeGetTime(); //Updates the Menue\r
- if ((time1-lastrendertime)>200) {//5 fps for OSD updates are enough, avoids tearing\r
- InternalRendering(NULL);\r
- lastrendertime=timeGetTime();\r
- } else {\r
- //Sleep(5); //Sleep for 5 ms, in order to avoid blocking the other threads\r
- }\r
- } else {\r
- DWORD time1=timeGetTime();\r
- if ((time1-lastrendertime)>50) {//10 fps for OSD updates are enough, avoids tearing\r
- InternalRendering(NULL);\r
- lastrendertime=timeGetTime();\r
- } else {\r
- //Sleep(5);\r
- \r
- }\r
- \r
- }\r
-}\r
-\r
-void OsdWin::RenderDS(LPDIRECT3DSURFACE9 present){\r
- if (!initted) return; \r
- if (external_driving) {\r
- InternalRendering(present);\r
- lastrendertime=timeGetTime();\r
- }\r
-}\r
-\r
-\r
-void OsdWin::InternalRendering(LPDIRECT3DSURFACE9 present){\r
- BeginPainting();\r
- HRESULT losty=d3ddevice->TestCooperativeLevel();\r
- if (losty==D3DERR_DEVICELOST) {\r
- //Sleep(10);\r
- EndPainting();\r
- return; //Device Lost\r
- }\r
- if (losty==D3DERR_DEVICENOTRESET){\r
- EndPainting();\r
- DoLost();\r
- return;\r
- }\r
- WaitForSingleObject(event,INFINITE);\r
- \r
- \r
- \r
- \r
- LPDIRECT3DSURFACE9 targetsurf;\r
- if (swappy)\r
- {\r
- targetsurf=swapsurf;\r
- d3ddevice->SetRenderTarget(0,swapsurf);//Stupid VMR manipulates the render target\r
- } \r
- else\r
- {\r
- targetsurf=d3drtsurf;\r
- d3ddevice->SetRenderTarget(0,d3drtsurf);//Stupid VMR manipulates the render target\r
- }\r
- D3DSURFACE_DESC targetdesc;\r
- targetsurf->GetDesc(&targetdesc);\r
-\r
- if (external_driving) {\r
- //Copy video to Backbuffer\r
- if (present!=NULL ) {\r
- VideoWin* video =(VideoWin*) Video::getInstance();\r
- /*calculating destination rect */\r
- RECT destrect={0,0,/*video->getScreenWidth()*/ targetdesc.Width,\r
- /*video->getScreenHeight()*/targetdesc.Height};\r
- UCHAR mode=video->getMode();\r
- switch (mode) {\r
- case Video::EIGHTH:\r
- destrect.right=destrect.right/2;\r
- destrect.bottom=destrect.bottom/2;\r
- case Video::QUARTER:\r
- destrect.right=destrect.right/2+video->getPosx()*2;\r
- destrect.bottom=destrect.bottom/2+video->getPosy()*2;\r
- destrect.left=video->getPosx()*2;\r
- destrect.top=video->getPosy()*2;\r
- d3ddevice->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0f,0);\r
- break;\r
- };\r
- D3DSURFACE_DESC surf_desc;\r
- present->GetDesc(&surf_desc);//for chop sides\r
- RECT sourcerect= {0,0,surf_desc.Width,surf_desc.Height};\r
- if (video->getPseudoTVsize()==Video::ASPECT4X3 \r
- && video->getMode()==Video::NORMAL \r
- && video->getAspectRatio()==Video::ASPECT16X9) {\r
- unsigned int correction=((double) (surf_desc.Width))*4.*9./3./16.;\r
- sourcerect.left=(surf_desc.Width-correction)/2;\r
- sourcerect.right=sourcerect.left+correction;\r
- }\r
- d3ddevice->StretchRect(present,&sourcerect,targetsurf ,&destrect,filter_type);\r
-\r
- }\r
- } else {\r
- VideoWin* video =(VideoWin*) Video::getInstance();\r
- //Clear Background\r
- if (!video->isVideoOn()) d3ddevice->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0f,0);\r
- }\r
- LPDIRECT3DVERTEXBUFFER9 vb=NULL;\r
- vb=InitVertexBuffer(targetdesc.Width,targetdesc.Height);\r
-\r
- //Drawing the OSD\r
- if (d3ddevice->BeginScene()==D3D_OK) {\r
- d3ddevice->SetStreamSource(0,vb,0,sizeof(OSDVERTEX));\r
- d3ddevice->SetFVF(D3DFVF_OSDVERTEX);\r
- d3ddevice->SetTexture(0,((SurfaceWin*)screen)->getD3dtexture());\r
- //d3ddevice->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_MODULATE);\r
- d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP,D3DTOP_MODULATE);\r
-\r
- d3ddevice->SetSamplerState(0,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR);\r
- d3ddevice->SetSamplerState(0,D3DSAMP_MINFILTER,D3DTEXF_LINEAR);\r
-\r
-\r
- d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE);\r
- d3ddevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);\r
- d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);\r
- d3ddevice->SetRenderState(D3DRS_LIGHTING,FALSE);\r
-\r
-\r
- d3ddevice->DrawPrimitive(D3DPT_TRIANGLEFAN,0,2);\r
- d3ddevice->EndScene();\r
- //Show it to the user!\r
- HRESULT hres;\r
- if (swappy)\r
- {\r
- if (hres=swappy->Present(NULL,NULL,NULL,NULL,0)==D3DERR_DEVICELOST){\r
- //EndPainting();\r
- if (!external_driving) DoLost();\r
- }\r
- } \r
- else\r
- {\r
- if (hres=d3ddevice->Present(NULL,NULL,NULL,NULL)==D3DERR_DEVICELOST){\r
- //EndPainting();\r
- if (!external_driving) DoLost();\r
- }\r
- }\r
- \r
- }\r
- \r
- vb->Release();\r
- EndPainting();\r
-\r
- \r
-// if (!external_driving) {\r
-// Sleep(4);//The User can wait for 4 milliseconds to see his changes\r
-// }\r
-}\r
-\r
-bool OsdWin::DoLost(){\r
- Log::getInstance()->log("OSD", Log::WARN, "Direct3D Device Lost! Reobtaining...");\r
- ResetEvent(event);\r
- if (external_driving && dsallocator!=NULL) {\r
- dsallocator->LostDevice(d3ddevice,d3d); //Propagate the information through DS\r
- }\r
- //First we free up all resources\r
- Video* video = Video::getInstance();\r
- ((SurfaceWin*)screen)->ReleaseSurface();\r
- if (d3drtsurf) d3drtsurf->Release();\r
- d3drtsurf=NULL;\r
- D3DPRESENT_PARAMETERS d3dparas;\r
- ZeroMemory(&d3dparas,sizeof(d3dparas));\r
- d3dparas.BackBufferWidth=BACKBUFFER_WIDTH;\r
- d3dparas.BackBufferHeight=BACKBUFFER_HEIGHT;\r
- d3dparas.Windowed=TRUE;\r
- d3dparas.SwapEffect=D3DSWAPEFFECT_COPY;\r
-\r
- if (swapsurf) {swapsurf->Release();swapsurf=NULL;};\r
- if (swappy) {swappy->Release();swappy=NULL;};\r
-\r
- if (d3ddevice->Reset(&d3dparas)!=D3D_OK){\r
- return false;\r
- }\r
- d3ddevice->GetRenderTarget(0,&d3drtsurf);\r
- if (d3ddevman) d3ddevman->ResetDevice(d3ddevice,dxvatoken);\r
- //InitVertexBuffer();\r
- //Redraw Views, Chris could you add a member function to BoxStack, so that\r
- // I can cause it to completely redraw the Views?\r
- // Otherwise the OSD would be distorted after Device Lost\r
- // FIXME\r
- \r
- SetEvent(event);\r
-\r
-\r
- screen->create(video->getScreenWidth(), video->getScreenHeight());\r
- screen->display();\r
- \r
- return true;\r
-\r
-}\r
-LPDIRECT3DDEVICE9 OsdWin::getD3dDev() {\r
- WaitForSingleObject(event,INFINITE);//We will only return if we are initted\r
- return d3ddevice;\r
-}\r
-\r
-LPDIRECT3D9 OsdWin::getD3d() {\r
- WaitForSingleObject(event,INFINITE);//We will only return if we are initted\r
- return d3d;\r
-}\r
-\r
-void OsdWin::BeginPainting() {//We synchronize calls to d3d between different threads\r
- WaitForSingleObject(d3dmutex,INFINITE);\r
- LockDevice();\r
-}\r
-\r
-void OsdWin::EndPainting() {\r
- UnlockDevice();\r
- ReleaseMutex(d3dmutex);\r
-}\r
-\r
-void OsdWin::setExternalDriving(DsAllocator* dsall,DWORD width, DWORD height) {\r
- \r
- if (swappy)\r
- {\r
- BeginPainting();\r
- d3ddevice->StretchRect(swapsurf,NULL,d3drtsurf,NULL,filter_type);\r
- LPDIRECT3DSWAPCHAIN9 temp=swappy;\r
- LPDIRECT3DSURFACE9 tempsurf=swapsurf;\r
- swappy=NULL;\r
- swapsurf=NULL;\r
- EndPainting();\r
- tempsurf->Release();\r
- temp->Release();\r
- }\r
-\r
- if (dsall==NULL) {\r
- external_driving=false;\r
- dsallocator=NULL; \r
- return;\r
- }\r
- WaitForSingleObject(event,INFINITE);//We will only return if we are initted\r
- BeginPainting();\r
-\r
- if (width>BACKBUFFER_WIDTH || height>BACKBUFFER_HEIGHT) \r
- {\r
- D3DPRESENT_PARAMETERS d3dparas;\r
- ZeroMemory(&d3dparas,sizeof(d3dparas));\r
- d3dparas.BackBufferWidth=width;\r
- d3dparas.BackBufferHeight=height;\r
- d3dparas.Windowed=TRUE;\r
- d3dparas.SwapEffect=D3DSWAPEFFECT_COPY;\r
- if (d3ddevice->CreateAdditionalSwapChain(&d3dparas,&swappy)!=D3D_OK){\r
- Log::getInstance()->log("OSD", Log::WARN, "Could not create Swap Chain!");\r
- //return 0;\r
- } else {\r
- swappy->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&swapsurf);\r
- }\r
- Log::getInstance()->log("OSD", Log::INFO, "Create Additional Swap Chain %d %d!",width,height);\r
- }\r
-\r
- dsallocator=dsall;\r
- external_driving=true;\r
- \r
- EndPainting();\r
-}\r
-\r
-void OsdWin::Blank() {\r
- WaitForSingleObject(event,INFINITE);\r
- BeginPainting();\r
- d3ddevice->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0f,0);\r
- EndPainting();\r
-}\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 "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();
+}
-/*\r
- Copyright 2004-2005 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef OSDWIN_H\r
-#define OSDWIN_H\r
-\r
-#include <stdio.h>\r
-\r
-#include "osd.h"\r
-#include "defines.h"\r
-#include "log.h"\r
-#include "threadwin.h"\r
-#include <winsock2.h>\r
-#include <d3d9.h>\r
-#include <Dxva2api.h>\r
-\r
-struct OSDVERTEX\r
-{\r
- FLOAT x,y,z,rhw;\r
- DWORD c;\r
- FLOAT u,v;\r
-};\r
-\r
-#define D3DFVF_OSDVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE| D3DFVF_TEX1)\r
-\r
-class DsAllocator;\r
-\r
-\r
-\r
-class OsdWin : public Osd, public ThreadWin\r
-{\r
- public:\r
- OsdWin();\r
- ~OsdWin();\r
-\r
- int init(void* device);\r
- int shutdown();\r
-\r
- int getFD();\r
-\r
- void screenShot(const char* fileName);\r
-\r
- Surface * createNewSurface();\r
-\r
- void threadMethod();\r
- void threadPostStopCleanup();\r
-\r
- LPDIRECT3DDEVICE9 getD3dDev() ;\r
- LPDIRECT3D9 getD3d() ;\r
- // This function is called from the WinMain function in order to get Screen updates\r
- void Render();\r
- void RenderDS(LPDIRECT3DSURFACE9 present);\r
- void BeginPainting();\r
- void EndPainting();\r
- void setExternalDriving(DsAllocator* dsall,DWORD width, DWORD height);\r
- void Blank();\r
- DWORD getFilterCaps();\r
- DWORD getFilterType(){return filter_type;};\r
- void setFilterType(D3DTEXTUREFILTERTYPE type) {filter_type=type;};\r
-\r
-\r
-\r
- \r
-\r
- enum EVR_state {\r
- EVR_pres_off=0,\r
- EVR_pres_started,\r
- EVR_pres_pause\r
- };\r
-\r
- void SetEVRStatus(EVR_state new_state){evrstate=new_state;};\r
- \r
- IDirect3DDeviceManager9 * getD3dMan() {return d3ddevman;};\r
- bool IsEvrSupported() {return evrsupported;};\r
- HWND getWindow() {return window;};\r
-\r
-private:\r
- void LockDevice();\r
- void UnlockDevice();\r
-\r
- LPDIRECT3D9 d3d;\r
- LPDIRECT3DDEVICE9 d3ddevice;\r
-// LPDIRECT3DVERTEXBUFFER9 d3dvb;\r
- LPDIRECT3DSURFACE9 d3drtsurf;\r
- LPDIRECT3DSWAPCHAIN9 swappy;\r
- LPDIRECT3DSURFACE9 swapsurf;\r
- DsAllocator* dsallocator;\r
- // This indicates, that currently a video is played, thus the osd updates are driven by the Directshow Filtersystem\r
- bool external_driving;\r
- HANDLE d3dmutex;\r
- DWORD lastrendertime;\r
- void InternalRendering(LPDIRECT3DSURFACE9 present);\r
- bool DoLost();\r
- LPDIRECT3DVERTEXBUFFER9 InitVertexBuffer(DWORD width, DWORD height);\r
- OSDVERTEX osdvertices[4];\r
- HANDLE event;\r
- D3DTEXTUREFILTERTYPE filter_type;\r
- EVR_state evrstate;\r
- bool evrsupported;\r
- HWND window;\r
-\r
- UINT dxvatoken;\r
- IDirect3DDeviceManager9 *d3ddevman;\r
- HANDLE dxvadevicehandle;\r
-};\r
-\r
-#endif\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.
+*/
+
+#ifndef OSDWIN_H
+#define OSDWIN_H
+
+#include <stdio.h>
+
+#include "osd.h"
+#include "defines.h"
+#include "log.h"
+#include "threadwin.h"
+#include <winsock2.h>
+#include <d3d9.h>
+#include <Dxva2api.h>
+
+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
-/*\r
- Copyright 2004-2008 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#include "player.h"\r
-\r
-#include "log.h"\r
-#include "audio.h"\r
-#include "video.h"\r
-#include "demuxervdr.h"\r
-#include "demuxerts.h"\r
-#include "vdr.h"\r
-#include "messagequeue.h"\r
-#include "remote.h"\r
-#include "message.h"\r
-#include "dvbsubtitles.h"\r
-#include "osdreceiver.h"\r
-\r
-#define USER_RESPONSE_TIME 500 // Milliseconds\r
-\r
-// ----------------------------------- Called from outside, one offs or info funcs\r
-\r
-Player::Player(MessageQueue* tmessageQueue, void* tmessageReceiver, OSDReceiver* tosdReceiver)\r
-: vfeed(this), afeed(this), tfeed(this)\r
-{\r
- messageQueue = tmessageQueue;\r
- messageReceiver = tmessageReceiver;\r
- osdReceiver = tosdReceiver;\r
- audio = Audio::getInstance();\r
- video = Video::getInstance();\r
- logger = Log::getInstance();\r
- vdr = VDR::getInstance();\r
- initted = false;\r
- lengthBytes = 0;\r
- lengthFrames = 0;\r
- currentFrameNumber = 0;\r
- state = S_STOP;\r
- ifactor = 4;\r
- is_pesrecording=true;\r
-\r
- subtitlesShowing = false;\r
-\r
- videoStartup = false;\r
- threadBuffer = NULL;\r
- \r
- blockSize = 100000;\r
- startupBlockSize = 250000;\r
- video->turnVideoOn();\r
-}\r
-\r
-Player::~Player()\r
-{\r
- if (initted) shutdown();\r
-}\r
-\r
-int Player::init(bool p_isPesRecording,double framespersecond)\r
-{\r
- if (initted) return 0;\r
-#ifndef WIN32\r
- pthread_mutex_init(&mutex, NULL);\r
-#else\r
- mutex=CreateMutex(NULL,FALSE,NULL);\r
-#endif\r
- is_pesrecording = p_isPesRecording;\r
- fps=framespersecond;\r
- if (is_pesrecording)\r
- demuxer = new DemuxerVDR();\r
- else\r
- demuxer = new DemuxerTS();\r
- if (!demuxer) return 0;\r
- subtitles = new DVBSubtitles(osdReceiver);\r
- if (!subtitles) return 0;\r
-\r
- teletext = new TeletextDecoderVBIEBU();\r
- if (!teletext) return 0;\r
- teletext->setRecordigMode(true);\r
- unsigned int demux_video_size=2097152;\r
- unsigned int demux_audio_size=524288;\r
- if (video->supportsh264()) {\r
- demux_video_size*=5*2;//5;\r
-\r
- }\r
- if (audio->maysupportAc3()) {\r
- //demux_audio_size*=2;\r
- }\r
- \r
- if (!demuxer->init(this, audio, video,teletext, demux_video_size,demux_audio_size,65536, framespersecond, subtitles))\r
- {\r
- logger->log("Player", Log::ERR, "Demuxer failed to init");\r
- shutdown();\r
- return 0;\r
- }\r
-\r
- vfeed.init();\r
- afeed.init();\r
- tfeed.init();\r
-\r
- video->stop();\r
- video->blank();\r
- audio->stop();\r
-\r
- initted = true;\r
- return 1;\r
-}\r
-\r
-int Player::shutdown()\r
-{\r
- if (!initted) return 0;\r
- switchState(S_STOP);\r
- initted = false;\r
-\r
- delete demuxer;\r
- demuxer = NULL;\r
- delete subtitles;\r
- subtitles = NULL;\r
- delete teletext;\r
- teletext = NULL;\r
-\r
-#ifdef WIN32\r
- CloseHandle(mutex);\r
-#endif\r
-\r
- return 1;\r
-}\r
-\r
-void Player::setStartFrame(ULONG startFrame)\r
-{\r
- currentFrameNumber = startFrame;\r
-}\r
-\r
-void Player::setLengthBytes(ULLONG length)\r
-{\r
- lengthBytes = length;\r
- logger->log("Player", Log::DEBUG, "Player has received length bytes of %llu", lengthBytes);\r
-}\r
-\r
-void Player::setLengthFrames(ULONG length)\r
-{\r
- lengthFrames = length;\r
- logger->log("Player", Log::DEBUG, "Player has received length frames of %lu", lengthFrames);\r
-}\r
-\r
-ULONG Player::getLengthFrames()\r
-{\r
- return lengthFrames;\r
-}\r
-\r
-ULONG Player::getCurrentFrameNum()\r
-{\r
- if (startup) return 0;\r
- switch(state)\r
- {\r
- case S_PLAY:\r
- case S_PAUSE_P:\r
- return demuxer->getFrameNumFromPTS(video->getCurrentTimestamp());\r
- case S_PAUSE_I:\r
- case S_FFWD:\r
- case S_FBWD:\r
- return currentFrameNumber;\r
- default:\r
- return 0; // shouldn't happen\r
- }\r
-}\r
-\r
-bool* Player::getDemuxerMpegAudioChannels()\r
-{\r
- return demuxer->getmpAudioChannels();\r
-}\r
-\r
-bool* Player::getDemuxerAc3AudioChannels()\r
-{\r
- return demuxer->getac3AudioChannels();\r
-}\r
-\r
-bool* Player::getDemuxerSubtitleChannels()\r
-{\r
- return demuxer->getSubtitleChannels();\r
-}\r
-\r
-int Player::getCurrentAudioChannel()\r
-{\r
- if (is_pesrecording) {\r
- return demuxer->getselAudioChannel();\r
- } else {\r
- return ((DemuxerTS*)demuxer)->getAID();\r
- }\r
-}\r
-\r
-int Player::getCurrentSubtitleChannel()\r
-{\r
- if (is_pesrecording) {\r
- return demuxer->getselSubtitleChannel();\r
- } else {\r
- return ((DemuxerTS*)demuxer)->getSubID();\r
- }\r
-}\r
-\r
-void Player::setSubtitleChannel(int newChannel)\r
-{\r
- if (is_pesrecording) {\r
- demuxer->setDVBSubtitleStream(newChannel);\r
- } else {\r
- ((DemuxerTS*)demuxer)->setSubID(newChannel);\r
- }\r
-}\r
-\r
-int *Player::getTeletxtSubtitlePages()\r
-{\r
- return teletext->getSubtitlePages();\r
-}\r
-\r
-void Player::setAudioChannel(int newChannel, int type, int streamtype)\r
-{\r
- if (is_pesrecording) {\r
- demuxer->setAudioStream(newChannel);\r
- return;\r
- } else {\r
- ((DemuxerTS*)demuxer)->setAID(newChannel,type,streamtype);\r
- return;\r
- }\r
-}\r
-\r
-bool Player::toggleSubtitles()\r
-{\r
- if (!subtitlesShowing)\r
- {\r
- subtitlesShowing = true;\r
- subtitles->show();\r
- }\r
- else\r
- {\r
- subtitlesShowing = false;\r
- subtitles->hide();\r
- }\r
- return subtitlesShowing;\r
-}\r
-\r
-void Player::turnSubtitlesOn(bool ison) {\r
- if (ison)\r
- {\r
- subtitlesShowing = true;\r
- subtitles->show();\r
- }\r
- else\r
- {\r
- subtitlesShowing = false;\r
- subtitles->hide();\r
- }\r
-\r
-}\r
-\r
-Channel * Player::getDemuxerChannel() {\r
- if (!is_pesrecording) {\r
- return ((DemuxerTS*) demuxer)->getChannelInfo();\r
- } \r
- return NULL; //Should not happen!\r
-}\r
-\r
-\r
-// ----------------------------------- Externally called events\r
-\r
-void Player::play()\r
-{\r
- if (!initted) return;\r
- if (state == S_PLAY) return;\r
- lock();\r
-\r
- bool doUnlock = false;\r
- if (state == S_PAUSE_P) doUnlock = true;\r
- switchState(S_PLAY);\r
- if (doUnlock) unLock();\r
-}\r
-\r
-void Player::playpause()\r
-{\r
- if (!initted) return;\r
- lock();\r
-\r
- bool doUnlock = false;\r
- if (state==S_PLAY) {\r
- doUnlock=true;\r
- switchState(S_PAUSE_P);\r
- } else {\r
- if (state == S_PAUSE_P) doUnlock = true;\r
- switchState(S_PLAY);\r
- }\r
- if (doUnlock) unLock();\r
-\r
-}\r
-\r
-\r
-\r
-\r
-void Player::stop()\r
-{\r
- if (!initted) return;\r
- if (state == S_STOP) return;\r
- lock();\r
- logger->log("Player", Log::DEBUG, "Stop called lock");\r
- switchState(S_STOP);\r
- unLock();\r
-}\r
-\r
-void Player::pause()\r
-{\r
- if (!initted) return;\r
- lock();\r
-\r
- if ((state == S_FFWD) || (state == S_FBWD))\r
- {\r
- switchState(S_PAUSE_I);\r
- }\r
- else if ((state == S_PAUSE_I) || (state == S_PAUSE_P))\r
- {\r
- switchState(S_PLAY);\r
- }\r
- else\r
- {\r
- switchState(S_PAUSE_P);\r
- }\r
-\r
- unLock();\r
-}\r
-\r
-void Player::fastForward()\r
-{\r
- if (!initted) return;\r
- lock();\r
-\r
- if (state == S_FFWD)\r
- {\r
- // change the rate\r
- switch(ifactor)\r
- {\r
- case 4: ifactor = 8; break;\r
- case 8: ifactor = 16; break;\r
- case 16: ifactor = 32; break;\r
- case 32: ifactor = 4; break;\r
- }\r
- }\r
- else\r
- {\r
- ifactor = 4;\r
- switchState(S_FFWD);\r
- }\r
- unLock();\r
-}\r
-\r
-void Player::fastBackward()\r
-{\r
- if (!initted) return;\r
- lock();\r
-\r
- if (state == S_FBWD)\r
- {\r
- // change the rate\r
- switch(ifactor)\r
- {\r
- case 4: ifactor = 8; break;\r
- case 8: ifactor = 16; break;\r
- case 16: ifactor = 32; break;\r
- case 32: ifactor = 4; break;\r
- }\r
- }\r
- else\r
- {\r
- ifactor = 4;\r
- switchState(S_FBWD);\r
- }\r
- unLock();\r
-}\r
-\r
-void Player::jumpToPercent(double percent)\r
-{\r
- lock();\r
- logger->log("Player", Log::DEBUG, "JUMP TO %f%%", percent);\r
- ULONG newFrame = (ULONG)(percent * lengthFrames / 100);\r
- switchState(S_JUMP, newFrame);\r
-// unLock(); - let thread unlock this\r
-}\r
-\r
-void Player::jumpToMark(int mark)\r
-{\r
- lock();\r
- logger->log("Player", Log::DEBUG, "JUMP TO MARK %i%%", mark);\r
- switchState(S_JUMP, mark);\r
-// unLock(); - let thread unlock this\r
-}\r
-\r
-void Player::jumpToFrameP(int newFrame)\r
-{\r
- lock();\r
- logger->log("Player", Log::DEBUG, "JUMP TO FRAME AND PAUSE %i", newFrame);\r
- switchState(S_JUMP_PI, newFrame);\r
- unLock();\r
-}\r
-\r
-void Player::skipForward(int seconds)\r
-{\r
- lock();\r
- logger->log("Player", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);\r
- ULONG newFrame = getCurrentFrameNum();\r
- if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid\r
- newFrame +=(ULONG) (((double)seconds) * fps);\r
- if (newFrame > lengthFrames) { switchState(S_PLAY); unLock(); }\r
- else switchState(S_JUMP, newFrame);\r
-// unLock(); - let thread unlock this\r
-}\r
-\r
-void Player::skipBackward(int seconds)\r
-{\r
- lock();\r
- logger->log("Player", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);\r
- long newFrame = getCurrentFrameNum();\r
- if (newFrame == 0) { unLock(); return; } // Current pos from demuxer is not valid\r
- newFrame -= (ULONG) (((double)seconds) * fps);\r
- if (newFrame < 0) newFrame = 0;\r
- switchState(S_JUMP, newFrame);\r
-// unLock(); - let thread unlock this\r
-}\r
-\r
-// ----------------------------------- Implementations called events\r
-\r
-void Player::switchState(UCHAR toState, ULONG jumpFrame)\r
-{\r
- if (!initted) return;\r
-\r
- logger->log("Player", Log::DEBUG, "Switch state from %u to %u", state, toState);\r
-\r
- switch(state) // current state selector\r
- {\r
- case S_PLAY: // from S_PLAY -----------------------------------\r
- {\r
- switch(toState)\r
- {\r
- case S_PLAY: // to S_PLAY\r
- {\r
- return;\r
- }\r
- case S_PAUSE_P: // to S_PAUSE_P\r
- {\r
- video->pause();\r
- audio->pause();\r
- state = S_PAUSE_P;\r
- return;\r
- }\r
- case S_PAUSE_I: // to S_PAUSE_I\r
- {\r
- // can't occur\r
- return;\r
- }\r
- case S_FFWD: // to S_FFWD\r
- {\r
- currentFrameNumber = getCurrentFrameNum();\r
- audio->systemMuteOn();\r
- threadStop();\r
- vfeed.stop();\r
- afeed.stop();\r
- tfeed.stop();\r
- subtitles->stop();\r
- demuxer->flush();\r
- state = S_FFWD;\r
- threadStart();\r
- return;\r
- }\r
- case S_FBWD: // to S_FBWD\r
- {\r
- currentFrameNumber = getCurrentFrameNum();\r
- audio->systemMuteOn();\r
- threadStop();\r
- vfeed.stop();\r
- afeed.stop();\r
- tfeed.stop();\r
- subtitles->stop();\r
- demuxer->flush();\r
- state = S_FBWD;\r
- threadStart();\r
- return;\r
- }\r
- case S_STOP: // to S_STOP\r
- {\r
- vfeed.stop();\r
- afeed.stop();\r
- tfeed.stop();\r
- subtitles->stop();\r
- threadStop();\r
- video->stop();\r
- video->blank();\r
- audio->stop();\r
- audio->unPause();\r
- video->reset();\r
- demuxer->reset();\r
- state = S_STOP;\r
- return;\r
- }\r
- case S_JUMP: // to S_JUMP\r
- {\r
- restartAtFrame(jumpFrame);\r
- return;\r
- }\r
- case S_JUMP_PI: // to S_JUMP_PI\r
- {\r
- audio->systemMuteOn();\r
- threadStop();\r
- vfeed.stop();\r
- afeed.stop();\r
- tfeed.stop();\r
- subtitles->stop();\r
- demuxer->flush();\r
- state = S_PAUSE_I;\r
- video->reset();\r
- video->play();\r
- restartAtFramePI(jumpFrame);\r
- return;\r
- }\r
- }\r
- }\r
- case S_PAUSE_P: // from S_PAUSE_P -----------------------------------\r
- {\r
- switch(toState)\r
- {\r
- case S_PLAY: // to S_PLAY\r
- {\r
- video->unPause();\r
- audio->unPause();\r
- state = S_PLAY;\r
- return;\r
- }\r
- case S_PAUSE_P: // to S_PAUSE_P\r
- {\r
- return;\r
- }\r
- case S_PAUSE_I: // to S_PAUSE_I\r
- {\r
- return;\r
- }\r
- case S_FFWD: // to S_FFWD\r
- {\r
- currentFrameNumber = getCurrentFrameNum();\r
- audio->systemMuteOn();\r
- vfeed.stop();\r
- afeed.stop();\r
- tfeed.stop();\r
- subtitles->stop();\r
- threadStop();\r
- video->unPause();\r
- audio->unPause();\r
- state = S_FFWD;\r
- threadStart();\r
- return;\r
- }\r
- case S_FBWD: // to S_FBWD\r
- {\r
- currentFrameNumber = getCurrentFrameNum();\r
- audio->systemMuteOn();\r
- vfeed.stop();\r
- afeed.stop();\r
- tfeed.stop();\r
- subtitles->stop();\r
- threadStop();\r
- video->unPause();\r
- audio->unPause();\r
- state = S_FBWD;\r
- threadStart();\r
- return;\r
- }\r
- case S_STOP: // to S_STOP\r
- {\r
- vfeed.stop();\r
- afeed.stop();\r
- tfeed.stop();\r
- subtitles->stop();\r
- threadStop();\r
- video->stop();\r
- video->blank();\r
- audio->stop();\r
- video->reset();\r
- audio->unPause();\r
- demuxer->reset();\r
- audio->systemMuteOff();\r
- state = S_STOP;\r
- return;\r
- }\r
- case S_JUMP: // to S_JUMP\r
- {\r
- state = S_PLAY;\r
- audio->systemMuteOn();\r
- audio->unPause();\r
- restartAtFrame(jumpFrame);\r
- return;\r
- }\r
- case S_JUMP_PI: // to S_JUMP_PI\r
- {\r
- audio->systemMuteOn();\r
- audio->unPause();\r
- threadStop();\r
- vfeed.stop();\r
- afeed.stop();\r
- tfeed.stop();\r
- subtitles->stop();\r
- demuxer->flush();\r
- state = S_PAUSE_I;\r
- video->reset();\r
- video->play();\r
- restartAtFramePI(jumpFrame);\r
- return;\r
- }\r
- }\r
- }\r
- case S_PAUSE_I: // from S_PAUSE_I -----------------------------------\r
- {\r
- switch(toState)\r
- {\r
- case S_PLAY: // to S_PLAY\r
- {\r
- state = S_PLAY;\r
- restartAtFrame(currentFrameNumber);\r
- return;\r
- }\r
- case S_PAUSE_P: // to S_PAUSE_P\r
- {\r
- return;\r
- }\r
- case S_PAUSE_I: // to S_PAUSE_I\r
- {\r
- return;\r
- }\r
- case S_FFWD: // to S_FFWD\r
- {\r
- state = S_FFWD;\r
- threadStart();\r
- return;\r
- }\r
- case S_FBWD: // to S_FBWD\r
- {\r
- state = S_FBWD;\r
- threadStart();\r
- return;\r
- }\r
- case S_STOP: // to S_STOP\r
- {\r
- video->stop();\r
- video->blank();\r
- audio->stop();\r
- video->reset();\r
- demuxer->reset();\r
- audio->systemMuteOff();\r
- state = S_STOP;\r
- return;\r
- }\r
- case S_JUMP: // to S_JUMP\r
- {\r
- state = S_PLAY;\r
- restartAtFrame(jumpFrame);\r
- return;\r
- }\r
- case S_JUMP_PI: // to S_JUMP_PI\r
- {\r
- restartAtFramePI(jumpFrame);\r
- return;\r
- }\r
- }\r
- }\r
- case S_FFWD: // from S_FFWD -----------------------------------\r
- {\r
- switch(toState)\r
- {\r
- case S_PLAY: // to S_PLAY\r
- {\r
- state = S_PLAY;\r
- ULONG stepback = (ULONG)(((double)USER_RESPONSE_TIME * ifactor) * fps / 1000.);\r
- if (stepback < currentFrameNumber)\r
- currentFrameNumber -= stepback;\r
- else\r
- currentFrameNumber = 0;\r
- restartAtFrame(currentFrameNumber);\r
- return;\r
- }\r
- case S_PAUSE_P: // to S_PAUSE_P\r
- {\r
- // can't occur\r
- return;\r
- }\r
- case S_PAUSE_I: // to S_PAUSE_I\r
- {\r
- threadStop();\r
- state = S_PAUSE_I;\r
- return;\r
- }\r
- case S_FFWD: // to S_FFWD\r
- {\r
- return;\r
- }\r
- case S_FBWD: // to S_FBWD\r
- {\r
- threadStop();\r
- state = S_FBWD;\r
- threadStart();\r
- return;\r
- }\r
- case S_STOP: // to S_STOP\r
- {\r
- threadStop();\r
- video->stop();\r
- video->blank();\r
- audio->stop();\r
- video->reset();\r
- demuxer->reset();\r
- state = S_STOP;\r
- return;\r
- }\r
- case S_JUMP: // to S_JUMP\r
- {\r
- state = S_PLAY;\r
- restartAtFrame(jumpFrame);\r
- return;\r
- }\r
- case S_JUMP_PI: // to S_JUMP_PI\r
- {\r
- threadStop();\r
- state = S_PAUSE_I;\r
- restartAtFramePI(jumpFrame);\r
- return;\r
- }\r
- }\r
- }\r
- case S_FBWD: // from S_FBWD -----------------------------------\r
- {\r
- switch(toState)\r
- {\r
- case S_PLAY: // to S_PLAY\r
- {\r
- state = S_PLAY;\r
- restartAtFrame(currentFrameNumber);\r
- return;\r
- }\r
- case S_PAUSE_P: // to S_PAUSE_P\r
- {\r
- // can't occur\r
- return;\r
- }\r
- case S_PAUSE_I: // to S_PAUSE_I\r
- {\r
- threadStop();\r
- state = S_PAUSE_I;\r
- return;\r
- }\r
- case S_FFWD: // to S_FFWD\r
- {\r
- threadStop();\r
- state = S_FFWD;\r
- threadStart();\r
- return;\r
- }\r
- case S_FBWD: // to S_FBWD\r
- {\r
- return;\r
- }\r
- case S_STOP: // to S_STOP\r
- {\r
- threadStop();\r
- video->stop();\r
- video->blank();\r
- audio->stop();\r
- video->reset();\r
- demuxer->reset();\r
- state = S_STOP;\r
- return;\r
- }\r
- case S_JUMP: // to S_JUMP\r
- {\r
- state = S_PLAY;\r
- restartAtFrame(jumpFrame);\r
- return;\r
- }\r
- case S_JUMP_PI: // to S_JUMP_PI\r
- {\r
- threadStop();\r
- state = S_PAUSE_I;\r
- restartAtFramePI(jumpFrame);\r
- return;\r
- }\r
- }\r
- }\r
- case S_STOP: // from S_STOP -----------------------------------\r
- {\r
- switch(toState)\r
- {\r
- case S_PLAY: // to S_PLAY\r
- {\r
- startup = true;\r
-\r
- audio->reset();\r
- audio->setStreamType(Audio::MPEG2_PES);\r
- audio->systemMuteOff();\r
- video->reset();\r
- demuxer->reset();\r
- // FIXME use restartAtFrame here?\r
- if (currentFrameNumber > lengthFrames) currentFrameNumber = 0;\r
- demuxer->setFrameNum(currentFrameNumber);\r
- demuxer->seek();\r
- videoStartup = true;\r
- state = S_PLAY;\r
- threadStart();\r
- logger->log("Player", Log::DEBUG, "Immediate play");\r
- afeed.start();\r
- vfeed.start();\r
- tfeed.start();\r
- subtitles->start();\r
- video->sync();\r
- audio->sync();\r
- audio->play();\r
- video->pause();\r
- return;\r
- }\r
- case S_PAUSE_P: // to S_PAUSE_P\r
- {\r
- return;\r
- }\r
- case S_PAUSE_I: // to S_PAUSE_I\r
- {\r
- return;\r
- }\r
- case S_FFWD: // to S_FFWD\r
- {\r
- return;\r
- }\r
- case S_FBWD: // to S_FBWD\r
- {\r
- return;\r
- }\r
- case S_STOP: // to S_STOP\r
- {\r
- return;\r
- }\r
- case S_JUMP: // to S_JUMP\r
- {\r
- return;\r
- }\r
- case S_JUMP_PI: // to S_JUMP_PI\r
- {\r
- return;\r
- }\r
- }\r
- }\r
- // case S_JUMP cannot be a start state because it auto flips to play\r
- // case S_JUMP_PI cannot be a start state because it auto flips to S_PAUSE_I\r
- }\r
-}\r
-\r
-// ----------------------------------- Internal functions\r
-\r
-void Player::lock()\r
-{\r
-#ifndef WIN32\r
- pthread_mutex_lock(&mutex);\r
- logger->log("Player", Log::DEBUG, "LOCKED");\r
-\r
-#else\r
- WaitForSingleObject(mutex, INFINITE);\r
-#endif\r
-}\r
-\r
-void Player::unLock()\r
-{\r
-#ifndef WIN32\r
- logger->log("Player", Log::DEBUG, "UNLOCKING");\r
- pthread_mutex_unlock(&mutex);\r
-#else\r
- ReleaseMutex(mutex);\r
-#endif\r
-}\r
-\r
-void Player::restartAtFrame(ULONG newFrame)\r
-{\r
- vfeed.stop();\r
- afeed.stop();\r
- tfeed.stop();\r
- subtitles->stop();\r
- threadStop();\r
- video->stop();\r
- video->reset();\r
- audio->reset();\r
- audio->setStreamType(Audio::MPEG2_PES);\r
- demuxer->flush();\r
- demuxer->seek();\r
- currentFrameNumber = newFrame;\r
- demuxer->setFrameNum(newFrame);\r
- videoStartup = true;\r
- afeed.start();\r
- tfeed.start();\r
- vfeed.start();\r
- subtitles->start();\r
- threadStart();\r
- audio->play();\r
- video->sync();\r
- audio->sync();\r
- audio->systemMuteOff();\r
- audio->doMuting();\r
-}\r
-\r
-\r
-void Player::restartAtFramePI(ULONG newFrame)\r
-{\r
- ULLONG filePos;\r
- ULONG nextiframeNumber;\r
- ULONG iframeLength;\r
- ULONG iframeNumber;\r
-\r
- UCHAR* buffer;\r
- UINT amountReceived;\r
- UINT videoLength;\r
-\r
- // newFrame could be anywhere, go forwards to next I-Frame\r
- if (!vdr->getNextIFrame(newFrame, 1, &filePos, &nextiframeNumber, &iframeLength)) return;\r
-\r
- // Now step back a GOP. This ensures we go to the greatest I-Frame equal to or less than the requested frame\r
- vdr->getNextIFrame(nextiframeNumber, 0, &filePos, &iframeNumber, &iframeLength);\r
-\r
- buffer = vdr->getBlock(filePos, iframeLength, &amountReceived);\r
- if (!vdr->isConnected())\r
- {\r
- if (buffer) free(buffer);\r
- doConnectionLost();\r
- }\r
- else\r
- {\r
- videoLength = demuxer->stripAudio(buffer, amountReceived);\r
- video->displayIFrame(buffer, videoLength);\r
- video->displayIFrame(buffer, videoLength); // If you do it twice, it works :)\r
- free(buffer);\r
- currentFrameNumber = iframeNumber;\r
- }\r
-}\r
-\r
-void Player::doConnectionLost()\r
-{\r
- logger->log("Player", Log::DEBUG, "Connection lost, sending message");\r
- Message* m = new Message();\r
- m->to = messageReceiver;\r
- m->from = this;\r
- m->message = Message::PLAYER_EVENT;\r
- m->parameter = Player::CONNECTION_LOST;\r
- messageQueue->postMessage(m);\r
-}\r
-\r
-// ----------------------------------- Callback\r
-\r
-void Player::call(void* caller)\r
-{\r
- if (caller == demuxer)\r
- {\r
- logger->log("Player", Log::DEBUG, "Callback from demuxer");\r
-\r
- if (video->getTVsize() == Video::ASPECT4X3)\r
- {\r
- logger->log("Player", Log::DEBUG, "TV is 4:3, ignoring aspect switching");\r
- return;\r
- }\r
-\r
- int dxCurrentAspect = demuxer->getAspectRatio();\r
- if (dxCurrentAspect == Demuxer::ASPECT_4_3)\r
- {\r
- logger->log("Player", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");\r
- video->setAspectRatio(Video::ASPECT4X3);\r
-\r
- Message* m = new Message();\r
- m->from = this;\r
- m->to = messageReceiver;\r
- m->message = Message::PLAYER_EVENT;\r
- m->parameter = Player::ASPECT43;\r
- messageQueue->postMessageFromOuterSpace(m);\r
- }\r
- else if (dxCurrentAspect == Demuxer::ASPECT_16_9)\r
- {\r
- logger->log("Player", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");\r
- video->setAspectRatio(Video::ASPECT16X9);\r
-\r
- Message* m = new Message();\r
- m->from = this;\r
- m->to = messageReceiver;\r
- m->message = Message::PLAYER_EVENT;\r
- m->parameter = Player::ASPECT169;\r
- messageQueue->postMessageFromOuterSpace(m);\r
- }\r
- else\r
- {\r
- logger->log("Player", Log::DEBUG, "Demuxer said video is something else... ignoring");\r
- }\r
-\r
- }\r
- else\r
- {\r
- if (videoStartup)\r
- {\r
- videoStartup = false;\r
- video->reset();\r
- video->play();\r
- video->sync();\r
- vfeed.release();\r
- unLock();\r
- }\r
-\r
- threadSignalNoLock();\r
- }\r
-}\r
-\r
-// ----------------------------------- Feed thread\r
-\r
-void Player::threadMethod()\r
-{\r
- // this method used to be simple, the only thing it does\r
- // is farm out to threadFeed Live/Play/Scan\r
- // All the guff is to support scan hitting one end\r
-\r
- if ((state == S_FFWD) || (state == S_FBWD))\r
- {\r
- if (video->PTSIFramePlayback()) threadPTSFeedScan();\r
- else threadFeedScan();\r
- // if this returns then scan hit one end\r
- if (state == S_FFWD) // scan hit the end. stop\r
- {\r
- threadCheckExit();\r
- Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex\r
- m->to = messageReceiver;\r
- m->from = this;\r
- m->message = Message::PLAYER_EVENT;\r
- m->parameter = STOP_PLAYBACK;\r
- logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);\r
- messageQueue->postMessage(m);\r
- logger->log("Player", Log::DEBUG, "Message posted...");\r
- return;\r
- }\r
- // if execution gets to here, threadFeedScan hit the start, go to play mode\r
- state = S_PLAY;\r
- audio->reset();\r
- audio->setStreamType(Audio::MPEG2_PES);\r
- demuxer->flush();\r
- demuxer->seek();\r
- demuxer->setFrameNum(currentFrameNumber);\r
- videoStartup = true;\r
- afeed.start();\r
- tfeed.start();\r
- vfeed.start();\r
- subtitles->start();\r
- audio->play();\r
- audio->sync();\r
- audio->systemMuteOff();\r
- audio->doMuting();\r
- }\r
-\r
- if (state == S_PLAY) threadFeedPlay();\r
-}\r
-\r
-void Player::threadFeedPlay()\r
-{\r
- ULLONG feedPosition;\r
- UINT thisRead, writeLength, thisWrite, askFor;\r
- time_t lastRescan = time(NULL);\r
-\r
- feedPosition = vdr->positionFromFrameNumber(currentFrameNumber);\r
- if (!vdr->isConnected()) { doConnectionLost(); return; }\r
- logger->log("Player", Log::DEBUG, "startFeedPlay: wantedframe %i goto %llu", currentFrameNumber, feedPosition);\r
-\r
-\r
- while(1)\r
- {\r
- thisRead = 0;\r
- writeLength = 0;\r
- thisWrite = 0;\r
-\r
- threadCheckExit();\r
-\r
- // If we havn't rescanned for a while..\r
- if ((lastRescan + 60) < time(NULL))\r
- {\r
- lengthBytes = vdr->rescanRecording(&lengthFrames);\r
- if (!vdr->isConnected()) { doConnectionLost(); return; }\r
- logger->log("Player", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);\r
- lastRescan = time(NULL);\r
- }\r
-\r
- if (feedPosition >= lengthBytes) break; // finished playback\r
-\r
- if (startup)\r
- {\r
- if (startupBlockSize > lengthBytes)\r
- askFor = lengthBytes; // is a very small recording!\r
- else\r
- askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams\r
- }\r
- else\r
- {\r
- if ((feedPosition + blockSize) > lengthBytes) // last block of recording\r
- askFor = lengthBytes - feedPosition;\r
- else // normal\r
- askFor = blockSize;\r
- }\r
- //logger->log("Player", Log::DEBUG, "Get Block in");\r
-\r
- threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);\r
- //logger->log("Player", Log::DEBUG, "Get Block out");\r
-\r
- feedPosition += thisRead;\r
-\r
- if (!vdr->isConnected())\r
- {\r
- doConnectionLost();\r
- return;\r
- }\r
-\r
- if (!threadBuffer) break;\r
-\r
- if (startup)\r
- {\r
- int a_stream = demuxer->scan(threadBuffer, thisRead);\r
- demuxer->setAudioStream(a_stream);\r
- logger->log("Player", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);\r
- startup = false;\r
- }\r
-\r
- threadCheckExit();\r
-\r
- while(writeLength < thisRead)\r
- {\r
- //logger->log("Player", Log::DEBUG, "Put in");\r
- thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);\r
- //logger->log("Player", Log::DEBUG, "Put out");\r
- writeLength += thisWrite;\r
-\r
- if (!thisWrite)\r
- {\r
- // demuxer is full and can't take anymore\r
- threadLock();\r
- threadWaitForSignal();\r
- threadUnlock();\r
- }\r
-\r
- threadCheckExit();\r
- }\r
-\r
- free(threadBuffer);\r
- threadBuffer = NULL;\r
-\r
- }\r
-\r
- // end of recording\r
- logger->log("Player", Log::DEBUG, "Recording playback ends");\r
-\r
- if (videoStartup) // oh woe. there never was a stream, I was conned!\r
- {\r
- videoStartup = false;\r
- unLock();\r
- MILLISLEEP(500); // I think this will solve a race\r
- }\r
-\r
- threadCheckExit();\r
-\r
-\r
- Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex\r
- m->to = messageReceiver;\r
- m->from = this;\r
- m->message = Message::PLAYER_EVENT;\r
- m->parameter = Player::STOP_PLAYBACK;\r
- logger->log("Player", Log::DEBUG, "Posting message to %p...", messageQueue);\r
- messageQueue->postMessage(m);\r
-}\r
-\r
-\r
-void Player::threadPTSFeedScan()\r
-{\r
- // This method manipulates the PTS instead of waiting, this is for the android devices, maybe later for windows?\r
-\r
- ULONG direction = 0;\r
- int dir_fac=-1;\r
- ULONG baseFrameNumber = 0;\r
- ULONG iframeNumber = 0;\r
- ULONG iframeLength = 0;\r
- ULONG currentfeedFrameNumber=currentFrameNumber;\r
- ULONG firstFrameNumber=currentFrameNumber;\r
- ULLONG filePos;\r
- UINT amountReceived;\r
- UINT videoLength;\r
-\r
- UINT playtime=0;\r
-\r
-#ifndef WIN32\r
- struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info\r
- struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data\r
- struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame\r
-#else\r
- DWORD clock0 = 0, clock1 = 0, clock2 = 0;\r
-#endif\r
-\r
- int frameTimeOffset = 0; // Time in msec between frames\r
- int disp_msec = 0; // Time taken to display data\r
- int total_msec = 0; // Time taken to fetch data and display it\r
- int sleepTime = 0;\r
-\r
- if (state == S_FFWD) {\r
- direction = 1; // and 0 for backward\r
- dir_fac=1;\r
- }\r
- video->EnterIframePlayback();\r
-\r
- while(1)\r
- {\r
- // Fetch I-frames until we get one that can be displayed in good time\r
- // Repeat while clock0 + total_msec > clock2 + frameTimeOffset\r
-\r
- baseFrameNumber = currentfeedFrameNumber;\r
-\r
- threadCheckExit();\r
- if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))\r
- return;\r
-\r
- if (iframeNumber >= lengthFrames) return;\r
- // scan has got to the end of what we knew to be there before we started scanning\r
-\r
- baseFrameNumber = iframeNumber;\r
-\r
- frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentfeedFrameNumber) * 1000) / (fps * (double)ifactor));\r
-\r
- logger->log("Player", Log::DEBUG, "XXX Got frame");\r
-\r
- threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);\r
-\r
- if (!vdr->isConnected())\r
- {\r
- if (threadBuffer) free(threadBuffer);\r
- doConnectionLost();\r
- break;\r
- }\r
-\r
-\r
- threadCheckExit();\r
-\r
-\r
- videoLength = demuxer->stripAudio(threadBuffer, amountReceived);\r
- demuxer->changeTimes(threadBuffer,videoLength,playtime);\r
- int count=0;\r
- while (!video->displayIFrame(threadBuffer, videoLength)) // the device might block\r
- {\r
- MILLISLEEP(20);\r
- threadCheckExit();\r
- count++;\r
- if (count%300==0) {\r
- ULLONG cur_time=video->getCurrentTimestamp();\r
- // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",\r
- // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);\r
- if (cur_time!=0) {\r
- currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.));\r
- }\r
- }\r
-\r
- }\r
- playtime +=frameTimeOffset;\r
- currentfeedFrameNumber = iframeNumber;\r
- {\r
- ULLONG cur_time=video->getCurrentTimestamp();\r
- // logger->log("Player", Log::ERR, "FFN: %d CFN: %d dirfac %d time %lld ifac %d fps %g",\r
- // firstFrameNumber, currentFrameNumber,dir_fac,cur_time,ifactor,fps);\r
- if (cur_time!=0) {\r
- currentFrameNumber=firstFrameNumber+(int)(((double)(dir_fac*((long long)cur_time)*((int)ifactor))*fps/90000.));\r
- }\r
- }\r
-\r
- free(threadBuffer);\r
- threadBuffer = NULL;\r
- }\r
-}\r
-\r
-\r
-\r
-void Player::threadFeedScan()\r
-{\r
- // This method is actually really simple - get frame from vdr,\r
- // spit it at the video chip, wait for a time. Most of the code here\r
- // is to get the wait right so that the scan occurs at the correct rate.\r
-\r
- ULONG direction = 0;\r
- ULONG baseFrameNumber = 0;\r
- ULONG iframeNumber = 0;\r
- ULONG iframeLength = 0;\r
- ULLONG filePos;\r
- UINT amountReceived;\r
- UINT videoLength;\r
-\r
-#ifndef WIN32\r
- struct timeval clock0 = {0,0}; // Time stamp after fetching I-frame info\r
- struct timeval clock1 = {0,0}; // Time stamp after fetching I-frame data\r
- struct timeval clock2 = {0,0} ; // Time stamp after displaying I-frame\r
-#else\r
- DWORD clock0 = 0, clock1 = 0, clock2 = 0;\r
-#endif\r
-\r
- int frameTimeOffset = 0; // Time in msec between frames\r
- int disp_msec = 0; // Time taken to display data\r
- int total_msec = 0; // Time taken to fetch data and display it\r
- int sleepTime = 0;\r
-\r
- if (state == S_FFWD) direction = 1; // and 0 for backward\r
-\r
- while(1)\r
- {\r
- // Fetch I-frames until we get one that can be displayed in good time\r
- // Repeat while clock0 + total_msec > clock2 + frameTimeOffset\r
-\r
- baseFrameNumber = currentFrameNumber;\r
- do\r
- {\r
- threadCheckExit();\r
- if (!vdr->getNextIFrame(baseFrameNumber, direction, &filePos, &iframeNumber, &iframeLength))\r
- return;\r
-\r
- if (iframeNumber >= lengthFrames) return;\r
- // scan has got to the end of what we knew to be there before we started scanning\r
-\r
- baseFrameNumber = iframeNumber;\r
- frameTimeOffset =(int) ((double)(abs((int)iframeNumber - (int)currentFrameNumber) * 1000) / (fps * (double)ifactor));\r
-#ifndef WIN32\r
- gettimeofday(&clock0, NULL);\r
-#else\r
- clock0 = timeGetTime();\r
-#endif\r
- }\r
-#ifndef WIN32\r
- while (clock2.tv_sec != 0 &&\r
- (clock0.tv_sec - clock2.tv_sec) * 1000 +\r
- (clock0.tv_usec - clock2.tv_usec) / 1000 > frameTimeOffset - total_msec);\r
-#else\r
- while (clock2 != 0 && clock0 + total_msec > clock2 + frameTimeOffset);\r
-#endif\r
- logger->log("Player", Log::DEBUG, "XXX Got frame");\r
-\r
- threadBuffer = vdr->getBlock(filePos, iframeLength, &amountReceived);\r
- \r
- if (!vdr->isConnected())\r
- {\r
- if (threadBuffer) free(threadBuffer);\r
- doConnectionLost();\r
- break;\r
- }\r
- \r
-#ifndef WIN32\r
- gettimeofday(&clock1, NULL);\r
- if (clock2.tv_sec != 0)\r
- sleepTime = (clock2.tv_sec - clock1.tv_sec) * 1000\r
- + (clock2.tv_usec - clock1.tv_usec) / 1000\r
- + frameTimeOffset - disp_msec;\r
-#else\r
- clock1 = timeGetTime();\r
- if (clock2 != 0)\r
- sleepTime = clock2 + frameTimeOffset - disp_msec - clock1;\r
-#endif\r
- if (sleepTime < 0) sleepTime = 0;\r
- threadCheckExit();\r
- MILLISLEEP(sleepTime);\r
- logger->log("Player", Log::DEBUG, "XXX Slept for %d", sleepTime);\r
-\r
- videoLength = demuxer->stripAudio(threadBuffer, amountReceived);\r
- video->displayIFrame(threadBuffer, videoLength);\r
- currentFrameNumber = iframeNumber;\r
- free(threadBuffer);\r
- threadBuffer = NULL;\r
-\r
-#ifndef WIN32\r
- gettimeofday(&clock2, NULL);\r
- total_msec = (clock2.tv_sec - clock0.tv_sec) * 1000\r
- + (clock2.tv_usec - clock0.tv_usec) / 1000\r
- - sleepTime;\r
- disp_msec = (clock2.tv_sec - clock1.tv_sec) * 1000\r
- + (clock2.tv_usec - clock1.tv_usec) / 1000\r
- - sleepTime;\r
-#else\r
- clock2 = timeGetTime();\r
- total_msec = clock2 - clock0 - sleepTime;\r
- disp_msec = clock2 - clock1 - sleepTime;\r
-#endif\r
- logger->log("Player", Log::DEBUG, "XXX disp_msec = %4d total_msec = %4d", disp_msec, total_msec);\r
- }\r
-}\r
-\r
-void Player::threadPostStopCleanup()\r
-{\r
- if (threadBuffer)\r
- {\r
- free(threadBuffer);\r
- threadBuffer = NULL;\r
- }\r
-}\r
-\r
-// ----------------------------------- Dev\r
-\r
-#ifdef DEV\r
-void Player::test1()\r
-{\r
- logger->log("Player", Log::DEBUG, "PLAYER TEST 1");\r
-}\r
-\r
-void Player::test2()\r
-{\r
- logger->log("Player", Log::DEBUG, "PLAYER TEST 2");\r
-}\r
-#endif\r
+/*
+ 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;
+ unsigned int demux_audio_size=524288;
+ if (video->supportsh264()) {
+ demux_video_size*=5*2;//5;
+
+ }
+ if (audio->maysupportAc3()) {
+ //demux_audio_size*=2;
+ }
+
+ if (!demuxer->init(this, audio, video,teletext, demux_video_size,demux_audio_size,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, int streamtype)
+{
+ if (is_pesrecording) {
+ demuxer->setAudioStream(newChannel);
+ return;
+ } else {
+ ((DemuxerTS*)demuxer)->setAID(newChannel,type,streamtype);
+ 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 NULL; //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
-/*\r
- Copyright 2004-2008 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef PLAYER_H\r
-#define PLAYER_H\r
-\r
-#include <stdio.h>\r
-#include <stdlib.h>\r
-#ifndef WIN32\r
-#include <sys/time.h>\r
-#endif\r
-#include <time.h>\r
-\r
-#include "threadsystem.h"\r
-\r
-#include "callback.h"\r
-#include "defines.h"\r
-#include "vfeed.h"\r
-#include "afeed.h"\r
-#include "tfeed.h"\r
-\r
-#include "teletextdecodervbiebu.h"\r
-\r
-class MessageQueue;\r
-class Audio;\r
-class Video;\r
-class VDR;\r
-class Log;\r
-class Demuxer;\r
-class OSDReceiver;\r
-class DVBSubtitles;\r
-class Channel;\r
-\r
-class Player : public Thread_TYPE, public Callback\r
-{\r
- public:\r
- Player(MessageQueue* messageQueue, void* messageReceiver, OSDReceiver* osdReceiver);\r
- virtual ~Player();\r
-\r
- int init(bool p_isPesRecording,double framespersec);\r
- int shutdown();\r
- void setStartFrame(ULONG frameNum);\r
- void setLengthBytes(ULLONG length);\r
- void setLengthFrames(ULONG length);\r
- void setAudioChannel(int newChannel, int type, int streamtype);\r
- void setSubtitleChannel(int newChannel);\r
- bool toggleSubtitles();\r
- void turnSubtitlesOn(bool ison); \r
- bool isSubtitlesOn() { return subtitlesShowing; }\r
-\r
- void play();\r
- void stop();\r
- void pause();\r
- void playpause();\r
- void fastForward();\r
- void fastBackward();\r
- void jumpToPercent(double percent);\r
- void skipForward(int seconds);\r
- void skipBackward(int seconds);\r
- void jumpToMark(int mark);\r
- void jumpToFrameP(int newFrame);\r
-\r
- UCHAR getState() { return state; }\r
- ULONG getCurrentFrameNum();\r
- ULONG getLengthFrames();\r
- UCHAR getIScanRate() { return ifactor; }\r
- bool* getDemuxerMpegAudioChannels();\r
- bool* getDemuxerAc3AudioChannels();\r
- bool* getDemuxerSubtitleChannels();\r
- int *getTeletxtSubtitlePages();\r
- int getCurrentAudioChannel();\r
- int getCurrentSubtitleChannel();\r
- bool isPesRecording() { return is_pesrecording; }\r
- Channel *getDemuxerChannel();\r
-\r
- TeletextDecoderVBIEBU * getTeletextDecoder() { return teletext; }\r
-\r
- void call(void*); // for callback interface\r
-\r
- const static UCHAR S_PLAY = 1;\r
- const static UCHAR S_PAUSE_P = 2;\r
- const static UCHAR S_PAUSE_I = 3;\r
- const static UCHAR S_FFWD = 4;\r
- const static UCHAR S_FBWD = 5;\r
- const static UCHAR S_STOP = 6;\r
- const static UCHAR S_JUMP = 7;\r
- const static UCHAR S_JUMP_PI = 8; // Jump to Pause_I mode\r
-\r
- // Player events\r
-\r
- // FIXME so far this just duplicates the old system + the wss\r
-\r
- const static UCHAR CONNECTION_LOST = 1;\r
- const static UCHAR STOP_PLAYBACK = 2;\r
- const static UCHAR STREAM_END = 3;\r
- const static UCHAR ASPECT43 = 4;\r
- const static UCHAR ASPECT169 = 5;\r
-\r
-#ifdef DEV\r
- void test1();\r
- void test2();\r
-#endif\r
-\r
- protected:\r
- void threadMethod();\r
- void threadPostStopCleanup();\r
-\r
- private:\r
- void switchState(UCHAR newState, ULONG jumpFrame=0);\r
-\r
- void threadFeedPlay();\r
- void threadFeedScan();\r
- void threadPTSFeedScan();\r
-\r
- void doConnectionLost();\r
- void restartAtFrame(ULONG newFrame);\r
- void restartAtFramePI(ULONG newFrame);\r
-\r
- bool subtitlesShowing;\r
- MessageQueue* messageQueue;\r
- void* messageReceiver;\r
- OSDReceiver* osdReceiver;\r
- Log* logger;\r
- Audio* audio;\r
- Video* video;\r
- Demuxer* demuxer;\r
- DVBSubtitles* subtitles;\r
- VDR* vdr;\r
- VFeed vfeed;\r
- AFeed afeed;\r
- TFeed tfeed;\r
- TeletextDecoderVBIEBU *teletext;\r
- \r
- \r
-\r
- bool initted;\r
- bool startup;\r
- bool videoStartup;\r
-\r
- bool is_pesrecording;\r
- double fps;\r
-\r
-#ifndef WIN32\r
- pthread_mutex_t mutex;\r
-#else\r
- HANDLE mutex;\r
-#endif\r
- void lock();\r
- void unLock();\r
-\r
- ULLONG lengthBytes;\r
- ULONG lengthFrames;\r
- ULONG currentFrameNumber;\r
- UINT blockSize;\r
- UINT startupBlockSize;\r
- UCHAR* threadBuffer;\r
- UCHAR state;\r
- UCHAR ifactor;\r
-};\r
-\r
-#endif\r
-\r
-\r
-/*\r
-\r
-Possible states:\r
-\r
-Play, Pause, FFwd, FBwd, (Stop), [Jump]\r
-\r
- Possible Working\r
-\r
-Play -> PauseP * *\r
- -> PauseI\r
- -> FFwd * *\r
- -> FBwd * *\r
- -> Stop * *\r
- -> Jump * *\r
- -> Jump_PI * *\r
-\r
-PauseP -> Play * *\r
- -> PauseI\r
- -> FFwd * *\r
- -> FBwd * *\r
- -> Stop * *\r
- -> Jump * *\r
- -> Jump_PI * *\r
-\r
-PauseI -> Play * *\r
- -> PauseP\r
- -> FFwd * *\r
- -> FBwd * *\r
- -> Stop * *\r
- -> Jump * *\r
- -> Jump_PI * *\r
-\r
-FFwd -> Play * *\r
- -> PauseP\r
- -> PauseI * *\r
- -> FBwd * *\r
- -> Stop * *\r
- -> Jump * *\r
- -> Jump_PI * *\r
-\r
-FBwd -> Play * *\r
- -> PauseP\r
- -> PauseI * *\r
- -> FFwd * *\r
- -> Stop * *\r
- -> Jump * *\r
- -> Jump_PI * *\r
-\r
-Stop -> Play * *\r
- -> PauseP\r
- -> PauseI\r
- -> FFwd\r
- -> FBwd\r
- -> Jump\r
- -> Jump_PI\r
-\r
-*/\r
+/*
+ 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 <stdio.h>
+#include <stdlib.h>
+#ifndef WIN32
+#include <sys/time.h>
+#endif
+#include <time.h>
+
+#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, int streamtype);
+ 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
+
+*/
-/*\r
- Copyright 2008 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#include "playerliveradio.h"\r
-\r
-#include "log.h"\r
-#include "audio.h"\r
-#include "demuxerts.h"\r
-#include "vdr.h"\r
-#include "messagequeue.h"\r
-#include "remote.h"\r
-#include "message.h"\r
-#include "channel.h"\r
-#include "video.h"\r
-\r
-// ----------------------------------- Called from outside, one offs or info funcs\r
-\r
-PlayerLiveRadio::PlayerLiveRadio(MessageQueue* tmessageQueue, void* tmessageReceiver, ChannelList* tchanList)\r
-: afeed(this)\r
-{\r
- messageQueue = tmessageQueue;\r
- messageReceiver = tmessageReceiver;\r
- chanList = tchanList;\r
- \r
- audio = Audio::getInstance();\r
- logger = Log::getInstance();\r
- vdr = VDR::getInstance();\r
- initted = false;\r
-\r
- stopNow = false;\r
- state = S_STOP;\r
- Video::getInstance()->turnVideoOff();\r
-}\r
-\r
-PlayerLiveRadio::~PlayerLiveRadio()\r
-{\r
- if (initted) shutdown();\r
-}\r
-\r
-int PlayerLiveRadio::init()\r
-{\r
- if (initted) return 0;\r
-\r
- demuxer = new DemuxerTS();\r
- if (!demuxer) return 0;\r
- \r
- if (!demuxer->init(this, audio, NULL, NULL, 0, 200000,0))\r
- {\r
- logger->log("PlayerLiveRadio", Log::ERR, "Demuxer failed to init");\r
- shutdown();\r
- return 0;\r
- }\r
-\r
- afeed.init();\r
- audio->stop();\r
-\r
- initted = true;\r
- return 1;\r
-}\r
-\r
-int PlayerLiveRadio::shutdown()\r
-{\r
- if (!initted) return 0;\r
- stop();\r
- initted = false;\r
-\r
- delete demuxer;\r
-\r
-\r
-\r
- return 1;\r
-}\r
-\r
-bool* PlayerLiveRadio::getDemuxerMpegAudioChannels()\r
-{\r
- return demuxer->getmpAudioChannels();\r
-}\r
-\r
-bool* PlayerLiveRadio::getDemuxerAc3AudioChannels()\r
-{\r
- return demuxer->getac3AudioChannels();\r
-}\r
-\r
-int PlayerLiveRadio::getCurrentAudioChannel()\r
-{\r
- return demuxer->getAID();\r
-}\r
-\r
-int *PlayerLiveRadio::getTeletxtSubtitlePages(){\r
- return NULL;\r
-}\r
-\r
-int PlayerLiveRadio::getCurrentSubtitleChannel(){\r
- return demuxer->getSubID();\r
-}\r
-\r
-void PlayerLiveRadio::setAudioChannel(int newChannel, int type,int streamtype)\r
-{\r
- demuxer->setAID(newChannel, type,streamtype);\r
-}\r
-\r
-void PlayerLiveRadio::setSubtitleChannel(int newChannel)\r
-{\r
- demuxer->setSubID(newChannel);\r
-}\r
-\r
-// ----------------------------------- Externally called events\r
-\r
-void PlayerLiveRadio::go(ULONG index)\r
-{\r
- struct PLInstruction i;\r
- i.instruction = I_SETCHANNEL;\r
- i.channelIndex = index;\r
- instructions.push(i);\r
- threadStart();\r
-}\r
-\r
-void PlayerLiveRadio::setChannel(ULONG index)\r
-{\r
- logger->log("PlayerLiveRadio", Log::DEBUG, "setChannel");\r
- struct PLInstruction i;\r
- i.instruction = I_SETCHANNEL;\r
- i.channelIndex = index;\r
- instructions.push(i); \r
- logger->log("PlayerLiveRadio", Log::DEBUG, "posted setChannel instruction, now %i in queue", instructions.size());\r
- threadSignalNoLock();\r
-}\r
-\r
-void PlayerLiveRadio::stop()\r
-{\r
- logger->log("PlayerLiveRadio", Log::DEBUG, "stop");\r
- struct PLInstruction i;\r
- i.instruction = I_STOP;\r
- instructions.push(i);\r
- threadSignal();\r
- threadStop();\r
-}\r
-\r
-// ----------------------------------- Callback\r
-\r
-void PlayerLiveRadio::call(void* caller)\r
-{\r
-}\r
-\r
-// -----------------------------------\r
-\r
-void PlayerLiveRadio::streamReceive(ULONG flag, void* data, ULONG len)\r
-{\r
- // Flag:\r
- // 0 = normal stream packet\r
- // 1 = stream end\r
- // 2 = connection lost\r
-\r
- if (flag == 1)\r
- {\r
- if (data) abort();\r
- \r
- Message* m = new Message();\r
- m->from = this;\r
- m->to = messageReceiver;\r
- m->message = Message::PLAYER_EVENT;\r
- m->parameter = PlayerLiveRadio::STREAM_END;\r
- messageQueue->postMessageFromOuterSpace(m);\r
- }\r
- \r
- if (streamChunks.size() < 11)\r
- {\r
- StreamChunk s;\r
- s.data = data;\r
- s.len = len;\r
- streamChunks.push(s);\r
- threadSignalNoLock();\r
- }\r
- else\r
- {\r
- // Too many chunks in streamChunks, drop this chunk\r
- free(data);\r
- logger->log("PlayerLiveRadio", Log::WARN, "Dropped chunk");\r
- }\r
-}\r
-\r
-void PlayerLiveRadio::clearStreamChunks()\r
-{\r
- while(streamChunks.size())\r
- {\r
- logger->log("PlayerLiveRadio", Log::DEBUG, "Dropping chunk from old stream");\r
- struct StreamChunk s = streamChunks.front();\r
- streamChunks.pop();\r
- free(s.data);\r
- }\r
-}\r
-\r
-void PlayerLiveRadio::chunkToDemuxer()\r
-{\r
- StreamChunk s = streamChunks.front();\r
- streamChunks.pop();\r
- //logger->log("PlayerLiveRadio", Log::DEBUG, "About to call demuxer with %p %lu", s.data, s.len);\r
- /*int a =*/ demuxer->put((UCHAR*)s.data, s.len);\r
- //logger->log("PlayerLiveRadio", Log::DEBUG, "put %i to demuxer", a);\r
- free(s.data); \r
-}\r
-\r
-void PlayerLiveRadio::switchState(UCHAR newState)\r
-{\r
- logger->log("PlayerLiveRadio", Log::DEBUG, "Switch from state %u to state %u", state, newState);\r
-\r
- switch(state)\r
- {\r
- case S_STOP: // FROM S_STOP\r
- {\r
- switch(newState)\r
- {\r
- case S_PREBUFFERING:\r
- {\r
- audio->stop();\r
- audio->unPause();\r
- audio->reset();\r
- audio->setStreamType(Audio::MPEG2_PES);\r
- audio->systemMuteOff(); \r
- audio->doMuting(); \r
- audio->play();\r
- audio->pause();\r
- demuxer->reset();\r
- afeed.start();\r
- \r
- state = newState;\r
- preBufferCount = 0;\r
- return;\r
- }\r
- default:\r
- {\r
- logger->log("PlayerLiveRadio", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);\r
- abort();\r
- break;\r
- }\r
- }\r
- }\r
-\r
- case S_PREBUFFERING: // FROM S_PREBUFFERING\r
- {\r
- switch(newState)\r
- {\r
- case S_PLAY:\r
- {\r
- audio->unPause();\r
- state = newState;\r
- return;\r
- }\r
- case S_STOP:\r
- {\r
- vdr->stopStreaming();\r
- clearStreamChunks();\r
- afeed.stop();\r
- audio->stop();\r
- audio->reset();\r
- state = newState;\r
- return; \r
- }\r
- case S_PREBUFFERING:\r
- {\r
- vdr->stopStreaming();\r
- clearStreamChunks();\r
- afeed.stop();\r
- audio->stop();\r
- audio->reset();\r
- audio->play();\r
- audio->pause();\r
- demuxer->reset();\r
- afeed.start();\r
-\r
- state = newState;\r
- preBufferCount = 0;\r
- return; \r
- }\r
- default:\r
- {\r
- logger->log("PlayerLiveRadio", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);\r
- abort();\r
- break;\r
- } \r
- }\r
- }\r
- \r
- case S_PLAY: // FROM S_PLAY\r
- {\r
- switch(newState)\r
- {\r
- case S_STOP:\r
- { \r
- vdr->stopStreaming();\r
- clearStreamChunks();\r
- afeed.stop();\r
- audio->stop();\r
- audio->reset();\r
- state = newState;\r
- return;\r
- }\r
- case S_PREBUFFERING: // IS THIS HOW IT WORKS?\r
- {\r
- vdr->stopStreaming();\r
- clearStreamChunks();\r
- afeed.stop();\r
- audio->stop();\r
- audio->reset();\r
- audio->play();\r
- audio->pause();\r
- demuxer->reset();\r
- afeed.start();\r
-\r
- state = newState;\r
- preBufferCount = 0;\r
- return;\r
- }\r
- default:\r
- {\r
- logger->log("PlayerLiveRadio", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);\r
- abort();\r
- break;\r
- } \r
- }\r
- } \r
- } \r
-}\r
-\r
-bool PlayerLiveRadio::checkError()\r
-{\r
- if (!vdr->isConnected())\r
- {\r
- switchState(S_STOP);\r
- \r
- Message* m = new Message();\r
- m->from = this;\r
- m->to = messageReceiver;\r
- m->message = Message::PLAYER_EVENT;\r
- m->parameter = PlayerLiveRadio::CONNECTION_LOST;\r
- messageQueue->postMessageFromOuterSpace(m);\r
- \r
- return true;\r
- } \r
- return false;\r
-}\r
-\r
-void PlayerLiveRadio::optimizeInstructionQueue()\r
-{\r
- // Walk the list\r
- \r
- // Currently there are only 2 instruction types, so this is a bit overkill...\r
-\r
- struct PLInstruction i;\r
- while(instructions.size() > 1)\r
- {\r
- i = instructions.front();\r
- if (i.instruction == I_SETCHANNEL)\r
- {\r
- instructions.pop(); // if this is the first of more than 1 command, currently it cannot possibly be relevant\r
- }\r
- else if (i.instruction == I_STOP)\r
- {\r
- return; // return here and ensure the next instruction will be stop\r
- }\r
- }\r
-}\r
-\r
-void PlayerLiveRadio::threadMethod()\r
-{\r
- while(1)\r
- {\r
- while(!instructions.empty())\r
- {\r
- if (instructions.size() > 1)\r
- {\r
- logger->log("PlayerLiveRadio", Log::DEBUG, "Should optimise");\r
- optimizeInstructionQueue();\r
- }\r
-\r
- struct PLInstruction i = instructions.front();\r
- instructions.pop();\r
- \r
- if (i.instruction == I_SETCHANNEL)\r
- {\r
- logger->log("PlayerLiveRadio", Log::DEBUG, "start new stream");\r
-\r
- switchState(S_PREBUFFERING);\r
-\r
- if (!checkError())\r
- {\r
- Channel* chan = (*chanList)[i.channelIndex];\r
- chan->loadPids();\r
-\r
- bool found=false;\r
-\r
- if (chan->numAPids > 0) \r
- {\r
- int j=0;\r
- while (j<chan->numAPids && !found) {\r
- if (Audio::getInstance()->streamTypeSupported(chan->apids[j].type)) {\r
- demuxer->setAID(chan->apids[j].pid,0,chan->apids[j].type);\r
- audio->setStreamType(Audio::MPEG2_PES);\r
- logger->log("PlayerLiveRadio", Log::DEBUG, "Demuxer pids: %u %u %u", chan->vpid, chan->apids[j].pid,chan->apids[j].type);\r
- found=true;\r
- }\r
- j++;\r
- }\r
- }\r
-\r
- if (!found)\r
- {\r
- if (chan->numDPids > 0 && audio->maysupportAc3())\r
- {\r
- int j=0;\r
- while (j<chan->numDPids && !found) {\r
- if (Audio::getInstance()->streamTypeSupported(chan->dpids[j].type)) {\r
- demuxer->setAID(chan->dpids[j].pid,1,chan->dpids[j].type);\r
- audio->setStreamType(Audio::MPEG2_PES);\r
- logger->log("PlayerLiveRadio", Log::DEBUG, "Demuxer pids: %u %u (ac3) %u", chan->vpid, chan->dpids[j].pid,chan->dpids[j].type);\r
- found=true;\r
- }\r
- j++;\r
- }\r
- }\r
- else\r
- {\r
- logger->log("PlayerLiveRadio", Log::WARN, "Demuxer no pids!");\r
- }\r
- }\r
-\r
-\r
-\r
- int streamSuccess = vdr->streamChannel(chan->number, this);\r
- if (!checkError() && !streamSuccess)\r
- { \r
- Message* m = new Message();\r
- m->from = this;\r
- m->to = messageReceiver;\r
- m->message = Message::PLAYER_EVENT;\r
- m->parameter = PlayerLiveRadio::STREAM_END;\r
- messageQueue->postMessageFromOuterSpace(m);\r
- }\r
- }\r
- }\r
- else if (i.instruction == I_STOP)\r
- {\r
- logger->log("PlayerLiveRadio", Log::DEBUG, "Stopping");\r
- switchState(S_STOP);\r
- checkError();\r
-\r
- stopNow = true;\r
- break;\r
- }\r
- }\r
-\r
- if (stopNow) break;\r
-\r
- while(streamChunks.size())\r
- {\r
- chunkToDemuxer();\r
-\r
- if (state == S_PREBUFFERING)\r
- {\r
- ++preBufferCount;\r
- ULONG percentDone = (ULONG)(preBufferCount / (float)preBufferAmount * 100);\r
- logger->log("PlayerLiveRadio", Log::DEBUG, "Prebuffering %lu%%", percentDone);\r
- \r
- Message* m = new Message();\r
- m->from = this;\r
- m->to = messageReceiver;\r
- m->message = Message::PLAYER_EVENT;\r
- m->parameter = PlayerLiveRadio::PREBUFFERING;\r
- m->tag = percentDone;\r
- messageQueue->postMessageFromOuterSpace(m);\r
-\r
- if (preBufferCount == preBufferAmount)\r
- {\r
- switchState(S_PLAY);\r
- checkError();\r
- }\r
- }\r
- }\r
- \r
- threadLock();\r
- threadWaitForSignal(); // unlocks and waits for signal\r
- threadUnlock();\r
- }\r
-\r
- logger->log("PlayerLiveRadio", Log::DEBUG, "End of thread");\r
-}\r
-\r
+/*
+ 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,int streamtype)
+{
+ demuxer->setAID(newChannel, type,streamtype);
+}
+
+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();
+
+ bool found=false;
+
+ if (chan->numAPids > 0)
+ {
+ int j=0;
+ while (j<chan->numAPids && !found) {
+ if (Audio::getInstance()->streamTypeSupported(chan->apids[j].type)) {
+ demuxer->setAID(chan->apids[j].pid,0,chan->apids[j].type);
+ audio->setStreamType(Audio::MPEG2_PES);
+ logger->log("PlayerLiveRadio", Log::DEBUG, "Demuxer pids: %u %u %u", chan->vpid, chan->apids[j].pid,chan->apids[j].type);
+ found=true;
+ }
+ j++;
+ }
+ }
+
+ if (!found)
+ {
+ if (chan->numDPids > 0 && audio->maysupportAc3())
+ {
+ int j=0;
+ while (j<chan->numDPids && !found) {
+ if (Audio::getInstance()->streamTypeSupported(chan->dpids[j].type)) {
+ demuxer->setAID(chan->dpids[j].pid,1,chan->dpids[j].type);
+ audio->setStreamType(Audio::MPEG2_PES);
+ logger->log("PlayerLiveRadio", Log::DEBUG, "Demuxer pids: %u %u (ac3) %u", chan->vpid, chan->dpids[j].pid,chan->dpids[j].type);
+ found=true;
+ }
+ j++;
+ }
+ }
+ 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");
+}
+
-/*\r
- Copyright 2007 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#include "playerlivetv.h"\r
-\r
-#include "log.h"\r
-#include "audio.h"\r
-#include "video.h"\r
-#include "demuxerts.h"\r
-#include "vdr.h"\r
-#include "messagequeue.h"\r
-#include "remote.h"\r
-#include "message.h"\r
-#include "channel.h"\r
-#include "dvbsubtitles.h"\r
-#include "osdreceiver.h"\r
-\r
-// ----------------------------------- Called from outside, one offs or info funcs\r
-\r
-PlayerLiveTV::PlayerLiveTV(MessageQueue* tmessageQueue, void* tmessageReceiver, OSDReceiver* tosdReceiver, ChannelList* tchanList)\r
-: vfeed(this), afeed(this), tfeed(this)\r
-{\r
- messageQueue = tmessageQueue;\r
- messageReceiver = tmessageReceiver;\r
- osdReceiver = tosdReceiver;\r
- chanList = tchanList;\r
- \r
- audio = Audio::getInstance();\r
- video = Video::getInstance();\r
- logger = Log::getInstance();\r
- vdr = VDR::getInstance();\r
- initted = false;\r
-\r
- subtitlesShowing = false;\r
- videoStartup = false;\r
- pendingAudioPlay = false;\r
-\r
- stopNow = false;\r
- state = S_STOP;\r
-\r
- video->turnVideoOn();\r
-}\r
-\r
-PlayerLiveTV::~PlayerLiveTV()\r
-{\r
- if (initted) shutdown();\r
-}\r
-\r
-int PlayerLiveTV::init()\r
-{\r
- if (initted) return 0;\r
-\r
- demuxer = new DemuxerTS();\r
- if (!demuxer) return 0;\r
- subtitles = new DVBSubtitles(osdReceiver);\r
- if (!subtitles) return 0;\r
-\r
- teletext = new TeletextDecoderVBIEBU();\r
- \r
- unsigned int demux_video_size=2097152;\r
- unsigned int demux_audio_size=524288;\r
- if (video->supportsh264()) {\r
- demux_video_size*=5*1;//5;\r
-\r
- }\r
- if (audio->maysupportAc3()) {\r
- //demux_audio_size*=2;\r
- }\r
-\r
- int text_fak=video->getTeletextBufferFaktor();\r
-\r
- \r
- if (!demuxer->init(this, audio, video, teletext, demux_video_size,demux_audio_size, 65536*text_fak,25./*unimportant*/,subtitles))\r
- {\r
- logger->log("PlayerLiveTV", Log::ERR, "Demuxer failed to init");\r
- shutdown();\r
- return 0;\r
- }\r
-\r
- vfeed.init();\r
- afeed.init();\r
- tfeed.init();\r
-\r
- video->stop();\r
- video->blank();\r
- audio->stop();\r
-\r
- initted = true;\r
- return 1;\r
-}\r
-\r
-int PlayerLiveTV::shutdown()\r
-{\r
- if (!initted) return 0;\r
- logger->log("PlayerLiveTV", Log::DEBUG, "Shutdown");\r
- stop();\r
- initted = false;\r
-\r
- delete demuxer;\r
- delete subtitles;\r
- delete teletext;\r
- teletext = NULL;\r
- return 1;\r
-}\r
-\r
-bool* PlayerLiveTV::getDemuxerMpegAudioChannels()\r
-{\r
- return demuxer->getmpAudioChannels();\r
-}\r
-\r
-bool* PlayerLiveTV::getDemuxerAc3AudioChannels()\r
-{\r
- return demuxer->getac3AudioChannels();\r
-}\r
-\r
-int PlayerLiveTV::getCurrentAudioChannel()\r
-{\r
- return demuxer->getAID();\r
-}\r
-\r
-void PlayerLiveTV::setAudioChannel(int newChannel, int type,int streamtype)\r
-{\r
- demuxer->setAID(newChannel,type,streamtype);\r
-}\r
-\r
-void PlayerLiveTV::setSubtitleChannel(int newChannel)\r
-{\r
- demuxer->setSubID(newChannel);\r
-}\r
-\r
-int *PlayerLiveTV::getTeletxtSubtitlePages()\r
-{\r
- return teletext->getSubtitlePages();\r
-}\r
-\r
-int PlayerLiveTV::getCurrentSubtitleChannel(){\r
- return demuxer->getSubID();\r
-}\r
-\r
-bool PlayerLiveTV::toggleSubtitles()\r
-{\r
- if (!subtitlesShowing)\r
- {\r
- subtitlesShowing = true;\r
- subtitles->show();\r
- }\r
- else\r
- {\r
- subtitlesShowing = false;\r
- subtitles->hide();\r
- }\r
- return subtitlesShowing;\r
-}\r
-\r
-\r
-void PlayerLiveTV::turnSubtitlesOn(bool ison) {\r
- if (ison)\r
- {\r
- subtitlesShowing = true;\r
- subtitles->show();\r
- }\r
- else\r
- {\r
- subtitlesShowing = false;\r
- subtitles->hide();\r
- }\r
-\r
-}\r
-// ----------------------------------- Externally called events\r
-\r
-void PlayerLiveTV::go(ULONG index)\r
-{\r
- struct PLInstruction i;\r
- i.instruction = I_SETCHANNEL;\r
- i.channelIndex = index;\r
- instructions.push(i);\r
- threadStart();\r
-}\r
-\r
-void PlayerLiveTV::setChannel(ULONG index)\r
-{\r
- logger->log("PlayerLiveTV", Log::DEBUG, "setChannel");\r
- struct PLInstruction i;\r
- i.instruction = I_SETCHANNEL;\r
- i.channelIndex = index;\r
- instructions.push(i); \r
- threadSignalNoLock();\r
-}\r
-\r
-void PlayerLiveTV::stop()\r
-{\r
- logger->log("PlayerLiveTV", Log::DEBUG, "stop");\r
- struct PLInstruction i;\r
- i.instruction = I_STOP;\r
- instructions.push(i);\r
- threadSignal();\r
- threadStop();\r
- logger->log("PlayerLiveTV", Log::DEBUG, "stop succesfull");\r
-}\r
-\r
-// ----------------------------------- Callback\r
-\r
-void PlayerLiveTV::call(void* caller)\r
-{\r
- if (caller == demuxer)\r
- {\r
- logger->log("PlayerLiveTV", Log::DEBUG, "Callback from demuxer");\r
-\r
- int dxCurrentAspect = demuxer->getAspectRatio();\r
- if (dxCurrentAspect == Demuxer::ASPECT_4_3)\r
- {\r
- if (video->getTVsize() == Video::ASPECT16X9)\r
- {\r
- logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is 4:3 aspect, switching TV");\r
- video->setAspectRatio(Video::ASPECT4X3);\r
- }\r
- else\r
- {\r
- logger->log("PlayerLiveTV", Log::DEBUG, "TV is 4:3, ignoring aspect switching");\r
- }\r
-\r
- Message* m = new Message();\r
- m->from = this;\r
- m->to = messageReceiver;\r
- m->message = Message::PLAYER_EVENT;\r
- m->parameter = PlayerLiveTV::ASPECT43;\r
- messageQueue->postMessageFromOuterSpace(m);\r
- }\r
- else if (dxCurrentAspect == Demuxer::ASPECT_16_9)\r
- {\r
- if (video->getTVsize() == Video::ASPECT16X9)\r
- {\r
- logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is 16:9 aspect, switching TV");\r
- video->setAspectRatio(Video::ASPECT16X9);\r
- }\r
- else\r
- {\r
- logger->log("PlayerLiveTV", Log::DEBUG, "TV is 4:3, ignoring aspect switching");\r
- } \r
-\r
- Message* m = new Message();\r
- m->from = this;\r
- m->to = messageReceiver;\r
- m->message = Message::PLAYER_EVENT;\r
- m->parameter = PlayerLiveTV::ASPECT169;\r
- messageQueue->postMessageFromOuterSpace(m);\r
- }\r
- else\r
- {\r
- logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer said video is something else... ignoring");\r
- }\r
- }\r
- else if (caller == &afeed)\r
- {\r
- if (state == S_VIDEOSTARTUP)\r
- {\r
- logger->log("PlayerLiveTV", Log::DEBUG, "afeed video startup");\r
- videoStartup = true;\r
- threadSignalNoLock();\r
- }\r
- }\r
-}\r
-\r
-// -----------------------------------\r
-\r
-void PlayerLiveTV::streamReceive(ULONG flag, void* data, ULONG len)\r
-{\r
- // Flag:\r
- // 0 = normal stream packet\r
- // 1 = stream end\r
- // 2 = connection lost\r
-\r
- //logger->log("PlayerLiveTV", Log::DEBUG, "Received a streamchunk from VDR, flag = %lu", flag);\r
-\r
- if (flag == 1)\r
- {\r
- if (data) abort();\r
- \r
- Message* m = new Message();\r
- m->from = this;\r
- m->to = messageReceiver;\r
- m->message = Message::PLAYER_EVENT;\r
- m->parameter = PlayerLiveTV::STREAM_END;\r
- messageQueue->postMessageFromOuterSpace(m);\r
- }\r
- \r
- if (streamChunks.size() < PLAYER_MAX_STREAMING_BUFFERS)\r
- {\r
- StreamChunk s;\r
- s.data = data;\r
- s.len = len;\r
- streamChunks.push(s);\r
- threadSignalNoLock();\r
- }\r
- else\r
- {\r
- // Too many chunks in streamChunks, drop this chunk\r
- free(data);\r
- logger->log("PlayerLiveTV", Log::WARN, "Dropped chunk");\r
- }\r
-}\r
-\r
-void PlayerLiveTV::clearStreamChunks()\r
-{\r
- while(streamChunks.size())\r
- {\r
- logger->log("PlayerLiveTV", Log::DEBUG, "Dropping chunk from old stream");\r
- struct StreamChunk s = streamChunks.front();\r
- streamChunks.pop();\r
- free(s.data);\r
- }\r
-}\r
-\r
-void PlayerLiveTV::chunkToDemuxer()\r
-{\r
- StreamChunk s = streamChunks.front();\r
- streamChunks.pop();\r
-// logger->log("PlayerLiveTV", Log::DEBUG, "About to call demuxer with %p %lu", s.data, s.len);\r
- /* int a =*/ demuxer->put((UCHAR*)s.data, s.len);\r
-// logger->log("PlayerLiveTV", Log::DEBUG, "put %i to demuxer", a);\r
- free(s.data); \r
- if (pendingAudioPlay && (demuxer->getHorizontalSize()|| !video->independentAVStartUp())) //Horizontal Size is zero, if not parsed\r
- {\r
- video->sync();\r
- video->play();\r
- video->pause();\r
- //audio->setStreamType(Audio::MPEG2_PES);\r
- audio->sync();\r
- audio->play();\r
- audio->pause();\r
- pendingAudioPlay = false;\r
- }\r
-}\r
-\r
-void PlayerLiveTV::switchState(UCHAR newState)\r
-{\r
- logger->log("PlayerLiveTV", Log::DEBUG, "Switch from state %u to state %u", state, newState);\r
-\r
- switch(state)\r
- {\r
- case S_STOP: // FROM S_STOP\r
- {\r
- switch(newState)\r
- {\r
- case S_VIDEOSTARTUP:\r
- {\r
- video->blank();\r
- video->reset();\r
- //video->sync();\r
- //video->play();\r
- //video->pause();\r
-\r
- audio->stop();\r
- audio->unPause();\r
- audio->reset();\r
- //audio->setStreamType(Audio::MPEG2_PES);\r
- //audio->sync();\r
- // I make this modification, since the video/audio devices needs to know at least\r
- // which kind of video is embedded inside the stream\r
- // therefore the demuxer needs to feeded at least with enough data\r
- // to have one video header\r
- // This is crucial, if we have mixed h264/mpeg2 channels\r
- // the information from channels is not enough since some directshow decoders need\r
- // width and height information before startup\r
- pendingAudioPlay = true;\r
-\r
- //audio->play();\r
- //audio->pause();\r
-\r
- demuxer->reset();\r
- demuxer->seek();\r
-\r
- afeed.start();\r
- vfeed.start();\r
- subtitles->start();\r
- tfeed.start();\r
- \r
- state = newState;\r
- if (!video->independentAVStartUp()){\r
- videoStartup = true;\r
- threadSignalNoLock();\r
- }\r
- return;\r
- }\r
- default:\r
- {\r
- logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);\r
- abort();\r
- break;\r
- }\r
- }\r
- }\r
-\r
- case S_VIDEOSTARTUP: // FROM S_VIDEOSTARTUP\r
- {\r
- switch(newState)\r
- {\r
- case S_PREBUFFERING:\r
- {\r
- pendingAudioPlay=false;\r
- vfeed.release();\r
- state = newState;\r
- return;\r
- }\r
- \r
- case S_VIDEOSTARTUP:\r
- {\r
-\r
- vdr->stopStreaming();\r
- clearStreamChunks(); \r
- vfeed.stop();\r
- afeed.stop();\r
- subtitles->stop();\r
- tfeed.stop();\r
- \r
- video->blank();\r
- video->reset();\r
- //video->sync();\r
- //video->play();\r
- //video->pause();\r
- audio->stop();\r
- audio->unPause();\r
- audio->reset();\r
- //audio->setStreamType(Audio::MPEG2_PES);\r
- //audio->sync();\r
- pendingAudioPlay = true;\r
- //audio->play();\r
- //audio->pause();\r
-\r
- demuxer->reset();\r
- demuxer->seek();\r
-\r
- afeed.start();\r
- vfeed.start();\r
- subtitles->start(); \r
- tfeed.start();\r
- state = newState;\r
- if (!video->independentAVStartUp()){\r
- videoStartup = true;\r
- threadSignalNoLock();\r
- }\r
- return;\r
- } \r
- case S_STOP:\r
- { \r
- vdr->stopStreaming();\r
- pendingAudioPlay=false;\r
- clearStreamChunks();\r
- vfeed.stop();\r
- afeed.stop();\r
- subtitles->stop();\r
- tfeed.stop();\r
- video->stop();\r
- video->blank();\r
- audio->stop();\r
- audio->reset();\r
- video->reset();\r
- state = newState;\r
- return;\r
- }\r
- default:\r
- {\r
- logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);\r
- abort();\r
- break;\r
- } \r
- }\r
- }\r
- \r
- case S_PREBUFFERING: // FROM S_PREBUFFERING\r
- {\r
- switch(newState)\r
- {\r
- case S_PLAY:\r
- {\r
- pendingAudioPlay=false;\r
- audio->unPause();\r
- video->unPause();\r
- state = newState;\r
- return;\r
- }\r
- case S_VIDEOSTARTUP:\r
- {\r
- vdr->stopStreaming();\r
- clearStreamChunks();\r
- vfeed.stop();\r
- afeed.stop();\r
- subtitles->stop();\r
- tfeed.stop();\r
- video->stop();\r
- video->blank();\r
- audio->stop();\r
- audio->unPause();\r
- audio->reset();\r
-\r
- video->reset();\r
- //video->sync();\r
- //video->play();\r
- //video->pause();\r
-\r
- //audio->setStreamType(Audio::MPEG2_PES);\r
- //audio->sync();\r
- pendingAudioPlay = true;\r
- //audio->play();\r
- //audio->pause();\r
-\r
- demuxer->reset();\r
- demuxer->seek();\r
-\r
- afeed.start();\r
- vfeed.start();\r
- subtitles->start();\r
- tfeed.start();\r
-\r
- state = newState;\r
- return;\r
- }\r
- case S_STOP:\r
- {\r
- pendingAudioPlay=false;\r
- vdr->stopStreaming();\r
- clearStreamChunks();\r
- vfeed.stop();\r
- afeed.stop();\r
- subtitles->stop();\r
- tfeed.stop();\r
- video->stop();\r
- video->blank();\r
- audio->stop();\r
- audio->reset();\r
- video->reset();\r
- state = newState;\r
- return; \r
- }\r
- default:\r
- {\r
- logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);\r
- abort();\r
- break;\r
- } \r
- }\r
- }\r
- \r
- case S_PLAY: // FROM S_PLAY\r
- {\r
- switch(newState)\r
- {\r
- case S_STOP:\r
- { \r
- pendingAudioPlay=false;\r
- vdr->stopStreaming();\r
- clearStreamChunks();\r
- vfeed.stop();\r
- afeed.stop();\r
- subtitles->stop();\r
- tfeed.stop();\r
- video->stop();\r
- video->blank();\r
- audio->stop();\r
- audio->reset();\r
- video->reset();\r
- state = newState;\r
- return;\r
- }\r
- case S_VIDEOSTARTUP:\r
- {\r
- vdr->stopStreaming();\r
- clearStreamChunks();\r
- vfeed.stop();\r
- afeed.stop();\r
- subtitles->stop();\r
- tfeed.stop();\r
- video->stop();\r
- video->blank();\r
- audio->stop();\r
- audio->unPause();\r
- audio->reset();\r
-\r
- video->reset();\r
- \r
- //video->sync();\r
- // video->play();\r
- //video->pause();\r
-\r
- //audio->setStreamType(Audio::MPEG2_PES);\r
- //audio->sync();\r
- //audio->play();\r
- //audio->pause();\r
- pendingAudioPlay = true;\r
- demuxer->reset();\r
- demuxer->seek();\r
-\r
- afeed.start();\r
- vfeed.start();\r
- subtitles->start();\r
- tfeed.start();\r
- state = newState;\r
- if (!video->independentAVStartUp()){\r
- videoStartup = true;\r
- threadSignalNoLock();\r
- }\r
- return;\r
- }\r
- default:\r
- {\r
- logger->log("PlayerLiveTV", Log::EMERG, "Thread called state %u to state %u which is not supported", state, newState);\r
- abort();\r
- break;\r
- } \r
- }\r
- } \r
- } \r
-}\r
-\r
-bool PlayerLiveTV::checkError()\r
-{\r
- if (!vdr->isConnected())\r
- {\r
- if (state != S_STOP) switchState(S_STOP);\r
- \r
- Message* m = new Message();\r
- m->from = this;\r
- m->to = messageReceiver;\r
- m->message = Message::PLAYER_EVENT;\r
- m->parameter = PlayerLiveTV::CONNECTION_LOST;\r
- messageQueue->postMessageFromOuterSpace(m);\r
- \r
- return true;\r
- } \r
- return false;\r
-}\r
-\r
-void PlayerLiveTV::optimizeInstructionQueue()\r
-{\r
- // Walk the list\r
- \r
- // Currently there are only 2 instruction types, so this is a bit overkill...\r
-\r
- struct PLInstruction i;\r
- while(instructions.size() > 1)\r
- {\r
- i = instructions.front();\r
- if (i.instruction == I_SETCHANNEL)\r
- {\r
- instructions.pop(); // if this is the first of more than 1 command, currently it cannot possibly be relevant\r
- }\r
- else if (i.instruction == I_STOP)\r
- {\r
- return; // return here and ensure the next instruction will be stop\r
- }\r
- }\r
-}\r
-\r
-void PlayerLiveTV::threadMethod()\r
-{\r
- while(1)\r
- {\r
-\r
- //logger->log("PlayerLiveTV", Log::DEBUG, "VS: %d pA %d",videoStartup,pendingAudioPlay);\r
- if (videoStartup && !pendingAudioPlay) // we are in S_VIDEOSTARTUP, afeed has signalled that it has written some data\r
- {\r
- logger->log("PlayerLiveTV", Log::DEBUG, "Enter prebuffering");\r
- switchState(S_PREBUFFERING);\r
- videoStartup = false;\r
- preBufferCount = 0;\r
- \r
- checkError();\r
- } \r
- \r
- while(!instructions.empty())\r
- {\r
- if (instructions.size() > 1) optimizeInstructionQueue();\r
-\r
- struct PLInstruction i = instructions.front();\r
- instructions.pop();\r
- \r
- if (i.instruction == I_SETCHANNEL)\r
- {\r
- logger->log("PlayerLiveTV", Log::DEBUG, "start new stream");\r
- \r
- \r
- switchState(S_VIDEOSTARTUP);\r
- \r
- if (!checkError())\r
- {\r
- Channel* chan = (*chanList)[i.channelIndex];\r
- chan->loadPids();\r
- h264=chan->vstreamtype==0x1b;\r
- demuxer->seth264(h264);\r
- video->seth264mode(chan->vstreamtype==0x1b);\r
- demuxer->setVID(chan->vpid);\r
- video->seth264mode(chan->vstreamtype==0x1b);\r
-\r
- bool found=false;\r
-\r
- if (chan->numAPids > 0) \r
- {\r
- int j=0;\r
- while (j<chan->numAPids && !found) {\r
- if (Audio::getInstance()->streamTypeSupported(chan->apids[j].type)) {\r
- demuxer->setAID(chan->apids[j].pid,0,chan->apids[j].type);\r
- audio->setStreamType(Audio::MPEG2_PES);\r
- logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer pids: %u %u %u", chan->vpid, chan->apids[j].pid,chan->apids[j].type);\r
- found=true;\r
- }\r
- j++;\r
- }\r
- }\r
-\r
- if (!found)\r
- {\r
- if (chan->numDPids > 0 && audio->maysupportAc3()) \r
- {\r
- int j=0;\r
- while (j<chan->numDPids && !found) {\r
- if (Audio::getInstance()->streamTypeSupported(chan->dpids[j].type)) {\r
- demuxer->setAID(chan->dpids[j].pid,1,chan->dpids[j].type);\r
- audio->setStreamType(Audio::MPEG2_PES);\r
- logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer pids: %u %u (ac3) %u", chan->vpid, chan->dpids[j].pid,chan->dpids[j].type);\r
- found=true;\r
- }\r
- j++;\r
- }\r
- } \r
- else\r
- {\r
- logger->log("PlayerLiveTV", Log::WARN, "Demuxer video pid only: %u", chan->vpid);\r
- }\r
- }\r
- if (chan->numSPids > 0)\r
- demuxer->setSubID(chan->spids[0].pid);\r
- demuxer->setTID(chan->tpid);\r
- teletext->ResetDecoder();\r
- int streamSuccess = vdr->streamChannel(chan->number, this);\r
- if (!checkError() && !streamSuccess)\r
- { \r
- Message* m = new Message();\r
- m->from = this;\r
- m->to = messageReceiver;\r
- m->message = Message::PLAYER_EVENT;\r
- m->parameter = PlayerLiveTV::STREAM_END;\r
- messageQueue->postMessageFromOuterSpace(m);\r
- }\r
- }\r
- }\r
- else if (i.instruction == I_STOP)\r
- {\r
- logger->log("PlayerLiveTV", Log::DEBUG, "Stopping");\r
- switchState(S_STOP);\r
- checkError();\r
-\r
- stopNow = true;\r
- break;\r
- }\r
- }\r
-\r
- threadCheckExit();\r
-\r
- if (stopNow) break;\r
-\r
- while(streamChunks.size())\r
- {\r
- //logger->log("PlayerLiveTV", Log::DEBUG, "chunk mark1 %d", streamChunks.size());\r
- chunkToDemuxer();\r
- //logger->log("PlayerLiveTV", Log::DEBUG, "chunk mark2 %d", streamChunks.size());\r
-\r
- if (state == S_PREBUFFERING)\r
- {\r
- // logger->log("PlayerLiveTV", Log::DEBUG, "chunk mark3");\r
- ++preBufferCount;\r
- ULONG percentDone = (ULONG)(preBufferCount / (float)preBufferAmount * 100);\r
- logger->log("PlayerLiveTV", Log::DEBUG, "Prebuffering %lu%%", percentDone);\r
- \r
- Message* m = new Message();\r
- m->from = this;\r
- m->to = messageReceiver;\r
- m->message = Message::PLAYER_EVENT;\r
- m->parameter = PlayerLiveTV::PREBUFFERING;\r
- m->tag = percentDone;\r
- messageQueue->postMessageFromOuterSpace(m);\r
-\r
- if (preBufferCount == preBufferAmount)\r
- {\r
- switchState(S_PLAY);\r
- checkError();\r
- }\r
- }\r
- }\r
- // logger->log("PlayerLiveTV", Log::DEBUG, "wait for signal %d", streamChunks.size());\r
- threadLock();\r
- threadWaitForSignal(); // unlocks and waits for signal\r
- threadUnlock();\r
- //logger->log("PlayerLiveTV", Log::DEBUG, "wait for signal2 %d",streamChunks.size());\r
- }\r
-\r
- logger->log("PlayerLiveTV", Log::DEBUG, "End of thread");\r
-}\r
-\r
+/*
+ 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;
+ unsigned int demux_audio_size=524288;
+ if (video->supportsh264()) {
+ demux_video_size*=5*1;//5;
+
+ }
+ if (audio->maysupportAc3()) {
+ //demux_audio_size*=2;
+ }
+
+ int text_fak=video->getTeletextBufferFaktor();
+
+
+ if (!demuxer->init(this, audio, video, teletext, demux_video_size,demux_audio_size, 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,int streamtype)
+{
+ demuxer->setAID(newChannel,type,streamtype);
+}
+
+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() < PLAYER_MAX_STREAMING_BUFFERS)
+ {
+ 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);
+
+ bool found=false;
+
+ if (chan->numAPids > 0)
+ {
+ int j=0;
+ while (j<chan->numAPids && !found) {
+ if (Audio::getInstance()->streamTypeSupported(chan->apids[j].type)) {
+ demuxer->setAID(chan->apids[j].pid,0,chan->apids[j].type);
+ audio->setStreamType(Audio::MPEG2_PES);
+ logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer pids: %u %u %u", chan->vpid, chan->apids[j].pid,chan->apids[j].type);
+ found=true;
+ }
+ j++;
+ }
+ }
+
+ if (!found)
+ {
+ if (chan->numDPids > 0 && audio->maysupportAc3())
+ {
+ int j=0;
+ while (j<chan->numDPids && !found) {
+ if (Audio::getInstance()->streamTypeSupported(chan->dpids[j].type)) {
+ demuxer->setAID(chan->dpids[j].pid,1,chan->dpids[j].type);
+ audio->setStreamType(Audio::MPEG2_PES);
+ logger->log("PlayerLiveTV", Log::DEBUG, "Demuxer pids: %u %u (ac3) %u", chan->vpid, chan->dpids[j].pid,chan->dpids[j].type);
+ found=true;
+ }
+ j++;
+ }
+ }
+ 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())
+ {
+ //logger->log("PlayerLiveTV", Log::DEBUG, "chunk mark1 %d", streamChunks.size());
+ chunkToDemuxer();
+ //logger->log("PlayerLiveTV", Log::DEBUG, "chunk mark2 %d", streamChunks.size());
+
+ if (state == S_PREBUFFERING)
+ {
+ // logger->log("PlayerLiveTV", Log::DEBUG, "chunk mark3");
+ ++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();
+ }
+ }
+ }
+ // logger->log("PlayerLiveTV", Log::DEBUG, "wait for signal %d", streamChunks.size());
+ threadLock();
+ threadWaitForSignal(); // unlocks and waits for signal
+ threadUnlock();
+ //logger->log("PlayerLiveTV", Log::DEBUG, "wait for signal2 %d",streamChunks.size());
+ }
+
+ logger->log("PlayerLiveTV", Log::DEBUG, "End of thread");
+}
+
-/*\r
- Copyright 2004-2006 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#include "playerradio.h"\r
-\r
-#include "log.h"\r
-#include "audio.h"\r
-#include "video.h"\r
-#include "demuxervdr.h"\r
-#include "demuxerts.h"\r
-#include "remote.h"\r
-#include "vdr.h"\r
-#include "message.h"\r
-#include "messagequeue.h"\r
-\r
-// ----------------------------------- Called from outside, one offs or info funcs\r
-\r
-PlayerRadio::PlayerRadio(MessageQueue* tmessageQueue, void* tmessageReceiver)\r
-: afeed(this)\r
-{\r
- messageQueue = tmessageQueue;\r
- messageReceiver = tmessageReceiver;\r
- audio = Audio::getInstance();\r
- logger = Log::getInstance();\r
- vdr = VDR::getInstance();\r
- initted = false;\r
- lengthBytes = 0;\r
- lengthPackets = 0;\r
- streamPos = 0;\r
- state = S_STOP;\r
-\r
- startPTS = 0;\r
- lengthSeconds = 0;\r
-\r
- threadBuffer = NULL;\r
-\r
- blockSize = 10000;\r
- startupBlockSize = 20000;\r
-\r
- Video::getInstance()->turnVideoOff();\r
-}\r
-\r
-PlayerRadio::~PlayerRadio()\r
-{\r
- if (initted) shutdown();\r
-}\r
-\r
-int PlayerRadio::init(ULLONG tlengthBytes, ULONG tlengthPackets, bool isPesRecording)\r
-{\r
- if (initted) return 0;\r
-#ifndef WIN32\r
- pthread_mutex_init(&mutex, NULL);\r
-#else\r
- mutex=CreateMutex(NULL,FALSE,NULL);\r
-#endif\r
-\r
- if (isPesRecording)\r
- demuxer = new DemuxerVDR();\r
- else\r
- demuxer = new DemuxerTS();\r
- if (!demuxer) return 0;\r
-\r
- if (!demuxer->init(this, audio, NULL, NULL, 0, 40000, 0))\r
- {\r
- logger->log("PlayerRadio", Log::ERR, "Demuxer failed to init");\r
- shutdown();\r
- return 0;\r
- }\r
-\r
- afeed.init();\r
- audio->stop();\r
-\r
- lengthBytes = tlengthBytes;\r
- lengthPackets = tlengthPackets;\r
-\r
- logger->log("PlayerRadio", Log::DEBUG, "PlayerRadio has received length bytes of %llu", lengthBytes);\r
-\r
- UINT thisRead = 0;\r
- int success;\r
-\r
- UCHAR* buffer = vdr->getBlock(0, 10000, &thisRead);\r
- if (!buffer)\r
- {\r
- logger->log("PlayerRadio", Log::ERR, "Failed to get start block");\r
- shutdown();\r
- if (!vdr->isConnected()) doConnectionLost();\r
- return 0;\r
- }\r
-\r
- success = demuxer->findPTS(buffer, thisRead, &startPTS);\r
- if (!success)\r
- {\r
- logger->log("PlayerRadio", Log::ERR, "Failed to get start PTS");\r
- free(buffer);\r
- shutdown();\r
- return 0;\r
- }\r
-\r
- free(buffer);\r
-\r
- if (!setLengthSeconds())\r
- {\r
- logger->log("PlayerRadio", Log::ERR, "Failed to setLengthSeconds");\r
- shutdown();\r
- return 0;\r
- }\r
-\r
- initted = true;\r
- return 1;\r
-}\r
-\r
-bool PlayerRadio::setLengthSeconds()\r
-{\r
- int success;\r
- ULLONG endPTS = 0;\r
- UINT thisRead = 0;\r
- UCHAR* buffer = vdr->getBlock(lengthBytes - 10000, 10000, &thisRead);\r
- if (!buffer)\r
- {\r
- logger->log("PlayerRadio", Log::ERR, "Failed to get end block");\r
- if (!vdr->isConnected()) doConnectionLost(); \r
- return false;\r
- }\r
-\r
- success = demuxer->findPTS(buffer, thisRead, &endPTS);\r
- free(buffer);\r
- if (!success)\r
- {\r
- logger->log("PlayerRadio", Log::ERR, "Failed to get end PTS");\r
- return false;\r
- }\r
-\r
- if (startPTS < endPTS)\r
- {\r
- lengthSeconds = (endPTS - startPTS) / 90000;\r
- }\r
- else\r
- {\r
- lengthSeconds = (startPTS - endPTS) / 90000;\r
- }\r
-\r
- return true;\r
-}\r
-\r
-int PlayerRadio::shutdown()\r
-{\r
- if (!initted) return 0;\r
- switchState(S_STOP);\r
- initted = false;\r
-\r
- delete demuxer;\r
- demuxer = NULL;\r
-\r
-#ifdef WIN32\r
- CloseHandle(mutex);\r
-#endif\r
-\r
- return 1;\r
-}\r
-\r
-\r
-void PlayerRadio::setStartBytes(ULLONG startBytes)\r
-{\r
- streamPos = startBytes;\r
-}\r
-\r
-ULONG PlayerRadio::getLengthSeconds()\r
-{\r
- return lengthSeconds;\r
-}\r
-\r
-ULONG PlayerRadio::getCurrentSeconds()\r
-{\r
- if (startup) return 0;\r
-\r
- long long currentPTS = demuxer->getAudioPTS();\r
- currentPTS -= startPTS;\r
- if (currentPTS < 0) currentPTS += 8589934592ULL;\r
- ULONG ret = currentPTS / 90000;\r
- return ret;\r
-}\r
-\r
-// ----------------------------------- Externally called events\r
-\r
-void PlayerRadio::play()\r
-{\r
- if (!initted) return;\r
- if (state == S_PLAY) return;\r
- lock();\r
- switchState(S_PLAY);\r
- unLock();\r
-}\r
-\r
-\r
-void PlayerRadio::playpause()\r
-{\r
- if (!initted) return;\r
- lock();\r
- if (state==S_PLAY) {\r
- switchState(S_PAUSE_P);\r
- } else {\r
- switchState(S_PLAY);\r
- }\r
- unLock();\r
-}\r
-\r
-void PlayerRadio::stop()\r
-{\r
- if (!initted) return;\r
- if (state == S_STOP) return;\r
- lock();\r
- logger->log("PlayerRadio", Log::DEBUG, "Stop called lock");\r
- switchState(S_STOP);\r
- unLock();\r
-}\r
-\r
-void PlayerRadio::pause()\r
-{\r
- if (!initted) return;\r
- lock();\r
-\r
- if (state == S_PAUSE_P)\r
- {\r
- switchState(S_PLAY);\r
- }\r
- else\r
- {\r
- switchState(S_PAUSE_P);\r
- }\r
-\r
- unLock();\r
-}\r
-\r
-void PlayerRadio::jumpToPercent(double percent)\r
-{\r
- lock();\r
- logger->log("PlayerRadio", Log::DEBUG, "JUMP TO %i%%", percent);\r
- ULONG newPacket = (ULONG)(percent * lengthPackets / 100);\r
- switchState(S_JUMP, newPacket);\r
- unLock();\r
-}\r
-\r
-void PlayerRadio::skipForward(UINT seconds)\r
-{\r
- lock();\r
- logger->log("PlayerRadio", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds);\r
- ULONG currentSeconds = getCurrentSeconds();\r
- ULONG currentPacket = demuxer->getPacketNum();\r
-\r
- if (currentSeconds == 0) { unLock(); return; } // div by zero\r
- if (currentPacket == 0) { unLock(); return; } // Current pos from demuxer is not valid\r
-\r
- ULONG newPacket = currentPacket + (currentPacket * seconds / currentSeconds);\r
- if (newPacket > lengthPackets) { switchState(S_PLAY); unLock(); }\r
- else switchState(S_JUMP, newPacket);\r
- unLock();\r
-}\r
-\r
-void PlayerRadio::skipBackward(UINT seconds)\r
-{\r
- lock();\r
- logger->log("PlayerRadio", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds);\r
-\r
- ULONG currentSeconds = getCurrentSeconds();\r
- ULONG currentPacket = demuxer->getPacketNum();\r
-\r
- if (currentSeconds == 0) { unLock(); return; } // div by zero\r
- if (currentPacket == 0) { unLock(); return; } // Current pos from demuxer is not valid\r
-\r
- ULONG newPacket;\r
- if ((UINT)seconds > currentSeconds)\r
- newPacket = 0;\r
- else\r
- newPacket = currentPacket - (currentPacket * seconds / currentSeconds);\r
-\r
- switchState(S_JUMP, newPacket);\r
- unLock();\r
-}\r
-\r
-// ----------------------------------- Implementations called events\r
-\r
-void PlayerRadio::switchState(UCHAR toState, ULONG jumpPacket)\r
-{\r
- if (!initted) return;\r
-\r
- logger->log("PlayerRadio", Log::DEBUG, "Switch state from %u to %u", state, toState);\r
-\r
- switch(state) // current state selector\r
- {\r
- case S_PLAY: // from S_PLAY -----------------------------------\r
- {\r
- switch(toState)\r
- {\r
- case S_PLAY: // to S_PLAY\r
- {\r
- return;\r
- }\r
- case S_PAUSE_P: // to S_PAUSE_P\r
- {\r
- audio->pause();\r
- state = S_PAUSE_P;\r
- return;\r
- }\r
- case S_STOP: // to S_STOP\r
- {\r
- afeed.stop();\r
- threadStop();\r
- audio->stop();\r
- audio->unPause();\r
- demuxer->reset();\r
- state = S_STOP;\r
- return;\r
- }\r
- case S_JUMP: // to S_JUMP\r
- {\r
- restartAtPacket(jumpPacket);\r
- return;\r
- }\r
- }\r
- }\r
- case S_PAUSE_P: // from S_PAUSE_P -----------------------------------\r
- {\r
- switch(toState)\r
- {\r
- case S_PLAY: // to S_PLAY\r
- {\r
- audio->unPause();\r
- state = S_PLAY;\r
- return;\r
- }\r
- case S_PAUSE_P: // to S_PAUSE_P\r
- {\r
- return;\r
- }\r
- case S_STOP: // to S_STOP\r
- {\r
- afeed.stop();\r
- threadStop();\r
- audio->stop();\r
- audio->unPause();\r
- demuxer->reset();\r
- audio->systemMuteOff();\r
- state = S_STOP;\r
- return;\r
- }\r
- case S_JUMP: // to S_JUMP\r
- {\r
- state = S_PLAY;\r
- audio->unPause();\r
- restartAtPacket(jumpPacket);\r
- return;\r
- }\r
- }\r
- }\r
- case S_STOP: // from S_STOP -----------------------------------\r
- {\r
- switch(toState)\r
- {\r
- case S_PLAY: // to S_PLAY\r
- {\r
- startup = true;\r
-\r
- audio->reset();\r
- audio->setStreamType(Audio::MPEG2_PES);\r
- audio->systemMuteOff();\r
- demuxer->reset();\r
-\r
- // FIXME use restartAtPacket here?\r
- if (currentPacketNumber > lengthPackets) currentPacketNumber = 0;\r
- demuxer->setPacketNum(currentPacketNumber);\r
- state = S_PLAY;\r
- threadStart();\r
- logger->log("PlayerRadio", Log::DEBUG, "Immediate play");\r
- afeed.start();\r
- audio->play();\r
-\r
- return;\r
- }\r
- case S_PAUSE_P: // to S_PAUSE_P\r
- {\r
- return;\r
- }\r
- case S_STOP: // to S_STOP\r
- {\r
- return;\r
- }\r
- case S_JUMP: // to S_JUMP\r
- {\r
- return;\r
- }\r
- }\r
- }\r
- // case S_JUMP cannot be selected as a start state because it auto flips to play\r
- }\r
-}\r
-\r
-// ----------------------------------- Internal functions\r
-\r
-void PlayerRadio::lock()\r
-{\r
-#ifndef WIN32\r
- pthread_mutex_lock(&mutex);\r
- logger->log("PlayerRadio", Log::DEBUG, "LOCKED");\r
-\r
-#else\r
- WaitForSingleObject(mutex, INFINITE);\r
-#endif\r
-}\r
-\r
-void PlayerRadio::unLock()\r
-{\r
-#ifndef WIN32\r
- logger->log("PlayerRadio", Log::DEBUG, "UNLOCKING");\r
- pthread_mutex_unlock(&mutex);\r
-#else\r
- ReleaseMutex(mutex);\r
-#endif\r
-}\r
-\r
-void PlayerRadio::restartAtPacket(ULONG newPacket)\r
-{\r
- afeed.stop();\r
- threadStop();\r
- audio->reset();\r
- audio->setStreamType(Audio::MPEG2_PES);\r
- demuxer->flush();\r
- currentPacketNumber = newPacket;\r
- demuxer->setPacketNum(newPacket);\r
- afeed.start();\r
- threadStart();\r
- audio->play();\r
- audio->systemMuteOff();\r
- audio->doMuting();\r
-}\r
-\r
-void PlayerRadio::doConnectionLost()\r
-{\r
- logger->log("PlayerRadio", Log::DEBUG, "Connection lost, sending message");\r
- Message* m = new Message();\r
- m->to = messageReceiver;\r
- m->from = this;\r
- m->message = Message::PLAYER_EVENT;\r
- m->parameter = PlayerRadio::CONNECTION_LOST;\r
- messageQueue->postMessage(m);\r
-}\r
-\r
-// ----------------------------------- Callback\r
-\r
-void PlayerRadio::call(void* caller)\r
-{\r
- threadSignalNoLock();\r
-}\r
-\r
-// ----------------------------------- Feed thread\r
-\r
-void PlayerRadio::threadMethod()\r
-{\r
- if (state == S_PLAY) threadFeedPlay();\r
-}\r
-\r
-void PlayerRadio::threadFeedPlay()\r
-{\r
- ULLONG feedPosition;\r
- UINT thisRead, writeLength, thisWrite, askFor;\r
- time_t lastRescan = time(NULL);\r
-\r
- feedPosition = vdr->positionFromFrameNumber(currentPacketNumber);\r
- if (!vdr->isConnected()) { doConnectionLost(); return; }\r
- logger->log("PlayerRadio", Log::DEBUG, "startFeedPlay: wantedPacket %i goto %llu", currentPacketNumber, feedPosition);\r
-\r
-\r
- while(1)\r
- {\r
- thisRead = 0;\r
- writeLength = 0;\r
- thisWrite = 0;\r
-\r
- threadCheckExit();\r
-\r
- // If we havn't rescanned for a while..\r
- if ((lastRescan + 60) < time(NULL))\r
- {\r
- lengthBytes = vdr->rescanRecording(&lengthPackets);\r
- if (!vdr->isConnected()) { doConnectionLost(); return; }\r
- logger->log("PlayerRadio", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes);\r
- lastRescan = time(NULL);\r
-\r
- if (!setLengthSeconds())\r
- {\r
- logger->log("PlayerRadio", Log::ERR, "Failed to setLengthSeconds in thread");\r
- return;\r
- }\r
- }\r
-\r
- if (feedPosition >= lengthBytes) break; // finished playback\r
-\r
- if (startup)\r
- {\r
- if (startupBlockSize > lengthBytes)\r
- askFor = lengthBytes; // is a very small recording!\r
- else\r
- askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams\r
- }\r
- else\r
- {\r
- if ((feedPosition + blockSize) > lengthBytes) // last block of recording\r
- askFor = lengthBytes - feedPosition;\r
- else // normal\r
- askFor = blockSize;\r
- }\r
-\r
- threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead);\r
- feedPosition += thisRead;\r
-\r
- if (!vdr->isConnected())\r
- {\r
- doConnectionLost();\r
- return;\r
- }\r
-\r
- if (!threadBuffer) break;\r
-\r
- if (startup)\r
- {\r
- int a_stream = demuxer->scan(threadBuffer, thisRead);\r
- demuxer->setAudioStream(a_stream);\r
- logger->log("PlayerRadio", Log::DEBUG, "Startup Audio stream chosen %x", a_stream);\r
- startup = false;\r
- }\r
-\r
- threadCheckExit();\r
-\r
- while(writeLength < thisRead)\r
- {\r
- thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength);\r
- writeLength += thisWrite;\r
-\r
- if (!thisWrite)\r
- {\r
- // demuxer is full and can't take anymore\r
- threadLock();\r
- threadWaitForSignal();\r
- threadUnlock();\r
- }\r
-\r
- threadCheckExit();\r
- }\r
-\r
- free(threadBuffer);\r
- threadBuffer = NULL;\r
-\r
- }\r
-\r
- // end of recording\r
- logger->log("PlayerRadio", Log::DEBUG, "Recording playback ends");\r
-\r
- threadCheckExit();\r
-\r
-\r
- Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex\r
- m->to = messageReceiver;\r
- m->from = this;\r
- m->message = Message::PLAYER_EVENT;\r
- m->parameter = PlayerRadio::STOP_PLAYBACK;\r
- logger->log("PlayerRadio", Log::DEBUG, "Posting message to %p...", messageQueue);\r
- messageQueue->postMessage(m);\r
-}\r
-\r
-void PlayerRadio::threadPostStopCleanup()\r
-{\r
- if (threadBuffer)\r
- {\r
- free(threadBuffer);\r
- threadBuffer = NULL;\r
- }\r
-}\r
-\r
+/*
+ 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;
+ }
+}
+
-/*\r
- Copyright 2004-2006 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef PLAYERRADIO_H\r
-#define PLAYERRADIO_H\r
-\r
-#include <stdio.h>\r
-#include <stdlib.h>\r
-#ifndef WIN32\r
-#include <sys/time.h>\r
-#endif\r
-#include <time.h>\r
-\r
-#include "threadsystem.h"\r
-\r
-#include "callback.h"\r
-#include "defines.h"\r
-#include "afeed.h"\r
-\r
-class Log;\r
-class Audio;\r
-class Video;\r
-class Demuxer;\r
-class VDR;\r
-class MessageQueue;\r
-\r
-class PlayerRadio : public Thread_TYPE, public Callback\r
-{\r
- public:\r
- PlayerRadio(MessageQueue* messageQueue, void* messageReceiver);\r
- virtual ~PlayerRadio();\r
-\r
- int init(ULLONG lengthBytes, ULONG lengthPackets, bool IsPesRecording);\r
- int shutdown();\r
- void setStartBytes(ULLONG startBytes);\r
-\r
- void play();\r
- void stop();\r
- void pause();\r
- void playpause();\r
- void jumpToPercent(double percent);\r
- void skipForward(UINT seconds);\r
- void skipBackward(UINT seconds);\r
-\r
- UCHAR getState() { return state; }\r
- ULONG getCurrentSeconds();\r
- ULONG getLengthSeconds();\r
-\r
- void call(void*); // for callback interface\r
-\r
- const static UCHAR S_PLAY = 1;\r
- const static UCHAR S_PAUSE_P = 2;\r
- const static UCHAR S_STOP = 6;\r
- const static UCHAR S_JUMP = 7;\r
-\r
- // Player events\r
-\r
- const static UCHAR CONNECTION_LOST = 1;\r
- const static UCHAR STOP_PLAYBACK = 2;\r
- const static UCHAR STREAM_END = 3;\r
-\r
- protected:\r
- void threadMethod();\r
- void threadPostStopCleanup();\r
-\r
- private:\r
- void switchState(UCHAR newState, ULONG jumpPacket=0);\r
-\r
- void threadFeedPlay();\r
- void threadFeedScan();\r
-\r
- void doConnectionLost();\r
- void restartAtPacket(ULONG newPacket);\r
- bool setLengthSeconds();\r
-\r
- MessageQueue* messageQueue;\r
- void* messageReceiver;\r
- Log* logger;\r
- Audio* audio;\r
- Demuxer* demuxer;\r
- VDR* vdr;\r
- AFeed afeed;\r
-\r
- bool initted;\r
- bool startup;\r
-\r
- ULLONG startPTS;\r
- ULONG lengthSeconds;\r
-\r
-#ifndef WIN32\r
- pthread_mutex_t mutex;\r
-#else\r
- HANDLE mutex;\r
-#endif\r
- void lock();\r
- void unLock();\r
-\r
- ULLONG lengthBytes;\r
- ULLONG streamPos;\r
- ULONG lengthPackets;\r
- ULONG currentPacketNumber;\r
- UINT blockSize;\r
- UINT startupBlockSize;\r
- UCHAR* threadBuffer;\r
- UCHAR state;\r
-};\r
-\r
-#endif\r
-\r
-\r
-/*\r
-\r
-Possible states:\r
-\r
-Play, Pause, FFwd, FBwd, (Stop), [Jump]\r
-\r
- Possible Working\r
-\r
-Play -> PauseP * *\r
- -> Stop * *\r
- -> Jump * *\r
-\r
-PauseP -> Play * *\r
- -> Stop * *\r
- -> Jump * *\r
-\r
-PauseI -> Play * *\r
- -> PauseP\r
- -> Stop * *\r
- -> Jump * *\r
-\r
-Stop -> Play * *\r
- -> PauseP\r
- -> Jump\r
-\r
-Jump -> Play\r
- -> PauseP\r
- -> Stop\r
-\r
-*/\r
+/*
+ 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 <stdio.h>
+#include <stdlib.h>
+#ifndef WIN32
+#include <sys/time.h>
+#endif
+#include <time.h>
+
+#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
+
+*/
-/*\r
- Copyright 2005-2006 Mark Calderbank\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#include "stream.h"\r
-#include "log.h"\r
-\r
-Stream::Stream()\r
-{\r
- initted = 0;\r
- draintarget = NULL;\r
- cur_packet_pos = 0;\r
-}\r
-\r
-Stream::~Stream()\r
-{\r
- shutdown();\r
-}\r
-\r
-void Stream::shutdown()\r
-{\r
- if (initted)\r
- {\r
- free(outbuf);\r
-#ifdef WIN32\r
- CloseHandle(mutex);\r
-#endif\r
- \r
- }\r
- initted = 0;\r
-}\r
-\r
-int Stream::init(DrainTarget* tdt, int bufsize)\r
-{\r
- outbuf = (UCHAR*) malloc(bufsize);\r
- if (!outbuf) return 0;\r
- draintarget = tdt;\r
- bufferSize = bufsize;\r
- initted = 1;\r
-#ifndef WIN32\r
- pthread_mutex_init(&mutex, NULL);\r
-#else\r
- mutex=CreateMutex(NULL,FALSE,NULL);\r
-#endif\r
- return 1;\r
-}\r
-\r
-void Stream::flush()\r
-{\r
- lock();\r
-\r
- mediapackets.clear();\r
- unLock();\r
- if (draintarget) draintarget->ResetTimeOffsets();\r
-}\r
-\r
-int Stream::put(const UCHAR* inbuf, int len, UCHAR type,unsigned int index)\r
-{\r
- int ret = 0;\r
- if (!draintarget) return 0;\r
- MediaPacket newPacket;\r
- newPacket.length = len;\r
- newPacket.pos_buffer = 0;\r
- newPacket.type = type;\r
- newPacket.pts=0;\r
- newPacket.dts=0;\r
- newPacket.synched=false;\r
- newPacket.index=index;\r
-#ifndef VOMP_PLATTFORM_MVP\r
- newPacket.disconti=false;\r
- newPacket.presentation_time=0;\r
-#endif\r
- if (type!=MPTYPE_MPEG_AUDIO_LAYER3) {//no PES\r
- //Extract the pts...\r
- bool hasdts=false;\r
- if ((inbuf[7] & 0x80) && len>14 ) {\r
- newPacket.synched=true;\r
- newPacket.pts=((ULLONG)(inbuf[9] & 0x0E) << 29 ) |\r
- ( (ULLONG)(inbuf[10]) << 22 ) |\r
- ( (ULLONG)(inbuf[11] & 0xFE) << 14 ) |\r
- ( (ULLONG)(inbuf[12]) << 7 ) |\r
- ( (ULLONG)(inbuf[13] & 0xFE) >> 1 );\r
- if ((inbuf[7] & 0x40) && len>19) {\r
- newPacket.dts=((ULLONG)(inbuf[14] & 0x0E) << 29 ) |\r
- ( (ULLONG)(inbuf[15]) << 22 ) |\r
- ( (ULLONG)(inbuf[16] & 0xFE) << 14 ) |\r
- ( (ULLONG)(inbuf[17]) << 7 ) |\r
- ( (ULLONG)(inbuf[18] & 0xFE) >> 1 );\r
- hasdts=true;\r
- }\r
-#ifndef VOMP_PLATTFORM_MVP\r
- //ok we have the pts now convert it to a continously time code in 100ns units\r
- if (hasdts && draintarget->dtsTimefix()) newPacket.presentation_time=(ULLONG)(newPacket.dts*10000LL/90LL);\r
- else newPacket.presentation_time=(ULLONG)(newPacket.pts*10000LL/90LL);\r
-\r
- //newPacket.presentation_time-=draintarget->SetStartOffset((ULLONG)(newPacket.pts*10000LL/90LL),&newPacket.disconti);\r
- newPacket.presentation_time-=draintarget->SetStartOffset((ULLONG)(newPacket.pts*10000LL/90LL),&newPacket.disconti);\r
-#endif\r
- }\r
- }\r
-\r
- lock();\r
- int front, back;\r
- if (mediapackets.empty())\r
- {\r
- back = 0; front = bufferSize;\r
- }\r
- else\r
- {\r
- front = mediapackets.front().pos_buffer;\r
- back = mediapackets.back().pos_buffer + mediapackets.back().length;\r
- if (back == bufferSize) back = 0;\r
- }\r
- unLock();\r
-\r
- if (back <= front)\r
- {\r
- // The free space (if any) is in one continuous chunk.\r
- if (len <= front - back) ret = len; // Is there enough of it?\r
- }\r
- else if (len <= bufferSize - back)\r
- {\r
- // There is enough space at the end of the buffer\r
- ret = len;\r
- }\r
- else if (len <= front)\r
- {\r
- // There is enough space at the start of the buffer\r
- back = 0;\r
- ret = len;\r
- }\r
-\r
- if (ret) // Nonzero if we managed to find room for the packet\r
- {\r
- memcpy(outbuf + back, inbuf, len);\r
- newPacket.pos_buffer = back;\r
- lock();\r
- mediapackets.push_back(newPacket);\r
- unLock();\r
- } else {\r
- // Log::getInstance()->log("Stream", Log::DEBUG, "We are full %d!",bufferSize);\r
- }\r
-\r
- return ret;\r
-}\r
-\r
-bool Stream::drain(bool * dataavail)\r
-{\r
- bool ret = false;\r
- if (dataavail) *dataavail=false;\r
- lock();\r
- UINT listlength = mediapackets.size();\r
- if (listlength != 0)\r
- {\r
- draintarget->PrepareMediaSample(mediapackets, cur_packet_pos);\r
- unLock();\r
- if (dataavail && draintarget->DrainTargetReady()) *dataavail=true;\r
- UINT consumed = draintarget->DeliverMediaSample(outbuf, &cur_packet_pos);\r
- lock();\r
- if (consumed != 0) ret = true;\r
- if (consumed > listlength) consumed = listlength;\r
- while (consumed--) \r
- {\r
- mediapackets.pop_front();\r
- }\r
- }\r
- unLock();\r
- return ret;\r
-}\r
-\r
-void Stream::lock()\r
-{\r
-#ifndef WIN32\r
- pthread_mutex_lock(&mutex);\r
- //logger->log("Player", Log::DEBUG, "LOCKED");\r
-\r
-#else\r
- WaitForSingleObject(mutex, INFINITE );\r
-#endif\r
-}\r
-\r
-void Stream::unLock()\r
-{\r
-#ifndef WIN32\r
- //logger->log("Player", Log::DEBUG, "UNLOCKING");\r
- pthread_mutex_unlock(&mutex);\r
-#else\r
- ReleaseMutex(mutex);\r
-#endif\r
-}\r
+/*
+ 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;
+#ifndef VOMP_PLATTFORM_MVP
+ 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;
+ }
+#ifndef VOMP_PLATTFORM_MVP
+ //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 * dataavail)
+{
+ bool ret = false;
+ if (dataavail) *dataavail=false;
+ lock();
+ UINT listlength = mediapackets.size();
+ if (listlength != 0)
+ {
+ draintarget->PrepareMediaSample(mediapackets, cur_packet_pos);
+ unLock();
+ if (dataavail && draintarget->DrainTargetReady()) *dataavail=true;
+ 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
+}
-/*\r
- Copyright 2004-2005 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#include "surface.h"\r
-\r
-#include <math.h>\r
-#include "osd.h"\r
-#include "log.h"\r
-#include "video.h"\r
-\r
-#include "teletxt/txtfont.h"\r
-\r
-unsigned int interpol_table_fac1[16][22];\r
-unsigned int interpol_table_fac2[16][22];\r
-unsigned int interpol_table_fac3[16][22];\r
-unsigned int interpol_table_fac4[16][22];\r
-int interpol_lowbit[16];\r
-int interpol_upbit[16];\r
-int interpol_lowline[22];\r
-int interpol_upline[22];\r
-bool pol_table_inited=false;\r
-\r
-void initpol_tables(){\r
- int charsizex;\r
- int charsizey;\r
- charsizex=16;\r
- if (Video::getInstance()->getFormat() == Video::PAL)\r
- {\r
- charsizey=22;\r
- } else {\r
- charsizey=18;\r
- }\r
- int ttcharsizex=12;\r
- int ttcharsizey=10;\r
- for (int py=0;py<charsizey;py++) {\r
- float fposy=((float)(ttcharsizey))/((float)(charsizey))*((float)py);\r
- float yweight=fposy-floor(fposy);\r
- float yinvweight=1.-yweight;\r
- interpol_upline[py]=min((unsigned int)ceil(fposy),9);\r
- interpol_lowline[py]=max((unsigned int)floor(fposy),0);\r
- for (int px=0;px<charsizex;px++) {\r
- float fposx=((float)(ttcharsizex))/((float)(charsizex))*((float)px);\r
- float xweight=fposx-floor(fposx);\r
- float xinvweight=1.-xweight;\r
- interpol_upbit[px]= (min((unsigned int)ceil(fposx),11));\r
- interpol_lowbit[px]= (max((unsigned int)floor(fposx),0));\r
-\r
- interpol_table_fac1[px][py]=xweight*yweight*256.;\r
- interpol_table_fac2[px][py]=xinvweight*yweight*256.;\r
- interpol_table_fac3[px][py]=xweight*yinvweight*256.;\r
- interpol_table_fac4[px][py]=xinvweight*yinvweight*256.;\r
-\r
- }\r
- }\r
-}\r
-\r
-\r
-Surface* Surface::screen = NULL;\r
-osd_font_t* Surface::font = &font_helvB18;\r
-\r
-Surface::Surface(int id)\r
-{\r
- if (id == SCREEN) screen = this;\r
-}\r
-\r
-Surface::~Surface()\r
-{\r
-}\r
-\r
-Surface* Surface::getScreen()\r
-{\r
- return screen;\r
-}\r
-\r
-int Surface::drawText(const char* text, int x, int y, const DrawStyle& c)\r
-{\r
- return drawText(text, x, y, 2000, c);\r
-}\r
-\r
-int Surface::drawText(const char* text, int x, int y, int width, const DrawStyle& c)\r
-{\r
- int h, n, i;\r
- int Y, X, cx;\r
-\r
- n = strlen(text);\r
- h = font->height;\r
-\r
- X = 0;\r
- cx = 0;\r
- ULONG rgba=c.rgba();\r
- startFastDraw();\r
- for (i=0; i<n; i++)\r
- {\r
- unsigned char c = text[i];\r
- unsigned long *character = &font->content[font->offset[c]];\r
- int w = font->width[c];\r
- int pixels = 0;\r
-\r
- for (X=0; (X<w) && (X + cx < width); X++)\r
- {\r
- for (Y=0; Y<h; Y++)\r
- {\r
- if ((character[Y] >> (32 - X)) & 0x1)\r
- {\r
- drawPixel(x+X+cx, y+Y, rgba,true);\r
- pixels++;\r
- }\r
- }\r
- }\r
- cx += w;\r
- }\r
- endFastDraw();\r
- return 1;\r
-}\r
-\r
-int Surface::drawTextRJ(const char* text, int x, int y, const DrawStyle& c)\r
-{\r
- int i, n, w;\r
- w = 0;\r
-\r
- n = strlen(text);\r
-\r
- for (i = 0; i < n; i++)\r
- {\r
- w += font->width[(unsigned char)text[i]];\r
- }\r
-\r
- x -= w;\r
-\r
- if (x < 0) return 0;\r
- else return drawText(text, x, y, c);\r
-}\r
-\r
-int Surface::drawTextCentre(const char* text, int x, int y, const DrawStyle& c)\r
-{\r
- int i, n, w;\r
- w = 0;\r
-\r
- n = strlen(text);\r
-\r
- for (i = 0; i < n; i++)\r
- {\r
- w += font->width[(unsigned char)text[i]]; //Characters bigger then 128 can appear\r
- }\r
-\r
- x -= w / 2;\r
-\r
- if (x < 0) return 0;\r
- else return drawText(text, x, y, c);\r
-}\r
-\r
-float Surface::getCharWidth(wchar_t c)\r
-{\r
- return (float)font->width[(unsigned char) c];\r
-}\r
-\r
-int Surface::getFontHeight()\r
-{\r
- return font->spacing;\r
-}\r
-\r
- wchar_t Surface::getWChar(const char* str, unsigned int *length)\r
- {\r
- *length=1;\r
- return *str;\r
- }\r
-\r
-//Moved from Teletext view in order to allow device depend optimizations\r
-\r
-Colour Surface::enumTeletextColorToCoulour(enumTeletextColor ttcol)\r
-{\r
- switch (ttcol) {\r
- case ttcBlack:\r
- return Colour(0,0,0);\r
- case ttcRed:\r
- return Colour(255,0,0);\r
- case ttcGreen:\r
- return Colour(0,255,0);\r
- case ttcYellow:\r
- return Colour(255,255,0);\r
- case ttcBlue:\r
- return Colour(0,0,255);\r
- case ttcMagenta:\r
- return Colour(255,0,255);\r
- case ttcCyan:\r
- return Colour(0,255,255);\r
- case ttcWhite:\r
- return Colour(255,255,255);\r
- case ttcTransparent:\r
- return Colour(0,0,0,0);\r
- case ttcHalfRed:\r
- return Colour(127,0,0);\r
- case ttcHalfGreen:\r
- return Colour(0,127,0);\r
- case ttcHalfYellow:\r
- return Colour(127,127,0);\r
- case ttcHalfBlue:\r
- return Colour(0,0,127);\r
- case ttcHalfMagenta:\r
- return Colour(127,0,127);\r
- case ttcHalfCyan:\r
- return Colour(0,127,127);\r
- case ttcGrey:\r
- return Colour(127,127,127);\r
- default:\r
- return Colour(0,0,0);\r
- };\r
-}\r
-\r
-\r
-\r
-//Next function inspired by osdteletext plugin\r
-void Surface::drawTTChar(int ox, int oy, int x, int y, cTeletextChar c)\r
-{\r
- if (!pol_table_inited){\r
- initpol_tables();\r
- pol_table_inited=true;\r
- }\r
- unsigned int buffer [10];\r
- unsigned int * charmap=GetFontChar(c,buffer);\r
- if (!charmap) { //invalid char\r
- memset(&buffer,0,10);\r
- charmap=buffer;\r
- }\r
- enumTeletextColor ttforegcolour=c.GetFGColor();\r
- enumTeletextColor ttbackgcolour=c.GetBGColor();\r
- if (c.GetBoxedOut()) {\r
- ttforegcolour=ttcTransparent;\r
- ttbackgcolour=ttcTransparent;\r
- }\r
- int charsizex;\r
- int charsizey;\r
- charsizex=16;\r
-\r
- if (Video::getInstance()->getFormat() == Video::PAL)\r
- {\r
- charsizey=22;\r
- } else {\r
- charsizey=18;\r
- }\r
- int ttcharsizex=12;\r
- int ttcharsizey=10;\r
- int screenposx=charsizex*x+ox; //12*40= 480 250\r
- int screenposy=y*charsizey+oy;\r
-\r
-\r
- // Log::getInstance()->log("Surface", Log::ERR, "TTpos %d %d %d %d %d %d",x,y,ox,oy,screenposx,screenposy);\r
- Colour fgcharcl=enumTeletextColorToCoulour(ttforegcolour);\r
- Colour bgcharcl=enumTeletextColorToCoulour(ttbackgcolour);\r
-\r
- startFastDraw();\r
- for (int py=0;py<charsizey;py++) {\r
- int upperbitline=charmap[interpol_upline[py]];\r
- int lowerbitline=charmap[interpol_lowline[py]];\r
- for (int px=0;px<charsizex;px++) {\r
- int upperbit= interpol_upbit[px];\r
- int lowerbit= interpol_lowbit[px];\r
- Colour uuc=( upperbitline &(0x8000>>upperbit)) ? fgcharcl: bgcharcl;\r
- Colour ulc=( upperbitline &(0x8000>>lowerbit)) ? fgcharcl: bgcharcl;\r
- Colour luc=( lowerbitline &(0x8000>>upperbit)) ? fgcharcl: bgcharcl;\r
- Colour llc=( lowerbitline &(0x8000>>lowerbit)) ? fgcharcl: bgcharcl;\r
- unsigned int fac1,fac2,fac3,fac4;\r
- fac1=interpol_table_fac1[px][py];\r
- fac2=interpol_table_fac2[px][py];\r
- fac3=interpol_table_fac3[px][py];\r
- fac4=interpol_table_fac4[px][py];\r
-\r
- Colour res((uuc.red*fac1+ulc.red*fac2+luc.red*fac3+llc.red*fac4)/256,\r
- (uuc.green*fac1+ulc.green*fac2+luc.green*fac3+llc.green*fac4)/256,\r
- (uuc.blue*fac1+ulc.blue*fac2+luc.blue*fac3+llc.blue*fac4)/256,\r
- (uuc.alpha*fac1+ulc.alpha*fac2+luc.alpha*fac3+llc.alpha*fac4)/256); //if this is too slow make a table\r
- int c = ( (res.alpha << 24 )\r
- | (res.red << 16)\r
- | (res.green << 8)\r
- | (res.blue ) );\r
- drawPixel(screenposx+px,screenposy+py,c, true);\r
- }\r
- }\r
-\r
-\r
- endFastDraw();\r
-\r
-\r
-}\r
-\r
-void Surface::drawMonoBitmap(UCHAR* base, int dx, int dy, unsigned int height,\r
- unsigned int width, Colour& nextColour) {\r
- startFastDraw();\r
- int x, y;\r
- unsigned int bytesIn, bitsIn;\r
- int widthBytes=width/8;\r
- for (y = 0; y < height; y++) {\r
- for (x = 0; x < width; x++) {\r
- bytesIn = (y * widthBytes) + (int) (x / 8);\r
- bitsIn = x % 8;\r
-\r
- if ((base[bytesIn] >> (7 - bitsIn)) & 0x01) {\r
- drawPixel(dx+x, dy+y, nextColour, true);\r
- }\r
- }\r
- }\r
- endFastDraw();\r
-}\r
-\r
-void Surface::drawPoint(int x, int y, DrawStyle& c, bool fastdraw)\r
-{\r
- drawPixel(x,y,c,fastdraw);\r
-}\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 "surface.h"
+
+#include <math.h>
+#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;py<charsizey;py++) {
+ float fposy=((float)(ttcharsizey))/((float)(charsizey))*((float)py);
+ float yweight=fposy-floor(fposy);
+ float yinvweight=1.-yweight;
+ interpol_upline[py]=min((unsigned int)ceil(fposy),9);
+ interpol_lowline[py]=max((unsigned int)floor(fposy),0);
+ for (int px=0;px<charsizex;px++) {
+ float fposx=((float)(ttcharsizex))/((float)(charsizex))*((float)px);
+ float xweight=fposx-floor(fposx);
+ float xinvweight=1.-xweight;
+ interpol_upbit[px]= (min((unsigned int)ceil(fposx),11));
+ interpol_lowbit[px]= (max((unsigned int)floor(fposx),0));
+
+ interpol_table_fac1[px][py]=xweight*yweight*256.;
+ interpol_table_fac2[px][py]=xinvweight*yweight*256.;
+ interpol_table_fac3[px][py]=xweight*yinvweight*256.;
+ interpol_table_fac4[px][py]=xinvweight*yinvweight*256.;
+
+ }
+ }
+}
+
+
+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, const DrawStyle& c)
+{
+ return drawText(text, x, y, 2000, c);
+}
+
+int Surface::drawText(const char* text, int x, int y, int width, const DrawStyle& c)
+{
+ int h, n, i;
+ int Y, X, cx;
+
+ n = strlen(text);
+ h = font->height;
+
+ X = 0;
+ cx = 0;
+ ULONG rgba=c.rgba();
+ startFastDraw();
+ for (i=0; i<n; i++)
+ {
+ unsigned char c = text[i];
+ unsigned long *character = &font->content[font->offset[c]];
+ int w = font->width[c];
+ int pixels = 0;
+
+ for (X=0; (X<w) && (X + cx < width); X++)
+ {
+ for (Y=0; Y<h; Y++)
+ {
+ if ((character[Y] >> (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, const DrawStyle& c)
+{
+ 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, c);
+}
+
+int Surface::drawTextCentre(const char* text, int x, int y, const DrawStyle& c)
+{
+ 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, c);
+}
+
+float Surface::getCharWidth(wchar_t c)
+{
+ return (float)font->width[(unsigned char) c];
+}
+
+int Surface::getFontHeight()
+{
+ return font->spacing;
+}
+
+ wchar_t Surface::getWChar(const char* str, unsigned int *length)
+ {
+ *length=1;
+ return *str;
+ }
+
+//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<charsizey;py++) {
+ int upperbitline=charmap[interpol_upline[py]];
+ int lowerbitline=charmap[interpol_lowline[py]];
+ for (int px=0;px<charsizex;px++) {
+ int upperbit= interpol_upbit[px];
+ int lowerbit= interpol_lowbit[px];
+ Colour uuc=( upperbitline &(0x8000>>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();
+
+
+}
+
+void Surface::drawMonoBitmap(UCHAR* base, int dx, int dy, unsigned int height,
+ unsigned int width, Colour& nextColour) {
+ startFastDraw();
+ int x, y;
+ unsigned int bytesIn, bitsIn;
+ int widthBytes=width/8;
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++) {
+ bytesIn = (y * widthBytes) + (int) (x / 8);
+ bitsIn = x % 8;
+
+ if ((base[bytesIn] >> (7 - bitsIn)) & 0x01) {
+ drawPixel(dx+x, dy+y, nextColour, true);
+ }
+ }
+ }
+ endFastDraw();
+}
+
+void Surface::drawPoint(int x, int y, DrawStyle& c, bool fastdraw)
+{
+ drawPixel(x,y,c,fastdraw);
+}
-/*\r
- Copyright 2004-2005 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef SURFACE_H\r
-#define SURFACE_H\r
-\r
-#include <stdio.h>\r
-#include "defines.h"\r
-#include "colour.h"\r
-\r
-#include "teletextdecodervbiebu.h"\r
-\r
-// Font stuff\r
-\r
-typedef struct bogl_font {\r
- char *name; /* Font name. */\r
- int height; /* Height in pixels. */\r
- int spacing; /* Vertical spacing in pixels. */\r
- unsigned long *content; /* 32-bit right-padded bitmap array. */\r
- short *offset; /* 256 offsets into content. */\r
- unsigned char *width; /* 256 character widths. */\r
-} osd_font_t;\r
-\r
-//extern osd_font_t font_CaslonRoman_1_25;\r
-//extern osd_font_t font_helvB24;\r
-extern osd_font_t font_helvB18;\r
-\r
-class Bitmap;\r
-class DisplayRegion;\r
-\r
-\r
-\r
-class Surface\r
-{\r
- friend class Wwss; // classes that need surface access, their usage is forbidden for vector based\r
- friend class WJpegComplex;// implementations of osd\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.
+*/
+
+#ifndef SURFACE_H
+#define SURFACE_H
+
+#include <stdio.h>
+#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 DisplayRegion;
+
+
+
+class Surface
+{
+ friend class Wwss; // classes that need surface access, their usage is forbidden for vector based
+ friend class WJpegComplex;// implementations of osd
friend class VColourTuner;
- public:\r
- Surface(int id = 0);\r
- virtual ~Surface();\r
-\r
- static Surface* getScreen();\r
- virtual int getFontHeight();\r
- virtual float getCharWidth(wchar_t c);\r
- virtual wchar_t getWChar(const char* str, unsigned int *length);\r
-\r
- virtual int drawText(const char* text, int x, int y, const DrawStyle& c);\r
- virtual int drawText(const char* text, int x, int y, int width, const DrawStyle& c);\r
- virtual int drawTextRJ(const char* text, int x, int y, const DrawStyle& c);\r
- virtual int drawTextCentre(const char* text, int x, int y, const DrawStyle& c);\r
-\r
- virtual void drawJpeg(const char *fileName,int x, int y,int *width, int *height) {}\r
-\r
- virtual int create(UINT width, UINT height)=0;\r
- virtual void display()=0;\r
-\r
- virtual int fillblt(int x, int y, int width, int height, const DrawStyle& c)=0;\r
- virtual void drawHorzLine(int x1, int x2, int y, const DrawStyle& c)=0;\r
- virtual void drawVertLine(int x, int y1, int y2, const DrawStyle& c)=0;\r
- virtual void drawBitmap(int x, int y, const Bitmap& bm,const DisplayRegion & region)=0;\r
- virtual void drawPoint(int x, int y, DrawStyle& c, bool fastdraw=false); // This draws a point, must not be a pixel\r
- virtual void drawMonoBitmap(UCHAR* base, int dx, int dy, unsigned int height,unsigned int width, Colour& nextColour);\r
- virtual int updateToScreen(int sx, int sy, int w, int h, int dx, int dy)=0;\r
- virtual void readPixel(int x, int y, unsigned char* r, unsigned char* g, unsigned char* b)=0;\r
- virtual void screenShot(const char* fileName)=0;\r
-\r
- /* This is for system which need a locking of the drawing surface to speed up drawing */\r
- virtual void startFastDraw() {};\r
- virtual void endFastDraw() {};\r
-\r
-\r
- virtual void drawTTChar(int ox, int oy,int x, int y, cTeletextChar c);\r
- static Colour enumTeletextColorToCoulour(enumTeletextColor ttcol);\r
-\r
-\r
- \r
-\r
- // virtual int blt(int fd, unsigned long shandle, int sx, int sy, int width, int height, unsigned long dhandle, int dx, int dy)=0;\r
-\r
- const static int SCREEN = 1;\r
- const static int BUFFER = 2;\r
-\r
- protected:\r
- static Surface* screen;\r
- static osd_font_t* font;\r
-\r
-\r
- virtual void drawPixel(int x, int y, unsigned int c, bool fastdraw=false)=0; // deprecated preparation for vector based drawing, only allowed to be called inside implementation\r
- virtual void drawPixel(int x, int y, Colour& c, bool fastdraw=false)=0; // deprecated preparation for vector based drawing, only allowed to be called inside implementation\r
-\r
-};\r
-\r
-#endif\r
+ public:
+ Surface(int id = 0);
+ virtual ~Surface();
+
+ static Surface* getScreen();
+ virtual int getFontHeight();
+ virtual float getCharWidth(wchar_t c);
+ virtual wchar_t getWChar(const char* str, unsigned int *length);
+
+ virtual int drawText(const char* text, int x, int y, const DrawStyle& c);
+ virtual int drawText(const char* text, int x, int y, int width, const DrawStyle& c);
+ virtual int drawTextRJ(const char* text, int x, int y, const DrawStyle& c);
+ virtual int drawTextCentre(const char* text, int x, int y, const DrawStyle& c);
+
+ 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, const DrawStyle& c)=0;
+ virtual void drawHorzLine(int x1, int x2, int y, const DrawStyle& c)=0;
+ virtual void drawVertLine(int x, int y1, int y2, const DrawStyle& c)=0;
+ virtual void drawBitmap(int x, int y, const Bitmap& bm,const DisplayRegion & region)=0;
+ virtual void drawPoint(int x, int y, DrawStyle& c, bool fastdraw=false); // This draws a point, must not be a pixel
+ virtual void drawMonoBitmap(UCHAR* base, int dx, int dy, unsigned int height,unsigned int width, Colour& nextColour);
+ 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);
+ static Colour enumTeletextColorToCoulour(enumTeletextColor ttcol);
+
+
+
+
+ // 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;
+
+
+ virtual void drawPixel(int x, int y, unsigned int c, bool fastdraw=false)=0; // deprecated preparation for vector based drawing, only allowed to be called inside implementation
+ virtual void drawPixel(int x, int y, Colour& c, bool fastdraw=false)=0; // deprecated preparation for vector based drawing, only allowed to be called inside implementation
+
+};
+
+#endif
-/*\r
- Copyright 2004-2005 Chris Tallon, 2009 Marten Richter\r
- Portions copyright 2008 Jon Gettler\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#include "surfacedirectfb.h"\r
-\r
-#include "osd.h"\r
-#include "bitmap.h"\r
-#include "log.h"\r
-\r
-#include "osddirectfb.h"\r
-\r
-\r
-\r
-SurfaceDirectFB::SurfaceDirectFB(int id)\r
-: Surface(id)\r
-{\r
- surface=NULL;\r
-}\r
-\r
-SurfaceDirectFB::~SurfaceDirectFB()\r
-{\r
- if (surface) surface->Release(surface);\r
-\r
-}\r
-\r
-int SurfaceDirectFB::create(UINT width, UINT height)\r
-{\r
- int counter=0;\r
- while (!Osd::getInstance()->isInitted() && counter<2000) {\r
- MILLISLEEP(50); //Wait for Grafiksystem initialization\r
- counter++;\r
- }\r
- if (!Osd::getInstance()->isInitted()) return -1;\r
- \r
- IDirectFB*dfb=((OsdDirectFB*)Osd::getInstance())->getDfb();\r
-printf("ich bin doof");fflush(stdout); \r
- if (screen == this) \r
- {\r
- IDirectFBDisplayLayer *osd_layer=((OsdDirectFB*)Osd::getInstance())->getOsdLayer();\r
- if (osd_layer->GetSurface(osd_layer,&surface)!=DFB_OK) \r
- {\r
-\r
- return 0;\r
- }\r
- surface->Clear(surface,0x0,0x0,0x0,0xFF);\r
- } else {\r
- DFBSurfaceDescription dsc;\r
- memset(&dsc,0,sizeof(dsc));\r
- *((int*)&dsc.flags)=DSDESC_CAPS | DSDESC_WIDTH | DSDESC_HEIGHT;\r
- \r
- dsc.width=width;\r
- dsc.height=height;\r
- dsc.caps= DSCAPS_NONE;\r
- if (dfb->CreateSurface(dfb,&dsc,&surface)!=DFB_OK) \r
- {\r
- return 0;\r
- }\r
- }\r
- return 1;\r
-}\r
-\r
-void SurfaceDirectFB::display()\r
-{\r
-// unsigned long fb_descriptor[2];\r
-/*\r
- fb_descriptor[0] = surface.sfc.handle;\r
- fb_descriptor[1] = 1;\r
-*/\r
-// ioctl(fdOsd, GFX_FB_ATTACH, fb_descriptor);\r
-}\r
-\r
-\r
-// ----------------------------------------------------------------------------\r
-\r
-// Now for the drawing functions\r
-\r
-\r
-int SurfaceDirectFB::fillblt(int x, int y, int width, int height, unsigned int c)\r
-{\r
- int sw,sh;\r
- unsigned char r,g,b,a;\r
- int nx,ny,nw,nh;\r
- nx=x;\r
- ny=y;\r
- nw=width;\r
- nh=height;\r
- surface->GetSize(surface,&sw,&sh);\r
- \r
- if (nx >sw) nx=sw-1;\r
- if (ny >sh) ny=sh-1;\r
- \r
- if ((nx+nw) >= sw) nw=sw-nx; \r
- if ((ny+nh) >= sh) nh=sh-ny; \r
-\r
- if ((nx<0) || (ny < 0) || (nh <=0) || (nw <=0)) {\r
- return 0;\r
- }\r
- \r
- a= (c &0xff000000)>>24;\r
- r= (c &0x00ff0000)>>16;\r
- g= (c &0x0000ff00)>>8;\r
- b= (c &0x000000ff);\r
- \r
- \r
- surface->SetColor(surface,r,g,b,a);\r
- surface->FillRectangle(surface,nx,ny,nw,nh);\r
- return 0;\r
-}\r
-\r
-void SurfaceDirectFB::drawPixel(int x, int y, unsigned int c, bool fastdraw)\r
-{\r
- int sw,sh;\r
- unsigned char r,g,b,a;\r
- char *dst=NULL;\r
- int offset=0;\r
- int pitch;\r
- surface->GetSize(surface,&sw,&sh);\r
- if (x>=sw) return;\r
- if (y>=sh) return;\r
- \r
-\r
- \r
- a= (c &0xff000000)>>24;\r
- r= (c &0x00ff0000)>>16;\r
- g= (c &0x0000ff00)>>8;\r
- b= (c &0x000000ff);\r
-\r
- //TODO Fastdraw\r
- if (surface->Lock(surface,DSLF_WRITE,(void**)(void*)&dst,&pitch) == DFB_OK) {\r
- offset= y* pitch+x*4;\r
- dst[offset++]=b;\r
- dst[offset++]=g;\r
- dst[offset++]=r;\r
- dst[offset]=a;\r
- surface->Unlock(surface);\r
- }\r
-\r
-}\r
-\r
-void SurfaceDirectFB::drawPixel(int x, int y, Colour& c, bool fastdraw)\r
-{\r
- int sw,sh;\r
- char *dst=NULL;\r
- int offset=0;\r
- int pitch;\r
- surface->GetSize(surface,&sw,&sh);\r
- if (x>=sw) return;\r
- if (y>=sh) return;\r
- \r
- //TODO Fastdraw\r
- if (surface->Lock(surface,DSLF_WRITE,(void**)(void*)&dst,&pitch) == DFB_OK) {\r
- offset= y* pitch+x*4;\r
- dst[offset++]=c.blue;\r
- dst[offset++]=c.green;\r
- dst[offset++]=c.red;\r
- dst[offset]=c.alpha;\r
- surface->Unlock(surface);\r
- }\r
-\r
-}\r
- \r
-void SurfaceDirectFB::drawHorzLine(int x1, int x2, int y, unsigned int c)\r
-{\r
- fillblt(x1, y, x2-x1, 1, c);\r
-}\r
-\r
-void SurfaceDirectFB::drawVertLine(int x, int y1, int y2, unsigned int c)\r
-{\r
- fillblt(x, y1, 1, y2-y1, c);\r
-}\r
-\r
-void SurfaceDirectFB::drawBitmap(int x, int y, const Bitmap& bm)\r
-{/*\r
- UINT bmw = bm.getWidth(); UINT bmh = bm.getHeight();\r
- if (bmw == 0 || bmh == 0) return;\r
- if ((x >= (int)surface.sfc.width) || (y >= (int)surface.sfc.height)) return;\r
- int remainder = (surface.sfc.width % 4);\r
- UINT line;\r
- if (remainder == 0)\r
- line = surface.sfc.width;\r
- else\r
- line = surface.sfc.width + (4 - remainder);\r
- const std::vector<UCHAR>& bmdata = bm.rawData();\r
- const std::vector<UCHAR>& Y = bm.palette.getYVector();\r
- const std::vector<UCHAR>& Cr = bm.palette.getCrVector();\r
- const std::vector<UCHAR>& Cb = bm.palette.getCbVector();\r
- const std::vector<UCHAR>& A = bm.palette.getAVector();\r
- UINT b_offset = 0;\r
- UINT s_offset = x + y*line;\r
- UINT plotWidth = bmw;\r
- UINT plotHeight = bmh;\r
- if (x + plotWidth - 1 > surface.sfc.width)\r
- plotWidth = surface.sfc.width - x + 1;\r
- if (y + plotHeight - 1 > surface.sfc.height)\r
- plotHeight = surface.sfc.height - y + 1;\r
- for (UINT j = 0; j < plotHeight; ++j)\r
- {\r
- UINT i = 0;\r
- if (x & 1) // odd x - need to plot first column separately\r
- {\r
- UCHAR index = bmdata[b_offset];\r
- *(surface.base[0] + s_offset) = Y[index];\r
- *(surface.base[1] + s_offset - 1) = Cb[index];\r
- *(surface.base[1] + s_offset) = Cr[index];\r
- *(surface.base[2] + s_offset) = A[index];\r
- i = 1;\r
- }\r
- // Now, plot pairs of pixels with averaged chroma values\r
- while (i < plotWidth - 1)\r
- {\r
- UCHAR index1 = bmdata[b_offset + i];\r
- UCHAR index2 = bmdata[b_offset + i + 1];\r
- *(surface.base[0] + s_offset + i) = Y[index1];\r
- *(surface.base[0] + s_offset + i + 1) = Y[index2];\r
- *(surface.base[1] + s_offset + i) = (Cb[index1] + Cb[index2]) / 2;\r
- *(surface.base[1] + s_offset + i + 1) = (Cr[index1] + Cr[index2]) / 2;\r
- *(surface.base[2] + s_offset + i) = A[index1];\r
- *(surface.base[2] + s_offset + i + 1) = A[index2];\r
- i += 2;\r
- }\r
- if (i == plotWidth - 1) // One column left to do\r
- {\r
- UCHAR index = bmdata[b_offset + i];\r
- *(surface.base[0] + s_offset + i) = Y[index];\r
- *(surface.base[1] + s_offset + i) = Cb[index];\r
- *(surface.base[1] + s_offset + i + 1) = Cr[index];\r
- *(surface.base[2] + s_offset + i) = A[index];\r
- }\r
- s_offset += line;\r
- b_offset += bmw;\r
- }*/\r
-}\r
-\r
- /* surface update to screen needs:\r
- source x distance into this surface\r
- source y distance into this surface\r
- width of update\r
- height of update\r
- destination x on screen\r
- destination y on screen\r
- */\r
-int SurfaceDirectFB::updateToScreen(int sx, int sy, int w, int h, int dx, int dy) // FIXME new, replace others with this FIXME\r
-{\r
- IDirectFBSurface* screensurf=((SurfaceDirectFB*)screen)->getSurfaceDFB();\r
- if (!screensurf) return 0;\r
- if (this==screen) return 0;\r
- \r
- DFBRectangle rect;\r
- int sw,sh;\r
- \r
- screensurf->GetSize(screensurf,&sw,&sh);\r
-// screensurf->Clear(screensurf,0x0,0x0,0x0,0xFF);\r
- rect.x = sx;\r
- rect.y=sy;\r
- rect.w=w;\r
- rect.h=h;\r
- \r
- DFBRectangle drect; //TODO make osd HD\r
- drect.x=dx*sw/720;\r
- drect.y=dy*sh/576;\r
- drect.w=w*sw/720;\r
- drect.h=h*sh/576;\r
- \r
-// screensurf->Blit(screensurf,surface,&rect,dx,dy);\r
- screensurf->StretchBlit(screensurf,surface,&rect,&drect);\r
- \r
- return 0;//blt(fdOsd, surface.sfc.handle, sx, sy, w, h, ((SurfaceDirectFB*)screen)->getSurfaceHandle(), dx, dy);\r
-}\r
-\r
-int SurfaceDirectFB::blt(int fd, unsigned long shandle, int sx, int sy, int width, int height, unsigned long dhandle, int dx, int dy)\r
-{//Skip it, noone uses this!\r
-\r
- return 0;\r
-}\r
-\r
-void SurfaceDirectFB::screenShot(const char* fileName)\r
-{\r
- return;\r
- \r
-}\r
-\r
-void SurfaceDirectFB::readPixel(int x, int y, unsigned char* r, unsigned char* g, unsigned char* b)\r
-{\r
- \r
-}\r
-\r
+/*
+ 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<UCHAR>& bmdata = bm.rawData();
+ const std::vector<UCHAR>& Y = bm.palette.getYVector();
+ const std::vector<UCHAR>& Cr = bm.palette.getCrVector();
+ const std::vector<UCHAR>& Cb = bm.palette.getCbVector();
+ const std::vector<UCHAR>& 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)
+{
+
+}
+
-/*\r
- Copyright 2004-2005 Chris Tallon, 2009 Marten Richter\r
- Portions copyright 2004 Jon Gettler\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef SURFACEDirectFB_H\r
-#define SURFACEDirectFB_H\r
-\r
-#include <stdio.h>\r
-#include <string.h>\r
-#include <unistd.h>\r
-#include <sys/mman.h>\r
-#include <sys/ioctl.h>\r
-\r
-extern "C"\r
-{\r
- #include <jpeglib.h>\r
-}\r
-\r
-#include "defines.h"\r
-#include "surface.h"\r
-\r
-#include <directfb.h>\r
-\r
-\r
-\r
-class SurfaceDirectFB : public Surface\r
-{\r
- public:\r
- SurfaceDirectFB(int id = 0);\r
- virtual ~SurfaceDirectFB();\r
-\r
- int create(UINT width, UINT height);\r
- void display();\r
-\r
- int fillblt(int x, int y, int width, int height, unsigned int rgba);\r
-\r
- void drawHorzLine(int x1, int x2, int y, unsigned int c);\r
- void drawVertLine(int x, int y1, int y2, unsigned int c);\r
- void drawBitmap(int x, int y, const Bitmap& bm);\r
- int updateToScreen(int sx, int sy, int w, int h, int dx, int dy);\r
- void readPixel(int x, int y, unsigned char* r, unsigned char* g, unsigned char* b);\r
- void screenShot(const char* fileName);\r
- IDirectFBSurface* getSurfaceDFB(){return surface;};\r
-\r
-\r
- int blt(int fd, unsigned long shandle, int sx, int sy, int width, int height, unsigned long dhandle, int dx, int dy);\r
-\r
- private:\r
- IDirectFBSurface *surface;\r
-\r
- void drawPixel(int x, int y, unsigned int c, bool fastdraw=false);\r
- void drawPixel(int x, int y, Colour& c, bool fastdraw=false);\r
-\r
-\r
-\r
-};\r
-\r
-#endif\r
+/*
+ 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 <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+
+extern "C"
+{
+ #include <jpeglib.h>
+}
+
+#include "defines.h"
+#include "surface.h"
+
+#include <directfb.h>
+
+
+
+class SurfaceDirectFB : public Surface
+{
+ public:
+ SurfaceDirectFB(int id = 0);
+ virtual ~SurfaceDirectFB();
+
+ int create(UINT width, UINT height);
+ void display();
+
+ int fillblt(int x, int y, int width, int height, unsigned int rgba);
+
+ 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;
+
+ void drawPixel(int x, int y, unsigned int c, bool fastdraw=false);
+ void drawPixel(int x, int y, Colour& c, bool fastdraw=false);
+
+
+
+};
+
+#endif
-/*\r
- Copyright 2004-2005 Chris Tallon\r
- Portions copyright 2004 Jon Gettler\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef SURFACEMVP_H\r
-#define SURFACEMVP_H\r
-\r
-#include <stdio.h>\r
-#include <string.h>\r
-#include <unistd.h>\r
-#include <sys/mman.h>\r
-#include <sys/ioctl.h>\r
-\r
-extern "C"\r
-{\r
- #include <jpeglib.h>\r
-}\r
-\r
-#include "defines.h"\r
-#include "surface.h"\r
-\r
-// Structures for surface management\r
-\r
-typedef struct\r
-{\r
- unsigned long num;\r
- unsigned long unknown[4];\r
- unsigned long width;\r
- unsigned long height;\r
- char unknown2;\r
-} stbgfx_display_t;\r
-\r
-typedef struct\r
-{\r
- unsigned long unknown;\r
- unsigned long win_unknown;\r
- unsigned long addr;\r
- unsigned long size;\r
- unsigned long unknown2;\r
- unsigned long width;\r
- unsigned long height;\r
- unsigned long unknown3;\r
- unsigned long unknown4;\r
- unsigned long width2;\r
- unsigned long unknown5;\r
- unsigned long unknown6;\r
-} stbgfx_map_item_t;\r
-\r
-typedef struct {\r
- stbgfx_map_item_t map[3];\r
- unsigned long other[2];\r
-} stbgfx_map_t;\r
-\r
-typedef struct\r
-{\r
- unsigned long handle; /* surface handle */\r
- unsigned long width;\r
- unsigned long height;\r
- unsigned long flags;\r
- long unknown; //unsigned long\r
- unsigned long depth; /* number of subplanes */\r
-} stbgfx_sfc_t;\r
-\r
-typedef struct\r
-{\r
- stbgfx_display_t display;\r
- stbgfx_map_t map;\r
- stbgfx_sfc_t sfc;\r
- unsigned char *base[3];\r
-} osd_surface_t;\r
-\r
-\r
-// Structures for surface drawing\r
-\r
-typedef struct\r
-{\r
- unsigned long handle;\r
- unsigned long x;\r
- unsigned long y;\r
- unsigned long width;\r
- unsigned long height;\r
- unsigned long colour;\r
-} osd_fillblt_t;\r
-\r
-typedef struct {\r
- unsigned long dst_handle;\r
- unsigned long dst_x;\r
- unsigned long dst_y;\r
- unsigned long width;\r
- unsigned long height;\r
- unsigned long src_handle;\r
- unsigned long src_x;\r
- unsigned long src_y;\r
- unsigned long u1;\r
- unsigned long u2;\r
- unsigned char u3;\r
-} osd_bitblt_t;\r
-\r
-// Surface ioctls\r
-\r
-#define GFX_FB_SFC_ALLOC _IOWR(0xfb,1,int*)\r
-#define GFX_FB_SFC_FREE _IOW(0xfb,2,int)\r
-#define GFX_FB_MAP _IOWR(0xfb,3,int*)\r
-#define GFX_FB_SFC_UNMAP _IOW(0xfb,4,int)\r
-#define GFX_FB_SET_PAL_1 _IOWR(0xfb,5,int*)\r
-#define GFX_FB_SET_PAL_2 _IOW(0xfb,6,int*)\r
-#define GFX_FB_OSD_SURFACE _IO(0xfb,7)\r
-#define GFX_FB_SFC_SET_SHARE _IOW(0xfb,8,int)\r
-#define GFX_FB_OSD_CUR_SETATTR _IOW(0xfb,9,int*)\r
-#define GFX_FB_ATTACH _IOW(0xfb,11,int)\r
-#define GFX_FB_SFC_DETACH _IOW(0xfb,12,int*)\r
-#define GFX_FB_MOVE_DISPLAY _IOWR(0xfb,13,int)\r
-#define GFX_FB_SET_DISPLAY _IOW(0xfb,14,int)\r
-#define GFX_FB_OSD_CUR_MOVE_1 _IOW(0xfb,15,int*)\r
-#define GFX_FB_OSD_CUR_MOVE_2 _IOW(0xfb,16,int)\r
-#define GFX_FB_SET_OSD _IOW(0xfb,18,int)\r
-#define GFX_FB_SET_DISP_CTRL _IOW(0xfb,21,int*)\r
-#define GFX_FB_GET_DISP_CTRL _IOWR(0xfb,22,int*)\r
-#define GFX_FB_SET_VIS_DEV_CTL _IOWR(0xfb,23,int*)\r
-#define GFX_FB_GET_VIS_DEV_CTL _IOWR(0xfb,24,int*)\r
-#define GFX_FB_OSD_BITBLT _IOW(0xfb,51,osd_bitblt_t*)\r
-#define GFX_FB_OSD_FILLBLT _IOW(0xfb,53,osd_fillblt_t*)\r
-#define GFX_FB_OSD_ADVFILLBLT _IOW(0xfb,54,osd_afillblt_t*)\r
-#define GFX_FB_OSD_BLEND _IOW(0xfb,55,int*)\r
-#define GFX_FB_OSD_ADVBLEND _IOW(0xfb,56,int*)\r
-#define GFX_FB_OSD_RESIZE _IOW(0xfb,58,int*)\r
-#define GFX_FB_ENGINE_WAIT _IOW(0xfb,60,int)\r
-#define GFX_FB_RESET_ENGINE _IO(0xfb,61)\r
-#define GFX_FB_SET_ENGINE_MODE _IOW(0xfb,62,int)\r
-#define GFX_FB_GET_ENGINE_MODE _IO(0xfb,63)\r
-#define GFX_FB_GET_SFC_INFO _IO(0xfb,64,int*)\r
-#define GFX_FB_OSD_SFC_CLIP _IOW(0xfb,65,osd_clip_rec_t*)\r
-#define GFX_FB_OSD_COLOURKEY _IOW(0xfb,67,int*)\r
-#define GFX_FB_GET_SFC_PSEUDO _IOWR(0xfb,68,int*)\r
-\r
-class SurfaceMVP : public Surface\r
-{\r
- public:\r
- SurfaceMVP(int id = 0);\r
- virtual ~SurfaceMVP();\r
-\r
- int create(UINT width, UINT height);\r
- void display();\r
- unsigned long getSurfaceHandle();\r
-\r
- int fillblt(int x, int y, int width, int height, const DrawStyle& c);\r
-\r
- void drawHorzLine(int x1, int x2, int y, const DrawStyle& c);\r
- void drawVertLine(int x, int y1, int y2, const DrawStyle& c);\r
- void drawBitmap(int x, int y, const Bitmap& bm, const DisplayRegion& region);\r
- int updateToScreen(int sx, int sy, int w, int h, int dx, int dy);\r
- void readPixel(int x, int y, unsigned char* r, unsigned char* g, unsigned char* b);\r
- void screenShot(const char* fileName);\r
-\r
- static void initConversionTables()\r
- {\r
- initConversionTables(100,100,100);\r
- }\r
- //init tables with factors for R,G,B - each in percent\r
- static void initConversionTables(int r, int g, int b);\r
-\r
- int blt(int fd, unsigned long shandle, int sx, int sy, int width, int height, unsigned long dhandle, int dx, int dy);\r
-\r
- private:\r
- int fdOsd;\r
- osd_surface_t surface;\r
-\r
- void yuv2rgb(int y, int u, int v, unsigned char* pr, unsigned char* pg, unsigned char* pb);\r
- /*\r
- * rgb2yuv() - convert an RGB pixel to YUV\r
- */\r
- inline void rgb2yuv(unsigned char r, unsigned char g, unsigned char b, unsigned char *y, unsigned char *u, unsigned char *v)\r
- {\r
- int Y, U, V;\r
-\r
- Y = conv_YB[b] + conv_YG[g] + conv_YR[r];\r
- U = conv_UB[b] + conv_UG[g] + conv_UR[r] + 128;\r
- V = conv_VB[b] + conv_VG[g] + conv_VR[r] + 128;\r
-\r
- if (Y > 255)\r
- Y = 255;\r
- else if (Y < 0)\r
- Y = 0;\r
- if (U > 255)\r
- U = 255;\r
- else if (U < 0)\r
- U = 0;\r
- if (V > 255)\r
- V = 255;\r
- else if (V < 0)\r
- V = 0;\r
-\r
- *y = Y;\r
- *u = U;\r
- *v = V;\r
- }\r
-\r
-\r
- // Implicit inlines\r
- void c2rgba(unsigned long c, unsigned char *r, unsigned char *g, unsigned char *b, unsigned char *a)\r
- {\r
- *a = (c & 0xff000000) >> 24;\r
- *r = (c & 0x00ff0000) >> 16;\r
- *g = (c & 0x0000ff00) >> 8;\r
- *b = (c & 0x000000ff);\r
- }\r
-\r
- static int conv_YB[256];\r
- static int conv_YG[256];\r
- static int conv_YR[256];\r
- static int conv_UB[256];\r
- static int conv_UG[256];\r
- static int conv_UR[256];\r
- static int conv_VB[256];\r
- static int conv_VG[256];\r
- static int conv_VR[256];\r
-\r
- static int conv_BY[256];\r
- static int conv_GY[256];\r
- static int conv_RY[256];\r
- static int conv_BU[256];\r
- static int conv_GU[256];\r
- static int conv_RU[256];\r
- static int conv_BV[256];\r
- static int conv_GV[256];\r
- static int conv_RV[256];\r
- protected:\r
- void drawPixel(int x, int y, unsigned int c, bool fastdraw=false);\r
- void drawPixel(int x, int y, Colour& c, bool fastdraw=false);\r
-\r
-};\r
-\r
-#endif\r
+/*
+ Copyright 2004-2005 Chris Tallon
+ 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 SURFACEMVP_H
+#define SURFACEMVP_H
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+
+extern "C"
+{
+ #include <jpeglib.h>
+}
+
+#include "defines.h"
+#include "surface.h"
+
+// Structures for surface management
+
+typedef struct
+{
+ unsigned long num;
+ unsigned long unknown[4];
+ unsigned long width;
+ unsigned long height;
+ char unknown2;
+} stbgfx_display_t;
+
+typedef struct
+{
+ unsigned long unknown;
+ unsigned long win_unknown;
+ unsigned long addr;
+ unsigned long size;
+ unsigned long unknown2;
+ unsigned long width;
+ unsigned long height;
+ unsigned long unknown3;
+ unsigned long unknown4;
+ unsigned long width2;
+ unsigned long unknown5;
+ unsigned long unknown6;
+} stbgfx_map_item_t;
+
+typedef struct {
+ stbgfx_map_item_t map[3];
+ unsigned long other[2];
+} stbgfx_map_t;
+
+typedef struct
+{
+ unsigned long handle; /* surface handle */
+ unsigned long width;
+ unsigned long height;
+ unsigned long flags;
+ long unknown; //unsigned long
+ unsigned long depth; /* number of subplanes */
+} stbgfx_sfc_t;
+
+typedef struct
+{
+ stbgfx_display_t display;
+ stbgfx_map_t map;
+ stbgfx_sfc_t sfc;
+ unsigned char *base[3];
+} osd_surface_t;
+
+
+// Structures for surface drawing
+
+typedef struct
+{
+ unsigned long handle;
+ unsigned long x;
+ unsigned long y;
+ unsigned long width;
+ unsigned long height;
+ unsigned long colour;
+} osd_fillblt_t;
+
+typedef struct {
+ unsigned long dst_handle;
+ unsigned long dst_x;
+ unsigned long dst_y;
+ unsigned long width;
+ unsigned long height;
+ unsigned long src_handle;
+ unsigned long src_x;
+ unsigned long src_y;
+ unsigned long u1;
+ unsigned long u2;
+ unsigned char u3;
+} osd_bitblt_t;
+
+// Surface ioctls
+
+#define GFX_FB_SFC_ALLOC _IOWR(0xfb,1,int*)
+#define GFX_FB_SFC_FREE _IOW(0xfb,2,int)
+#define GFX_FB_MAP _IOWR(0xfb,3,int*)
+#define GFX_FB_SFC_UNMAP _IOW(0xfb,4,int)
+#define GFX_FB_SET_PAL_1 _IOWR(0xfb,5,int*)
+#define GFX_FB_SET_PAL_2 _IOW(0xfb,6,int*)
+#define GFX_FB_OSD_SURFACE _IO(0xfb,7)
+#define GFX_FB_SFC_SET_SHARE _IOW(0xfb,8,int)
+#define GFX_FB_OSD_CUR_SETATTR _IOW(0xfb,9,int*)
+#define GFX_FB_ATTACH _IOW(0xfb,11,int)
+#define GFX_FB_SFC_DETACH _IOW(0xfb,12,int*)
+#define GFX_FB_MOVE_DISPLAY _IOWR(0xfb,13,int)
+#define GFX_FB_SET_DISPLAY _IOW(0xfb,14,int)
+#define GFX_FB_OSD_CUR_MOVE_1 _IOW(0xfb,15,int*)
+#define GFX_FB_OSD_CUR_MOVE_2 _IOW(0xfb,16,int)
+#define GFX_FB_SET_OSD _IOW(0xfb,18,int)
+#define GFX_FB_SET_DISP_CTRL _IOW(0xfb,21,int*)
+#define GFX_FB_GET_DISP_CTRL _IOWR(0xfb,22,int*)
+#define GFX_FB_SET_VIS_DEV_CTL _IOWR(0xfb,23,int*)
+#define GFX_FB_GET_VIS_DEV_CTL _IOWR(0xfb,24,int*)
+#define GFX_FB_OSD_BITBLT _IOW(0xfb,51,osd_bitblt_t*)
+#define GFX_FB_OSD_FILLBLT _IOW(0xfb,53,osd_fillblt_t*)
+#define GFX_FB_OSD_ADVFILLBLT _IOW(0xfb,54,osd_afillblt_t*)
+#define GFX_FB_OSD_BLEND _IOW(0xfb,55,int*)
+#define GFX_FB_OSD_ADVBLEND _IOW(0xfb,56,int*)
+#define GFX_FB_OSD_RESIZE _IOW(0xfb,58,int*)
+#define GFX_FB_ENGINE_WAIT _IOW(0xfb,60,int)
+#define GFX_FB_RESET_ENGINE _IO(0xfb,61)
+#define GFX_FB_SET_ENGINE_MODE _IOW(0xfb,62,int)
+#define GFX_FB_GET_ENGINE_MODE _IO(0xfb,63)
+#define GFX_FB_GET_SFC_INFO _IO(0xfb,64,int*)
+#define GFX_FB_OSD_SFC_CLIP _IOW(0xfb,65,osd_clip_rec_t*)
+#define GFX_FB_OSD_COLOURKEY _IOW(0xfb,67,int*)
+#define GFX_FB_GET_SFC_PSEUDO _IOWR(0xfb,68,int*)
+
+class SurfaceMVP : public Surface
+{
+ public:
+ SurfaceMVP(int id = 0);
+ virtual ~SurfaceMVP();
+
+ int create(UINT width, UINT height);
+ void display();
+ unsigned long getSurfaceHandle();
+
+ int fillblt(int x, int y, int width, int height, const DrawStyle& c);
+
+ void drawHorzLine(int x1, int x2, int y, const DrawStyle& c);
+ void drawVertLine(int x, int y1, int y2, const DrawStyle& c);
+ void drawBitmap(int x, int y, const Bitmap& bm, const DisplayRegion& region);
+ 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);
+
+ static void initConversionTables()
+ {
+ initConversionTables(100,100,100);
+ }
+ //init tables with factors for R,G,B - each in percent
+ static void initConversionTables(int r, int g, int b);
+
+ int blt(int fd, unsigned long shandle, int sx, int sy, int width, int height, unsigned long dhandle, int dx, int dy);
+
+ private:
+ int fdOsd;
+ osd_surface_t surface;
+
+ void yuv2rgb(int y, int u, int v, unsigned char* pr, unsigned char* pg, unsigned char* pb);
+ /*
+ * rgb2yuv() - convert an RGB pixel to YUV
+ */
+ inline void rgb2yuv(unsigned char r, unsigned char g, unsigned char b, unsigned char *y, unsigned char *u, unsigned char *v)
+ {
+ int Y, U, V;
+
+ Y = conv_YB[b] + conv_YG[g] + conv_YR[r];
+ U = conv_UB[b] + conv_UG[g] + conv_UR[r] + 128;
+ V = conv_VB[b] + conv_VG[g] + conv_VR[r] + 128;
+
+ if (Y > 255)
+ Y = 255;
+ else if (Y < 0)
+ Y = 0;
+ if (U > 255)
+ U = 255;
+ else if (U < 0)
+ U = 0;
+ if (V > 255)
+ V = 255;
+ else if (V < 0)
+ V = 0;
+
+ *y = Y;
+ *u = U;
+ *v = V;
+ }
+
+
+ // Implicit inlines
+ void c2rgba(unsigned long c, unsigned char *r, unsigned char *g, unsigned char *b, unsigned char *a)
+ {
+ *a = (c & 0xff000000) >> 24;
+ *r = (c & 0x00ff0000) >> 16;
+ *g = (c & 0x0000ff00) >> 8;
+ *b = (c & 0x000000ff);
+ }
+
+ static int conv_YB[256];
+ static int conv_YG[256];
+ static int conv_YR[256];
+ static int conv_UB[256];
+ static int conv_UG[256];
+ static int conv_UR[256];
+ static int conv_VB[256];
+ static int conv_VG[256];
+ static int conv_VR[256];
+
+ static int conv_BY[256];
+ static int conv_GY[256];
+ static int conv_RY[256];
+ static int conv_BU[256];
+ static int conv_GU[256];
+ static int conv_RU[256];
+ static int conv_BV[256];
+ static int conv_GV[256];
+ static int conv_RV[256];
+ protected:
+ void drawPixel(int x, int y, unsigned int c, bool fastdraw=false);
+ void drawPixel(int x, int y, Colour& c, bool fastdraw=false);
+
+};
+
+#endif
-/*\r
- Copyright 2006 Marten Richter\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#include <stdlib.h>\r
-\r
-#include "surfaceopengl.h"\r
-#include "osdopengl.h"\r
-#include "bitmap.h"\r
-#include "log.h"\r
-#include "mutex.h"\r
-\r
-inline unsigned int InternalColour(unsigned int c){\r
- return (c &0x000000FF)<<16 |\r
- (c &0x0000FF00) |\r
- (c &0x00FF0000)>>16 |\r
- (c &0xFF000000);\r
-}\r
-\r
-SurfaceOpenGL::SurfaceOpenGL(int id)\r
-: Surface(id)\r
-{\r
- gltexture=0;\r
- data=NULL;\r
- sheight=swidth=0;\r
-// fastdraw=false;\r
- srf_mutex.Lock();\r
-}\r
-\r
-SurfaceOpenGL::~SurfaceOpenGL()\r
-{\r
- srf_mutex.Lock();\r
- if (data) {\r
- free(data);\r
- data=NULL;\r
- } else {\r
- glDeleteTextures(1,&gltexture);\r
- }\r
- srf_mutex.Unlock();\r
-}\r
-\r
-int SurfaceOpenGL::create(UINT width, UINT height)\r
-{\r
- OsdOpenGL* osd=((OsdOpenGL*)(Osd::getInstance()));\r
- //osd->BeginPainting();\r
-\r
-\r
- if (this == screen) { // We do not need locking here, since the osd calls this\r
-\r
- swidth=width;\r
- sheight=height;\r
- glGenTextures(1, &gltexture);\r
-\r
- glBindTexture(GL_TEXTURE_2D, gltexture);\r
-\r
- void *image = malloc(sheight * swidth * 4);\r
- memset(image, 0, sheight * swidth * 4);\r
- /* for (int i=0;i< sheight * swidth * 4; i++) { //fill it with garbage, useful for debugging\r
- ((char*)image)[i]=i%255;\r
- }*/\r
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, swidth, sheight, 0, GL_RGBA,\r
- GL_UNSIGNED_BYTE, image);\r
-\r
- } else {\r
- sheight = height;\r
- swidth = width;\r
- data=(char*)malloc(sizeof(uint32_t)*width*height);\r
- }\r
-\r
-\r
- //osd->EndPainting();\r
- srf_mutex.Unlock();\r
- return 1;\r
-}\r
-\r
-void SurfaceOpenGL::display()\r
-{\r
-}\r
-\r
-int SurfaceOpenGL::fillblt(int x, int y, int width, int height, const DrawStyle& c) {\r
- srf_mutex.Lock();\r
- //since this might be called before surface\r
- //allocation we will wait in this case, hopefully without deadlocks\r
- if (screen == this || !data ) {\r
- //This should not happen!\r
- srf_mutex.Unlock();\r
- return 0;\r
-\r
- }\r
- /* OsdWin* osd=((OsdWin*)(Osd::getInstance()));\r
-\r
- osd->BeginPainting();\r
-\r
- glViewport(0,0,swidth,sheight);*/\r
-\r
- //osd->EndPainting();\r
- srf_mutex.Unlock();\r
-\r
- unsigned int my_c=InternalColour(c.rgba());\r
-\r
- int iheight=height;\r
- if (height+y>sheight) iheight=sheight-y;\r
- int iwidth=width;\r
- if (width+x>swidth) iwidth=swidth-y;\r
-\r
- unsigned int line;\r
- unsigned int column;\r
- for (line = y; line < (iheight + y); line++) {\r
- uint32_t *row = ((unsigned int*) (((char*) data) + (swidth * line + x)\r
- * sizeof(uint32_t)));\r
- for (column = 0; column < iwidth; column++) {\r
- row[column] = my_c;\r
- }\r
- }\r
-\r
-/* if (d3dsurface->UnlockRect() != D3D_OK) {\r
- osd->EndPainting();\r
- return 0;\r
- }\r
- osd->EndPainting();*/\r
-\r
- return 0;\r
-}\r
-\r
-\r
-void SurfaceOpenGL::startFastDraw(){\r
- srf_mutex.Lock();\r
-\r
-}\r
-void SurfaceOpenGL::endFastDraw(){\r
- srf_mutex.Unlock();\r
- }\r
-\r
-void SurfaceOpenGL::drawPixel(int x, int y, Colour & colour, bool fastdraw) {\r
- int c = ( (0xFF000000 )\r
- | (colour.red << 16)\r
- | (colour.green << 8)\r
- | (colour.blue ) );\r
-\r
- drawPixel(x, y, c, fastdraw);\r
- }\r
-\r
-void SurfaceOpenGL::drawPixel(int x, int y, unsigned int c, bool fastdraw) {\r
- //FixMe: locking for every single Pixel will be painfully slow\r
- if (screen == this) {\r
- //This should not happen!\r
- return ;\r
- }\r
- if (!data) {\r
- return; //why does this happen\r
- }\r
- //OsdWin* osd;\r
- if (!fastdraw) {\r
- srf_mutex.Lock(); //since this might be called before surface\r
- }\r
- //allocation we will wait in this case, hopefully without deadlocks\r
-\r
- //osd = ((OsdWin*) (Osd::getInstance()));\r
-\r
- if (x >= swidth || y >= sheight)\r
- return; //do not draw outside the surface\r
-\r
- unsigned int my_c=InternalColour(c);\r
-\r
- unsigned int*row = (unsigned int*) (((char*) data + (swidth * y + x)\r
- * sizeof(uint32_t)));\r
- row[0] = my_c;\r
-\r
- if (!fastdraw) {\r
- srf_mutex.Unlock(); //since this might be called before surface\r
- }\r
-\r
-}\r
-\r
-void SurfaceOpenGL::drawHorzLine(int x1, int x2, int y, const DrawStyle& c)\r
-{\r
- fillblt(x1, y, x2-x1, 1, c);\r
-}\r
-\r
-void SurfaceOpenGL::drawVertLine(int x, int y1, int y2, const DrawStyle& c)\r
-{\r
- fillblt(x, y1, 1, y2-y1, c);\r
-}\r
-\r
-void SurfaceOpenGL::drawBitmap(int x, int y, const Bitmap& bm,const DisplayRegion & region) //region should not matter for SD\r
-{\r
- // Temporary code? Draw one pixel at a time using drawPixel()\r
- startFastDraw();\r
- for (UINT j = 0; j < bm.getHeight(); ++j)\r
- for (UINT i = 0; i < bm.getWidth(); ++i)\r
- drawPixel(x+i, y+j, bm.getColour(i,j),true);\r
- endFastDraw();\r
-}\r
-\r
-int SurfaceOpenGL::updateToScreen(int sx, int sy, int w, int h, int dx, int dy) // FIXME new, replace others with this FIXME\r
-{\r
-// Log::getInstance()->log("Surface", Log::WARN, "UTS Mark1");\r
- srf_mutex.Lock();//since this might be called before surface\r
- //allocation we will wait in this case, hopefully without deadlocks\r
-\r
- OsdOpenGL* osd=((OsdOpenGL*)(Osd::getInstance()));\r
- // Log::getInstance()->log("Surface", Log::WARN, "UTS Mark2");\r
- GLuint screengltexture=((SurfaceOpenGL*)screen)->getTexture();\r
-\r
- osd->BeginPainting();\r
- // Log::getInstance()->log("Surface", Log::WARN, "UTS Mark3");\r
- glBindTexture(GL_TEXTURE_2D, screengltexture);\r
-// Log::getInstance()->log("Surface", Log::WARN, "UTS Mark4 %d",glGetError());\r
-\r
- for (int y=0;y<h;y++) { //hopefully this is not too slow\r
- // Log::getInstance()->log("Surface", Log::WARN, "UTS Mark4a %d %d %d %d %d %d",sx,sy,w,h,dx,dy);\r
- glTexSubImage2D(GL_TEXTURE_2D,0,dx,(dy+y),w,1,GL_RGBA,GL_UNSIGNED_BYTE,\r
- data+((y+sy)*swidth+sx)*sizeof(uint32_t));\r
-\r
-\r
- }\r
-\r
- osd->EndPainting();\r
-\r
- srf_mutex.Unlock();\r
-\r
- return 0;\r
-}\r
-\r
-int SurfaceOpenGL::blt(int fd, unsigned long shandle, int sx, int sy, int width, int height, unsigned long dhandle, int dx, int dy)\r
-{\r
- //I don't see code using this function, so I skip it, since it is a MVP specific interface\r
- return 0;\r
-}\r
-\r
-void SurfaceOpenGL::screenShot(const char* fileName)\r
-{\r
- //Isn't this for debugging only, so I won't implement it yet\r
-}\r
-\r
-void SurfaceOpenGL::readPixel(int x, int y, unsigned char* r, unsigned char* g, unsigned char* b)\r
-{\r
- //Isn't this for debugging only, so I won't implement it yet\r
-}\r
-\r
-\r
-void SurfaceOpenGL::drawJpeg(const char *fileName,int x, int y,int *width, int *height){\r
-}/*\r
- WaitForSingleObject(event,INFINITE); //since this might be called before surface\r
- //allocation we will wait in this case, hopefully without deadlocks\r
- if (!d3dsurface) {\r
- return ; //why does this happen\r
- }\r
- OsdWin* osd=((OsdWin*)(Osd::getInstance()));\r
-\r
-\r
- D3DXIMAGE_INFO image_inf;\r
- osd->BeginPainting();\r
-// D3DXGetImageInfoFromFile(fileName,&image_inf);\r
- D3DXGetImageInfoFromResource(NULL,fileName,&image_inf);\r
- RECT dest_rec={x,y,x+image_inf.Width,\r
- y+image_inf.Height};\r
-/* if (D3DXLoadSurfaceFromFile(\r
- d3dsurface,\r
- NULL,\r
- &dest_rec,\r
- fileName,\r
- NULL,\r
- D3DX_FILTER_NONE,\r
- 0,\r
- &image_inf)!=D3D_OK) {\r
- Log::getInstance()->log("Surface", Log::DEBUG, "Could not open jpeg!");\r
-\r
- }*\r
- if (D3DXLoadSurfaceFromResource(\r
- d3dsurface,\r
- NULL,\r
- &dest_rec,\r
- NULL,\r
- fileName,\r
- NULL,\r
- D3DX_FILTER_NONE,\r
- 0,\r
- &image_inf)!=D3D_OK) {\r
- Log::getInstance()->log("Surface", Log::DEBUG, "Could not open jpeg!");\r
-\r
- }\r
- osd->EndPainting();\r
- *width=image_inf.Width;\r
- *height=image_inf.Height;\r
-\r
-}*/\r
-\r
-/*\r
-void SurfaceOpenGL::drawJpeg(char *buffer,ULONG buflength,DWORD x, DWORD y,DWORD *width, DWORD *height){\r
- WaitForSingleObject(event,INFINITE); //since this might be called before surface\r
- //allocation we will wait in this case, hopefully without deadlocks\r
- if (!d3dsurface) {\r
- return ; //why does this happen\r
- }\r
- OsdWin* osd=((OsdWin*)(Osd::getInstance()));\r
-\r
-\r
- D3DXIMAGE_INFO image_inf;\r
- osd->BeginPainting();\r
-// D3DXGetImageInfoFromFile(fileName,&image_inf);\r
- D3DXGetImageInfoFromFileInMemory((void*)buffer,buflength,&image_inf);\r
- RECT dest_rec={x,y,x+image_inf.Width,\r
- y+image_inf.Height};\r
-/* if (D3DXLoadSurfaceFromFile(\r
- d3dsurface,\r
- NULL,\r
- &dest_rec,\r
- fileName,\r
- NULL,\r
- D3DX_FILTER_NONE,\r
- 0,\r
- &image_inf)!=D3D_OK) {\r
- Log::getInstance()->log("Surface", Log::DEBUG, "Could not open jpeg!");\r
-\r
- }*/\r
-/* if (D3DXLoadSurfaceFromResource(\r
- d3dsurface,\r
- NULL,\r
- &dest_rec,\r
- NULL,\r
- fileName,\r
- NULL,\r
- D3DX_FILTER_NONE,\r
- 0,\r
- &image_inf)!=D3D_OK) {\r
- Log::getInstance()->log("Surface", Log::DEBUG, "Could not open jpeg!");\r
-\r
- }*\r
- if (D3DXLoadSurfaceFromFileInMemory(\r
- d3dsurface,\r
- NULL,\r
- &dest_rec,\r
- (void*)buffer,\r
- buflength,\r
- NULL,\r
- D3DX_FILTER_NONE,\r
- 0,\r
- &image_inf)!=D3D_OK) {\r
- Log::getInstance()->log("Surface", Log::DEBUG, "Could not open jpeg!");\r
-\r
- }\r
- osd->EndPainting();\r
- *width=image_inf.Width;\r
- *height=image_inf.Height;\r
-\r
-}*/\r
-\r
+/*
+ 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 <stdlib.h>
+
+#include "surfaceopengl.h"
+#include "osdopengl.h"
+#include "bitmap.h"
+#include "log.h"
+#include "mutex.h"
+
+inline unsigned int InternalColour(unsigned int c){
+ return (c &0x000000FF)<<16 |
+ (c &0x0000FF00) |
+ (c &0x00FF0000)>>16 |
+ (c &0xFF000000);
+}
+
+SurfaceOpenGL::SurfaceOpenGL(int id)
+: Surface(id)
+{
+ gltexture=0;
+ data=NULL;
+ sheight=swidth=0;
+// fastdraw=false;
+ srf_mutex.Lock();
+}
+
+SurfaceOpenGL::~SurfaceOpenGL()
+{
+ srf_mutex.Lock();
+ if (data) {
+ free(data);
+ data=NULL;
+ } else {
+ glDeleteTextures(1,&gltexture);
+ }
+ srf_mutex.Unlock();
+}
+
+int SurfaceOpenGL::create(UINT width, UINT height)
+{
+ OsdOpenGL* osd=((OsdOpenGL*)(Osd::getInstance()));
+ //osd->BeginPainting();
+
+
+ if (this == screen) { // We do not need locking here, since the osd calls this
+
+ swidth=width;
+ sheight=height;
+ glGenTextures(1, &gltexture);
+
+ glBindTexture(GL_TEXTURE_2D, gltexture);
+
+ void *image = malloc(sheight * swidth * 4);
+ memset(image, 0, sheight * swidth * 4);
+ /* for (int i=0;i< sheight * swidth * 4; i++) { //fill it with garbage, useful for debugging
+ ((char*)image)[i]=i%255;
+ }*/
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, swidth, sheight, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, image);
+
+ } else {
+ sheight = height;
+ swidth = width;
+ data=(char*)malloc(sizeof(uint32_t)*width*height);
+ }
+
+
+ //osd->EndPainting();
+ srf_mutex.Unlock();
+ return 1;
+}
+
+void SurfaceOpenGL::display()
+{
+}
+
+int SurfaceOpenGL::fillblt(int x, int y, int width, int height, const DrawStyle& c) {
+ srf_mutex.Lock();
+ //since this might be called before surface
+ //allocation we will wait in this case, hopefully without deadlocks
+ if (screen == this || !data ) {
+ //This should not happen!
+ srf_mutex.Unlock();
+ return 0;
+
+ }
+ /* OsdWin* osd=((OsdWin*)(Osd::getInstance()));
+
+ osd->BeginPainting();
+
+ glViewport(0,0,swidth,sheight);*/
+
+ //osd->EndPainting();
+ srf_mutex.Unlock();
+
+ unsigned int my_c=InternalColour(c.rgba());
+
+ int iheight=height;
+ if (height+y>sheight) iheight=sheight-y;
+ int iwidth=width;
+ if (width+x>swidth) iwidth=swidth-y;
+
+ unsigned int line;
+ unsigned int column;
+ for (line = y; line < (iheight + y); line++) {
+ uint32_t *row = ((unsigned int*) (((char*) data) + (swidth * line + x)
+ * sizeof(uint32_t)));
+ for (column = 0; column < iwidth; column++) {
+ row[column] = my_c;
+ }
+ }
+
+/* if (d3dsurface->UnlockRect() != D3D_OK) {
+ osd->EndPainting();
+ return 0;
+ }
+ osd->EndPainting();*/
+
+ return 0;
+}
+
+
+void SurfaceOpenGL::startFastDraw(){
+ srf_mutex.Lock();
+
+}
+void SurfaceOpenGL::endFastDraw(){
+ srf_mutex.Unlock();
+ }
+
+void SurfaceOpenGL::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 SurfaceOpenGL::drawPixel(int x, int y, unsigned int c, bool fastdraw) {
+ //FixMe: locking for every single Pixel will be painfully slow
+ if (screen == this) {
+ //This should not happen!
+ return ;
+ }
+ if (!data) {
+ return; //why does this happen
+ }
+ //OsdWin* osd;
+ if (!fastdraw) {
+ srf_mutex.Lock(); //since this might be called before surface
+ }
+ //allocation we will wait in this case, hopefully without deadlocks
+
+ //osd = ((OsdWin*) (Osd::getInstance()));
+
+ if (x >= swidth || y >= sheight)
+ return; //do not draw outside the surface
+
+ unsigned int my_c=InternalColour(c);
+
+ unsigned int*row = (unsigned int*) (((char*) data + (swidth * y + x)
+ * sizeof(uint32_t)));
+ row[0] = my_c;
+
+ if (!fastdraw) {
+ srf_mutex.Unlock(); //since this might be called before surface
+ }
+
+}
+
+void SurfaceOpenGL::drawHorzLine(int x1, int x2, int y, const DrawStyle& c)
+{
+ fillblt(x1, y, x2-x1, 1, c);
+}
+
+void SurfaceOpenGL::drawVertLine(int x, int y1, int y2, const DrawStyle& c)
+{
+ fillblt(x, y1, 1, y2-y1, c);
+}
+
+void SurfaceOpenGL::drawBitmap(int x, int y, const Bitmap& bm,const DisplayRegion & region) //region should not matter for SD
+{
+ // 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 SurfaceOpenGL::updateToScreen(int sx, int sy, int w, int h, int dx, int dy) // FIXME new, replace others with this FIXME
+{
+// Log::getInstance()->log("Surface", Log::WARN, "UTS Mark1");
+ srf_mutex.Lock();//since this might be called before surface
+ //allocation we will wait in this case, hopefully without deadlocks
+
+ OsdOpenGL* osd=((OsdOpenGL*)(Osd::getInstance()));
+ // Log::getInstance()->log("Surface", Log::WARN, "UTS Mark2");
+ GLuint screengltexture=((SurfaceOpenGL*)screen)->getTexture();
+
+ osd->BeginPainting();
+ // Log::getInstance()->log("Surface", Log::WARN, "UTS Mark3");
+ glBindTexture(GL_TEXTURE_2D, screengltexture);
+// Log::getInstance()->log("Surface", Log::WARN, "UTS Mark4 %d",glGetError());
+
+ for (int y=0;y<h;y++) { //hopefully this is not too slow
+ // Log::getInstance()->log("Surface", Log::WARN, "UTS Mark4a %d %d %d %d %d %d",sx,sy,w,h,dx,dy);
+ glTexSubImage2D(GL_TEXTURE_2D,0,dx,(dy+y),w,1,GL_RGBA,GL_UNSIGNED_BYTE,
+ data+((y+sy)*swidth+sx)*sizeof(uint32_t));
+
+
+ }
+
+ osd->EndPainting();
+
+ srf_mutex.Unlock();
+
+ return 0;
+}
+
+int SurfaceOpenGL::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 SurfaceOpenGL::screenShot(const char* fileName)
+{
+ //Isn't this for debugging only, so I won't implement it yet
+}
+
+void SurfaceOpenGL::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 SurfaceOpenGL::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 SurfaceOpenGL::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;
+
+}*/
+
-/*\r
- Copyright 2006, 2011-2012 Marten Richter\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef SURFACEOPENGL_H\r
-#define SURFACEOPENGL_H\r
-\r
-#include "defines.h"\r
-#include "surface.h"\r
-#include "mutex.h"\r
-#include <GLES2/gl2.h>\r
-\r
-class SurfaceOpenGL : public Surface\r
-{\r
- public:\r
- SurfaceOpenGL(int id = 0);\r
- virtual ~SurfaceOpenGL();\r
-\r
- int create(UINT width, UINT height);\r
- void display();\r
-\r
- void startFastDraw();\r
- void endFastDraw();\r
-\r
- int fillblt(int x, int y, int width, int height, const DrawStyle& c);\r
-\r
- void drawHorzLine(int x1, int x2, int y, const DrawStyle& c);\r
- void drawVertLine(int x, int y1, int y2, const DrawStyle& c);\r
- void drawBitmap(int x, int y, const Bitmap& bm,const DisplayRegion & region);\r
- int updateToScreen(int sx, int sy, int w, int h, int dx, int dy);\r
- void readPixel(int x, int y, unsigned char* r, unsigned char* g, unsigned char* b);\r
- void screenShot(const char* fileName);\r
- void ReleaseSurface();\r
- int blt(int fd, unsigned long shandle, int sx, int sy, int width, int height, unsigned long dhandle, int dx, int dy);\r
- void drawJpeg(const char *fileName,int x, int y,int *width, int *height);\r
-/* void drawJpeg(char *buffer,ULONG buflength,DWORD x, DWORD y,DWORD *width, DWORD *height);*/\r
-\r
- GLuint getTexture() {return gltexture;};\r
-\r
-\r
-\r
- private:\r
- GLuint gltexture;\r
- UINT sheight,swidth;\r
- char * data;\r
-\r
-\r
-\r
- Mutex srf_mutex;\r
- protected:\r
- void drawPixel(int x, int y, Colour& c, bool fastdraw=false);\r
- void drawPixel(int x, int y, unsigned int c, bool fastdraw=false);\r
-\r
-};\r
-\r
-#endif\r
+/*
+ Copyright 2006, 2011-2012 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 SURFACEOPENGL_H
+#define SURFACEOPENGL_H
+
+#include "defines.h"
+#include "surface.h"
+#include "mutex.h"
+#include <GLES2/gl2.h>
+
+class SurfaceOpenGL : public Surface
+{
+ public:
+ SurfaceOpenGL(int id = 0);
+ virtual ~SurfaceOpenGL();
+
+ int create(UINT width, UINT height);
+ void display();
+
+ void startFastDraw();
+ void endFastDraw();
+
+ int fillblt(int x, int y, int width, int height, const DrawStyle& c);
+
+ void drawHorzLine(int x1, int x2, int y, const DrawStyle& c);
+ void drawVertLine(int x, int y1, int y2, const DrawStyle& c);
+ void drawBitmap(int x, int y, const Bitmap& bm,const DisplayRegion & region);
+ 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);*/
+
+ GLuint getTexture() {return gltexture;};
+
+
+
+ private:
+ GLuint gltexture;
+ UINT sheight,swidth;
+ char * data;
+
+
+
+ Mutex srf_mutex;
+ protected:
+ void drawPixel(int x, int y, Colour& c, bool fastdraw=false);
+ void drawPixel(int x, int y, unsigned int c, bool fastdraw=false);
+
+};
+
+#endif
-/*\r
- Copyright 2012 Marten Richter\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#include "surfacevector.h"\r
-#include "bitmap.h"\r
-#include <wchar.h>\r
-#include <stdlib.h>\r
-\r
-SurfaceVector::SurfaceVector(OsdVector* vosd)\r
-{\r
-\r
- osd=vosd;\r
-}\r
-\r
-SurfaceVector::~SurfaceVector()\r
-{\r
- osd->removeSurface(this);\r
- list<SVGCommand>::iterator itty=commands.begin();\r
- while (itty!=commands.end())\r
- {\r
- osd->removeStyleRef((*itty).getRef()); // We remove the Style reference, so that osd can free stuff\r
- ImageIndex ii=(*itty).getImageIndex();\r
- if (ii) osd->removeImageRef(ii);\r
- itty++;\r
- }\r
-}\r
-\r
-\r
-int SurfaceVector::getFontHeight()\r
-{\r
- return osd->getFontHeight();\r
-}\r
-\r
-float SurfaceVector::getCharWidth(wchar_t c)\r
-{\r
- return osd->getCharWidth(c);\r
-}\r
-\r
-wchar_t SurfaceVector::getWChar(const char* str, unsigned int *length)\r
-{\r
- int mlength=0;\r
- int max_length=4;\r
- wchar_t tempo[1];\r
- size_t num_bytes=1;\r
- if (str[0]=='\0') {\r
- *length=1;\r
- return '\0';\r
- } else if (str[1]=='\0'){\r
- max_length=2;\r
- } else if (str[2]=='\0'){\r
- max_length=3;\r
- }\r
- mbstate_t state;\r
- memset((void*)&state,0,sizeof(state));\r
- num_bytes=mbrtowc(tempo, str, max_length, &state);\r
- if (num_bytes!=((size_t) -1) && num_bytes!=((size_t) -2))\r
- {\r
- *length=num_bytes;\r
- return *tempo;\r
- }\r
- *length=1;\r
- return '?';\r
-}\r
-\r
-\r
-int SurfaceVector::drawText(const char* text, int x, int y, const DrawStyle& c){\r
- return drawText(text, x, y, 0, c);\r
-}\r
-\r
-int SurfaceVector::drawText(const char* text, int x, int y, int width, const DrawStyle& c)\r
-{\r
- float shift=0.;\r
- const char *run=text;\r
- mbstate_t state;\r
- wchar_t tempo[1];\r
- size_t num_bytes=1;\r
- size_t length=strlen(text);\r
- memset((void*)&state,0,sizeof(state));\r
- command_mutex.Lock();\r
- num_bytes=mbrtowc(tempo, run, length, &state);\r
- while (num_bytes!=((size_t) -1) && num_bytes!=((size_t) -2) && length>0)\r
- {\r
- unsigned int ref=osd->getStyleRef(c);\r
- commands.push_back(SVGCommand(x+shift,y,*tempo,ref));\r
- shift+=osd->getCharWidth(*tempo);\r
- length -= num_bytes;\r
- run += num_bytes;\r
- if (shift>width && width >0) {\r
- command_mutex.Unlock();\r
- return 1;\r
- }\r
- num_bytes=mbrtowc(tempo, run, length, &state);\r
- }\r
- command_mutex.Unlock();\r
- return 1;\r
-\r
-}\r
-int SurfaceVector::drawTextRJ(const char* text, int x, int y, const DrawStyle& c)\r
-{\r
- float shift=0.;\r
- const char *run=text;\r
- mbstate_t state;\r
- wchar_t tempo[1];\r
- size_t num_bytes=1;\r
- size_t length=strlen(text);\r
- memset((void*)&state,0,sizeof(state));\r
-\r
- num_bytes=mbrtowc(tempo, run, length, &state);\r
- while (num_bytes!=((size_t) -1) && num_bytes!=((size_t) -2) && length>0)\r
- {\r
- shift+=osd->getCharWidth(*tempo);\r
- length -= num_bytes;\r
- run += num_bytes;\r
- num_bytes=mbrtowc(tempo, run, length, &state);\r
- }\r
- return drawText(text, x-shift, y, c);\r
-}\r
-\r
-int SurfaceVector::drawTextCentre(const char* text, int x, int y, const DrawStyle& c)\r
-{\r
- float shift=0;\r
- const char *run=text;\r
- mbstate_t state;\r
- wchar_t tempo[1];\r
- size_t num_bytes=1;\r
- size_t length=strlen(text);\r
- memset((void*)&state,0,sizeof(state));\r
-\r
- num_bytes=mbrtowc(tempo, run, length, &state);\r
- while (num_bytes!=((size_t) -1) && num_bytes!=((size_t) -2) && length>0)\r
- {\r
- shift+=osd->getCharWidth(*tempo);\r
- length -= num_bytes;\r
- run += num_bytes;\r
- num_bytes=mbrtowc(tempo, run, length, &state);\r
- }\r
- return drawText(text, x-shift/2., y, c);\r
-}\r
-\r
-void SurfaceVector::drawJpeg(const char *fileName,int x, int y,int *width, int *height)\r
-{\r
- command_mutex.Lock();\r
- ImageIndex image=osd->getJpegRef(fileName,width,height);\r
- commands.push_back(SVGCommand(x,y,*width,*height,image,0));\r
- command_mutex.Unlock();\r
-}\r
-\r
-int SurfaceVector::create(UINT width, UINT height)\r
-{\r
- sheight=height;\r
- swidth=width;\r
- return 1;\r
-}\r
-void SurfaceVector::display()\r
-{\r
- //nothing this is really mvp specific\r
-}\r
-\r
-int SurfaceVector::fillblt(int x, int y, int width, int height, const DrawStyle& c)\r
-{\r
- command_mutex.Lock();\r
- removeCommands(x,y,width,height); // remove commands below the box\r
- unsigned int ref=osd->getStyleRef(c);\r
- commands.push_back(SVGCommand(x,y,width,height,Rectangle,ref));\r
- command_mutex.Unlock();\r
- return 1;\r
-\r
-}\r
-void SurfaceVector::drawHorzLine(int x1, int x2, int y, const DrawStyle& c)\r
-{\r
- command_mutex.Lock();\r
- unsigned int ref=osd->getStyleRef(c);\r
- commands.push_back(SVGCommand(x1,y,x2-x1,1,HorzLine,ref));\r
- command_mutex.Unlock();\r
-}\r
-\r
-void SurfaceVector::drawVertLine(int x, int y1, int y2, const DrawStyle& c){\r
- command_mutex.Lock();\r
- unsigned int ref=osd->getStyleRef(c);\r
- commands.push_back(SVGCommand(x,y1,1,y2-y1,VertLine,ref));\r
- command_mutex.Unlock();\r
-}\r
-\r
-void SurfaceVector::drawBitmap(int x, int y, const Bitmap& bm,const DisplayRegion & region)\r
-{\r
- //this is complicated\r
- command_mutex.Lock();\r
-/*\r
- unsigned int * data=(unsigned int*)malloc(sizeof(unsigned int)*bm.getWidth()*bm.getHeight());\r
- for (UINT j = 0; j < bm.getHeight(); ++j){\r
- for (UINT i = 0; i < bm.getWidth(); ++i)\r
- {\r
- data[i+j*bm.getHeight()]=bm.getColour(i,j);\r
- }\r
- }*/\r
- ImageIndex image=osd->getImagePalette(bm.getWidth(),bm.getHeight(),&(bm.rawData()[0]),\r
- (const unsigned int*)&bm.palette.getColourVector()[0]); // data is freed by the OSD\r
- //free(data);\r
- float tx=x+region.windowx;\r
- float ty=y+region.windowy;\r
- float th=bm.getHeight();\r
- float tw=bm.getWidth();\r
-\r
- float scalex=720.f/((float) (region.framewidth+1));\r
- float scaley=576.f/((float) (region.frameheight+1));\r
- tx*=scalex;\r
- ty*=scaley;\r
- tw*=scalex;\r
- th*=scaley;\r
- SVGCommand temp=SVGCommand(tx,ty,tw,th,image,0);\r
- commands.push_back(temp);\r
- command_mutex.Unlock();\r
-}\r
-\r
-void SurfaceVector::drawPoint(int x, int y, DrawStyle& c, bool fastdraw){\r
- if (!fastdraw) command_mutex.Lock();\r
- unsigned int ref=osd->getStyleRef(c);\r
- commands.push_back(SVGCommand(x,y,1,1,Point,ref));\r
- if (!fastdraw) command_mutex.Unlock();\r
-}\r
-void SurfaceVector::drawMonoBitmap(UCHAR* base, int dx, int dy, unsigned int height,unsigned int width, Colour& nextColour)\r
-{\r
- command_mutex.Lock();\r
- ImageIndex image=osd->getMonoBitmapRef(base,width,height);\r
- unsigned int ref=osd->getColorRef(nextColour);\r
- commands.push_back(SVGCommand(dx,dy,height,width,image,ref));\r
- command_mutex.Unlock();\r
-}\r
-\r
-\r
-int SurfaceVector::removeCommands(float x,float y,float width,float height)\r
-{\r
- // we iterate through all old commands in order to remove commands hidden by this rectangle\r
- list<SVGCommand>::iterator itty=commands.begin();\r
- while (itty!=commands.end())\r
- {\r
- if ((*itty).Test(x,y,width,height) ) {\r
- osd->removeStyleRef((*itty).getRef()); // We remove the Style reference, so that osd can free stuff\r
- ImageIndex ii=(*itty).getImageIndex();\r
- if (ii) osd->removeImageRef(ii);\r
- itty=commands.erase(itty);\r
- } else {\r
- itty++;\r
- }\r
- }\r
- return 1;\r
-\r
-}\r
-\r
-int SurfaceVector::updateToScreen(int sx, int sy, int w, int h, int dx, int dy)\r
-{\r
- // ok this method really works in a pixel oriented way\r
- command_mutex.Lock();\r
- osd->updateOrAddSurface(this,dx-sx,dy-sy,swidth,sheight,commands);\r
- command_mutex.Unlock();\r
- return 1;\r
-}\r
-\r
-\r
-/* This is for systems which need a locking of the drawing surface to speed up drawing */\r
-void SurfaceVector::startFastDraw() {\r
- command_mutex.Lock();\r
-}\r
-void SurfaceVector::endFastDraw() {\r
- command_mutex.Unlock();\r
-}\r
-\r
-\r
-void SurfaceVector::drawTTChar(int ox, int oy,int x, int y, cTeletextChar c)\r
-{\r
- command_mutex.Lock();\r
- list<SVGCommand>::iterator itty=commands.begin();\r
- while (itty!=commands.end())\r
- {\r
- if ((*itty).TTTest(ox,oy,x,y) ) {\r
- itty=commands.erase(itty);\r
- break;\r
- } else {\r
- itty++;\r
- }\r
- }\r
- commands.push_back(SVGCommand(ox,oy,x,y,c.getInternal()));\r
- command_mutex.Unlock();\r
-}\r
+/*
+ Copyright 2012 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 "surfacevector.h"
+#include "bitmap.h"
+#include <wchar.h>
+#include <stdlib.h>
+
+SurfaceVector::SurfaceVector(OsdVector* vosd)
+{
+
+ osd=vosd;
+}
+
+SurfaceVector::~SurfaceVector()
+{
+ osd->removeSurface(this);
+ list<SVGCommand>::iterator itty=commands.begin();
+ while (itty!=commands.end())
+ {
+ osd->removeStyleRef((*itty).getRef()); // We remove the Style reference, so that osd can free stuff
+ ImageIndex ii=(*itty).getImageIndex();
+ if (ii) osd->removeImageRef(ii);
+ itty++;
+ }
+}
+
+
+int SurfaceVector::getFontHeight()
+{
+ return osd->getFontHeight();
+}
+
+float SurfaceVector::getCharWidth(wchar_t c)
+{
+ return osd->getCharWidth(c);
+}
+
+wchar_t SurfaceVector::getWChar(const char* str, unsigned int *length)
+{
+ int mlength=0;
+ int max_length=4;
+ wchar_t tempo[1];
+ size_t num_bytes=1;
+ if (str[0]=='\0') {
+ *length=1;
+ return '\0';
+ } else if (str[1]=='\0'){
+ max_length=2;
+ } else if (str[2]=='\0'){
+ max_length=3;
+ }
+ mbstate_t state;
+ memset((void*)&state,0,sizeof(state));
+ num_bytes=mbrtowc(tempo, str, max_length, &state);
+ if (num_bytes!=((size_t) -1) && num_bytes!=((size_t) -2))
+ {
+ *length=num_bytes;
+ return *tempo;
+ }
+ *length=1;
+ return '?';
+}
+
+
+int SurfaceVector::drawText(const char* text, int x, int y, const DrawStyle& c){
+ return drawText(text, x, y, 0, c);
+}
+
+int SurfaceVector::drawText(const char* text, int x, int y, int width, const DrawStyle& c)
+{
+ float shift=0.;
+ const char *run=text;
+ mbstate_t state;
+ wchar_t tempo[1];
+ size_t num_bytes=1;
+ size_t length=strlen(text);
+ memset((void*)&state,0,sizeof(state));
+ command_mutex.Lock();
+ num_bytes=mbrtowc(tempo, run, length, &state);
+ while (num_bytes!=((size_t) -1) && num_bytes!=((size_t) -2) && length>0)
+ {
+ unsigned int ref=osd->getStyleRef(c);
+ commands.push_back(SVGCommand(x+shift,y,*tempo,ref));
+ shift+=osd->getCharWidth(*tempo);
+ length -= num_bytes;
+ run += num_bytes;
+ if (shift>width && width >0) {
+ command_mutex.Unlock();
+ return 1;
+ }
+ num_bytes=mbrtowc(tempo, run, length, &state);
+ }
+ command_mutex.Unlock();
+ return 1;
+
+}
+int SurfaceVector::drawTextRJ(const char* text, int x, int y, const DrawStyle& c)
+{
+ float shift=0.;
+ const char *run=text;
+ mbstate_t state;
+ wchar_t tempo[1];
+ size_t num_bytes=1;
+ size_t length=strlen(text);
+ memset((void*)&state,0,sizeof(state));
+
+ num_bytes=mbrtowc(tempo, run, length, &state);
+ while (num_bytes!=((size_t) -1) && num_bytes!=((size_t) -2) && length>0)
+ {
+ shift+=osd->getCharWidth(*tempo);
+ length -= num_bytes;
+ run += num_bytes;
+ num_bytes=mbrtowc(tempo, run, length, &state);
+ }
+ return drawText(text, x-shift, y, c);
+}
+
+int SurfaceVector::drawTextCentre(const char* text, int x, int y, const DrawStyle& c)
+{
+ float shift=0;
+ const char *run=text;
+ mbstate_t state;
+ wchar_t tempo[1];
+ size_t num_bytes=1;
+ size_t length=strlen(text);
+ memset((void*)&state,0,sizeof(state));
+
+ num_bytes=mbrtowc(tempo, run, length, &state);
+ while (num_bytes!=((size_t) -1) && num_bytes!=((size_t) -2) && length>0)
+ {
+ shift+=osd->getCharWidth(*tempo);
+ length -= num_bytes;
+ run += num_bytes;
+ num_bytes=mbrtowc(tempo, run, length, &state);
+ }
+ return drawText(text, x-shift/2., y, c);
+}
+
+void SurfaceVector::drawJpeg(const char *fileName,int x, int y,int *width, int *height)
+{
+ command_mutex.Lock();
+ ImageIndex image=osd->getJpegRef(fileName,width,height);
+ commands.push_back(SVGCommand(x,y,*width,*height,image,0));
+ command_mutex.Unlock();
+}
+
+int SurfaceVector::create(UINT width, UINT height)
+{
+ sheight=height;
+ swidth=width;
+ return 1;
+}
+void SurfaceVector::display()
+{
+ //nothing this is really mvp specific
+}
+
+int SurfaceVector::fillblt(int x, int y, int width, int height, const DrawStyle& c)
+{
+ command_mutex.Lock();
+ removeCommands(x,y,width,height); // remove commands below the box
+ unsigned int ref=osd->getStyleRef(c);
+ commands.push_back(SVGCommand(x,y,width,height,Rectangle,ref));
+ command_mutex.Unlock();
+ return 1;
+
+}
+void SurfaceVector::drawHorzLine(int x1, int x2, int y, const DrawStyle& c)
+{
+ command_mutex.Lock();
+ unsigned int ref=osd->getStyleRef(c);
+ commands.push_back(SVGCommand(x1,y,x2-x1,1,HorzLine,ref));
+ command_mutex.Unlock();
+}
+
+void SurfaceVector::drawVertLine(int x, int y1, int y2, const DrawStyle& c){
+ command_mutex.Lock();
+ unsigned int ref=osd->getStyleRef(c);
+ commands.push_back(SVGCommand(x,y1,1,y2-y1,VertLine,ref));
+ command_mutex.Unlock();
+}
+
+void SurfaceVector::drawBitmap(int x, int y, const Bitmap& bm,const DisplayRegion & region)
+{
+ //this is complicated
+ command_mutex.Lock();
+/*
+ unsigned int * data=(unsigned int*)malloc(sizeof(unsigned int)*bm.getWidth()*bm.getHeight());
+ for (UINT j = 0; j < bm.getHeight(); ++j){
+ for (UINT i = 0; i < bm.getWidth(); ++i)
+ {
+ data[i+j*bm.getHeight()]=bm.getColour(i,j);
+ }
+ }*/
+ ImageIndex image=osd->getImagePalette(bm.getWidth(),bm.getHeight(),&(bm.rawData()[0]),
+ (const unsigned int*)&bm.palette.getColourVector()[0]); // data is freed by the OSD
+ //free(data);
+ float tx=x+region.windowx;
+ float ty=y+region.windowy;
+ float th=bm.getHeight();
+ float tw=bm.getWidth();
+
+ float scalex=720.f/((float) (region.framewidth+1));
+ float scaley=576.f/((float) (region.frameheight+1));
+ tx*=scalex;
+ ty*=scaley;
+ tw*=scalex;
+ th*=scaley;
+ SVGCommand temp=SVGCommand(tx,ty,tw,th,image,0);
+ commands.push_back(temp);
+ command_mutex.Unlock();
+}
+
+void SurfaceVector::drawPoint(int x, int y, DrawStyle& c, bool fastdraw){
+ if (!fastdraw) command_mutex.Lock();
+ unsigned int ref=osd->getStyleRef(c);
+ commands.push_back(SVGCommand(x,y,1,1,Point,ref));
+ if (!fastdraw) command_mutex.Unlock();
+}
+void SurfaceVector::drawMonoBitmap(UCHAR* base, int dx, int dy, unsigned int height,unsigned int width, Colour& nextColour)
+{
+ command_mutex.Lock();
+ ImageIndex image=osd->getMonoBitmapRef(base,width,height);
+ unsigned int ref=osd->getColorRef(nextColour);
+ commands.push_back(SVGCommand(dx,dy,height,width,image,ref));
+ command_mutex.Unlock();
+}
+
+
+int SurfaceVector::removeCommands(float x,float y,float width,float height)
+{
+ // we iterate through all old commands in order to remove commands hidden by this rectangle
+ list<SVGCommand>::iterator itty=commands.begin();
+ while (itty!=commands.end())
+ {
+ if ((*itty).Test(x,y,width,height) ) {
+ osd->removeStyleRef((*itty).getRef()); // We remove the Style reference, so that osd can free stuff
+ ImageIndex ii=(*itty).getImageIndex();
+ if (ii) osd->removeImageRef(ii);
+ itty=commands.erase(itty);
+ } else {
+ itty++;
+ }
+ }
+ return 1;
+
+}
+
+int SurfaceVector::updateToScreen(int sx, int sy, int w, int h, int dx, int dy)
+{
+ // ok this method really works in a pixel oriented way
+ command_mutex.Lock();
+ osd->updateOrAddSurface(this,dx-sx,dy-sy,swidth,sheight,commands);
+ command_mutex.Unlock();
+ return 1;
+}
+
+
+/* This is for systems which need a locking of the drawing surface to speed up drawing */
+void SurfaceVector::startFastDraw() {
+ command_mutex.Lock();
+}
+void SurfaceVector::endFastDraw() {
+ command_mutex.Unlock();
+}
+
+
+void SurfaceVector::drawTTChar(int ox, int oy,int x, int y, cTeletextChar c)
+{
+ command_mutex.Lock();
+ list<SVGCommand>::iterator itty=commands.begin();
+ while (itty!=commands.end())
+ {
+ if ((*itty).TTTest(ox,oy,x,y) ) {
+ itty=commands.erase(itty);
+ break;
+ } else {
+ itty++;
+ }
+ }
+ commands.push_back(SVGCommand(ox,oy,x,y,c.getInternal()));
+ command_mutex.Unlock();
+}
-/*\r
- Copyright 2012 Marten Richter\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef SURFACEVECTOR_H\r
-#define SURFACEVECTOR_H\r
-\r
-#include "surface.h"\r
-#include "osdvector.h"\r
-\r
-#include <list>\r
-\r
-\r
-class SurfaceVector : public Surface\r
-{\r
- public:\r
- SurfaceVector(OsdVector* vosd);\r
- virtual ~SurfaceVector();\r
-\r
- int getFontHeight();\r
- float getCharWidth(wchar_t c);\r
- wchar_t getWChar(const char* str, unsigned int *length);\r
-\r
- int drawText(const char* text, int x, int y, const DrawStyle& c);\r
- int drawText(const char* text, int x, int y, int width, const DrawStyle& c);\r
- int drawTextRJ(const char* text, int x, int y, const DrawStyle& c);\r
- int drawTextCentre(const char* text, int x, int y, const DrawStyle& c);\r
-\r
- void drawJpeg(const char *fileName,int x, int y,int *width, int *height);\r
-\r
- int create(UINT width, UINT height);\r
- void display();\r
-\r
- int fillblt(int x, int y, int width, int height, const DrawStyle& c);\r
- void drawHorzLine(int x1, int x2, int y, const DrawStyle& c);\r
- void drawVertLine(int x, int y1, int y2, const DrawStyle& c);\r
- void drawBitmap(int x, int y, const Bitmap& bm,const DisplayRegion & region);\r
- void drawPoint(int x, int y, DrawStyle& c, bool fastdraw=false); // This draws a point, must not be a pixel\r
- void drawMonoBitmap(UCHAR* base, int dx, int dy, unsigned int height,unsigned int width, Colour& nextColour);\r
- int updateToScreen(int sx, int sy, int w, int h, int dx, int dy);\r
-\r
-\r
- /* This is for system which need a locking of the drawing surface to speed up drawing */\r
- void startFastDraw();\r
- void endFastDraw() ;\r
-\r
-\r
- void drawTTChar(int ox, int oy,int x, int y, cTeletextChar c);\r
-\r
- void readPixel(int x, int y, unsigned char* r, unsigned char* g, unsigned char* b){};\r
- void screenShot(const char* fileName){};\r
-\r
- protected:\r
-\r
-\r
- int removeCommands(float x,float y,float width,float height);\r
-\r
- list<SVGCommand> commands;\r
- int swidth, sheight;\r
- Mutex command_mutex;\r
- OsdVector* osd;\r
-\r
- void drawPixel(int x, int y, unsigned int c, bool fastdraw){}; // these are not supported!\r
- void drawPixel(int x, int y, Colour& c, bool fastdraw){};\r
-\r
-\r
-};\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-#endif\r
+/*
+ Copyright 2012 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 SURFACEVECTOR_H
+#define SURFACEVECTOR_H
+
+#include "surface.h"
+#include "osdvector.h"
+
+#include <list>
+
+
+class SurfaceVector : public Surface
+{
+ public:
+ SurfaceVector(OsdVector* vosd);
+ virtual ~SurfaceVector();
+
+ int getFontHeight();
+ float getCharWidth(wchar_t c);
+ wchar_t getWChar(const char* str, unsigned int *length);
+
+ int drawText(const char* text, int x, int y, const DrawStyle& c);
+ int drawText(const char* text, int x, int y, int width, const DrawStyle& c);
+ int drawTextRJ(const char* text, int x, int y, const DrawStyle& c);
+ int drawTextCentre(const char* text, int x, int y, const DrawStyle& c);
+
+ void drawJpeg(const char *fileName,int x, int y,int *width, int *height);
+
+ int create(UINT width, UINT height);
+ void display();
+
+ int fillblt(int x, int y, int width, int height, const DrawStyle& c);
+ void drawHorzLine(int x1, int x2, int y, const DrawStyle& c);
+ void drawVertLine(int x, int y1, int y2, const DrawStyle& c);
+ void drawBitmap(int x, int y, const Bitmap& bm,const DisplayRegion & region);
+ void drawPoint(int x, int y, DrawStyle& c, bool fastdraw=false); // This draws a point, must not be a pixel
+ void drawMonoBitmap(UCHAR* base, int dx, int dy, unsigned int height,unsigned int width, Colour& nextColour);
+ int updateToScreen(int sx, int sy, int w, int h, int dx, int dy);
+
+
+ /* This is for system which need a locking of the drawing surface to speed up drawing */
+ void startFastDraw();
+ void endFastDraw() ;
+
+
+ void drawTTChar(int ox, int oy,int x, int y, cTeletextChar c);
+
+ void readPixel(int x, int y, unsigned char* r, unsigned char* g, unsigned char* b){};
+ void screenShot(const char* fileName){};
+
+ protected:
+
+
+ int removeCommands(float x,float y,float width,float height);
+
+ list<SVGCommand> commands;
+ int swidth, sheight;
+ Mutex command_mutex;
+ OsdVector* osd;
+
+ void drawPixel(int x, int y, unsigned int c, bool fastdraw){}; // these are not supported!
+ void drawPixel(int x, int y, Colour& c, bool fastdraw){};
+
+
+};
+
+
+
+
+
+
+
+
+
+#endif
-/*\r
- Copyright 2006 Marten Richter\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#include "surfacewin.h"\r
-#include "osdwin.h"\r
-#include "bitmap.h"\r
-#include "log.h"\r
-\r
-SurfaceWin::SurfaceWin(int id)\r
-: Surface(id)\r
-{\r
- d3dtexture=NULL;\r
- d3dsurface=NULL;\r
- sheight=swidth=0;\r
-// fastdraw=false;\r
- event = CreateEvent(NULL,/*FALSE*/TRUE,FALSE,NULL);\r
-}\r
-\r
-SurfaceWin::~SurfaceWin()\r
-{\r
- if (d3dsurface) d3dsurface->Release();\r
- if (d3dtexture) d3dtexture->Release();\r
- CloseHandle(event);\r
-}\r
-\r
-int SurfaceWin::create(UINT width, UINT height)\r
-{\r
- OsdWin* osd=((OsdWin*)(Osd::getInstance()));\r
-\r
-\r
- LPDIRECT3DDEVICE9 d3ddev=osd->getD3dDev();\r
-\r
- while (true) {\r
- if (screen==this) {\r
- osd->BeginPainting();\r
- if (d3ddev->CreateTexture(1024,1024,0,0,D3DFMT_A8R8G8B8,\r
- // Does every adapter with alpha blending support this?\r
- D3DPOOL_DEFAULT,&d3dtexture ,NULL)!=D3D_OK) {\r
- osd->EndPainting();\r
- MILLISLEEP(50);//wait maybe next time it will work\r
- continue;\r
- }\r
- if (d3dtexture->GetSurfaceLevel(0,&d3dsurface)!=D3D_OK) {\r
- d3dtexture->Release();\r
- d3dtexture=NULL;\r
- MILLISLEEP(50);\r
- osd->EndPainting();\r
- continue;\r
- }\r
- } else {\r
- HRESULT hres;\r
- if (hres=d3ddev->CreateOffscreenPlainSurface(width,height,D3DFMT_A8R8G8B8,\r
- D3DPOOL_SYSTEMMEM,&d3dsurface,NULL)!=D3D_OK) {\r
- osd->EndPainting();\r
- MILLISLEEP(50);//wait maybe next time it will work\r
-\r
- continue;\r
- }\r
-\r
- }\r
- osd->EndPainting();\r
-\r
- sheight=height;\r
- swidth=width;\r
- /* If someone does high performance Animations on the OSD, we have to change the types\r
- of surface in order to address these performance issues, if we have only very few updates\r
- per second this would be fast enough !*/\r
- break;\r
- }\r
- SetEvent(event);\r
- return 1;\r
-}\r
-\r
-void SurfaceWin::display()\r
-{\r
-}\r
-\r
-int SurfaceWin::fillblt(int x, int y, int width, int height, const DrawStyle& c)\r
-{\r
- WaitForSingleObject(event,INFINITE); //since this might be called before surface\r
- //allocation we will wait in this case, hopefully without deadlocks\r
- OsdWin* osd=((OsdWin*)(Osd::getInstance()));\r
-\r
- if (!d3dsurface) {\r
- return 0; //why does this happen\r
- }\r
- ULONG col=c.rgba();\r
-\r
- LPDIRECT3DDEVICE9 d3ddev=osd->getD3dDev();\r
-\r
- if (screen==this) {\r
- //This should not happen!\r
- return 0;\r
-\r
- } else {\r
- osd->BeginPainting();\r
- D3DLOCKED_RECT lockrect;\r
- int cx,cy,cwidth,cheight;\r
- cx=min(max(x,0),swidth-1);\r
- cy=min(max(y,0),sheight-1);\r
- cwidth=min(width,swidth-x);\r
- cheight=min(height,sheight-y);\r
- RECT rect={cx,cy,cwidth,cheight};\r
-\r
- if (d3dsurface->LockRect(&lockrect,&rect,D3DLOCK_DISCARD)!=D3D_OK) {\r
- return 0;\r
- }\r
- unsigned int line;\r
- unsigned int column;\r
- for (line=0;line<cheight;line++) {\r
- unsigned int*row=((unsigned int*)(((char*)lockrect.pBits)+lockrect.Pitch*line));\r
- for (column=0;column<cwidth;column++) {\r
- row[column]=col;\r
- }\r
- }\r
-\r
- if (d3dsurface->UnlockRect()!=D3D_OK) {\r
- osd->EndPainting();\r
- return 0;\r
- }\r
- osd->EndPainting();\r
- }\r
-\r
- return 0;\r
-}\r
-\r
-\r
-void SurfaceWin::startFastDraw(){\r
- WaitForSingleObject(event,INFINITE); //since this might be called before surface\r
- //allocation we will wait in this case, hopefully without deadlocks\r
- if (!d3dsurface) {\r
- return; //why does this happen\r
- }\r
- OsdWin* osd=((OsdWin*)(Osd::getInstance()));\r
- LPDIRECT3DDEVICE9 d3ddev=osd->getD3dDev();\r
- if (screen==this) {\r
- //This should not happen!\r
- return ;\r
-\r
- } else {\r
- osd->BeginPainting();\r
-// D3DLOCKED_RECT lockrect;\r
- RECT rect={0,0,swidth,sheight};\r
- if (d3dsurface->LockRect(&lockrect,&rect,D3DLOCK_DISCARD)!=D3D_OK) {\r
- osd->EndPainting();\r
- return ;\r
- }\r
- }\r
-// fastdraw=true;\r
-}\r
-void SurfaceWin::endFastDraw(){\r
- OsdWin* osd=((OsdWin*)(Osd::getInstance()));\r
- if (d3dsurface->UnlockRect()!=D3D_OK) {\r
- osd->EndPainting();\r
- return ;\r
- }\r
- osd->EndPainting();\r
-// fastdraw=false;\r
- }\r
-\r
-void SurfaceWin::drawPixel(int x, int y, Colour & colour, bool fastdraw) {\r
- int c = ( (0xFF000000 )\r
- | (colour.red << 16)\r
- | (colour.green << 8)\r
- | (colour.blue ) );\r
-\r
- drawPixel(x, y, c, fastdraw);\r
- }\r
-\r
-void SurfaceWin::drawPixel(int x, int y, unsigned int c, bool fastdraw)\r
-{\r
- //FixMe: locking for every single Pixel will be painfully slow\r
- OsdWin* osd;\r
- if (!fastdraw) {\r
- WaitForSingleObject(event,INFINITE); //since this might be called before surface\r
- //allocation we will wait in this case, hopefully without deadlocks\r
- if (!d3dsurface) {\r
- return; //why does this happen\r
- }\r
- osd=((OsdWin*)(Osd::getInstance()));\r
- }\r
- if (x>=swidth || y>=sheight) return; //do not draw outside the surface\r
- if (screen==this) {\r
- //This should not happen!\r
- return ;\r
-\r
- } else {\r
- if (!fastdraw) {\r
- osd->BeginPainting();\r
-// D3DLOCKED_RECT lockrect;\r
- RECT rect={x,y,x+1,y+1};\r
- if (d3dsurface->LockRect(&lockrect,&rect,D3DLOCK_DISCARD)!=D3D_OK) {\r
- osd->EndPainting();\r
- return ;\r
- }\r
- unsigned int*row=(unsigned int*)(((char*)lockrect.pBits));\r
- row[0]=c;\r
- if (d3dsurface->UnlockRect()!=D3D_OK) {\r
- osd->EndPainting();\r
- return ;\r
- }\r
- osd->EndPainting();\r
- } else {\r
- unsigned int*row=(unsigned int*)(((char*)lockrect.pBits+lockrect.Pitch*y+4*x));\r
- row[0]=c;\r
- }\r
-\r
- }\r
-\r
-}\r
-\r
-void SurfaceWin::drawHorzLine(int x1, int x2, int y,const DrawStyle& c)\r
-{\r
- fillblt(x1, y, x2-x1, 1, c);\r
-}\r
-\r
-void SurfaceWin::drawVertLine(int x, int y1, int y2, const DrawStyle& c)\r
-{\r
- fillblt(x, y1, 1, y2-y1, c);\r
-}\r
-\r
-void SurfaceWin::drawBitmap(int x, int y, const Bitmap& bm)\r
-{\r
- // Temporary code? Draw one pixel at a time using drawPixel()\r
- startFastDraw();\r
- for (UINT j = 0; j < bm.getHeight(); ++j)\r
- for (UINT i = 0; i < bm.getWidth(); ++i)\r
- drawPixel(x+i, y+j, bm.getColour(i,j),true);\r
- endFastDraw();\r
-}\r
-\r
-int SurfaceWin::updateToScreen(int sx, int sy, int w, int h, int dx, int dy) // FIXME new, replace others with this FIXME\r
-{\r
- WaitForSingleObject(event,INFINITE); //since this might be called before surface\r
- //allocation we will wait in this case, hopefully without deadlocks\r
- if (!d3dsurface) {\r
- return 0; //why does this happen\r
- }\r
- OsdWin* osd=((OsdWin*)(Osd::getInstance()));\r
- LPDIRECT3DDEVICE9 d3ddev=osd->getD3dDev();\r
- LPDIRECT3DSURFACE9 screensurface=((SurfaceWin*)screen)->getD3dsurface();\r
- if (!screensurface) return 0;\r
- RECT sourcerect={sx,sy,sx+w,sy+h};\r
- POINT destpoint={dx,dy};\r
- osd->BeginPainting();\r
- if (d3ddev->UpdateSurface(d3dsurface,&sourcerect,screensurface,&destpoint)!=D3D_OK) {\r
- Log::getInstance()->log("Surface", Log::DEBUG, "Could not update to Screen!");\r
- osd->EndPainting();\r
- return 0;\r
- }\r
- osd->EndPainting();\r
- return 0;\r
-}\r
-\r
-int SurfaceWin::blt(int fd, unsigned long shandle, int sx, int sy, int width, int height, unsigned long dhandle, int dx, int dy)\r
-{\r
- //I don't see code using this function, so I skip it, since it is a MVP specific interface\r
- return 0;\r
-}\r
-\r
-void SurfaceWin::screenShot(const char* fileName)\r
-{\r
- //Isn't this for debugging only, so I won't implement it yet\r
-}\r
-\r
-void SurfaceWin::readPixel(int x, int y, unsigned char* r, unsigned char* g, unsigned char* b)\r
-{\r
- //Isn't this for debugging only, so I won't implement it yet\r
-}\r
-void SurfaceWin::ReleaseSurface()\r
-{\r
- ResetEvent(event);\r
- LPDIRECT3DSURFACE9 temp_surf=d3dsurface;\r
- LPDIRECT3DTEXTURE9 temp_text=d3dtexture;\r
- d3dsurface=NULL;\r
- d3dtexture=NULL;\r
- sheight=swidth=0;\r
- if (temp_surf) temp_surf->Release();\r
- if (temp_text) temp_text->Release();\r
-}\r
-\r
-void SurfaceWin::drawJpeg(const char *fileName,int x, int y,int *width, int *height){\r
- WaitForSingleObject(event,INFINITE); //since this might be called before surface\r
- //allocation we will wait in this case, hopefully without deadlocks\r
- if (!d3dsurface) {\r
- return ; //why does this happen\r
- }\r
- OsdWin* osd=((OsdWin*)(Osd::getInstance()));\r
-\r
-\r
- D3DXIMAGE_INFO image_inf;\r
- osd->BeginPainting();\r
-// D3DXGetImageInfoFromFile(fileName,&image_inf);\r
- D3DXGetImageInfoFromResource(NULL,fileName,&image_inf);\r
- RECT dest_rec={x,y,x+image_inf.Width,\r
- y+image_inf.Height};\r
-/* if (D3DXLoadSurfaceFromFile(\r
- d3dsurface,\r
- NULL,\r
- &dest_rec,\r
- fileName,\r
- NULL,\r
- D3DX_FILTER_NONE,\r
- 0,\r
- &image_inf)!=D3D_OK) {\r
- Log::getInstance()->log("Surface", Log::DEBUG, "Could not open jpeg!");\r
-\r
- }*/\r
- if (D3DXLoadSurfaceFromResource(\r
- d3dsurface,\r
- NULL,\r
- &dest_rec,\r
- NULL,\r
- fileName,\r
- NULL,\r
- D3DX_FILTER_NONE,\r
- 0,\r
- &image_inf)!=D3D_OK) {\r
- Log::getInstance()->log("Surface", Log::DEBUG, "Could not open jpeg!");\r
-\r
- }\r
- osd->EndPainting();\r
- *width=image_inf.Width;\r
- *height=image_inf.Height;\r
-\r
-}\r
-\r
-/*\r
-void SurfaceWin::drawJpeg(char *buffer,ULONG buflength,DWORD x, DWORD y,DWORD *width, DWORD *height){\r
- WaitForSingleObject(event,INFINITE); //since this might be called before surface\r
- //allocation we will wait in this case, hopefully without deadlocks\r
- if (!d3dsurface) {\r
- return ; //why does this happen\r
- }\r
- OsdWin* osd=((OsdWin*)(Osd::getInstance()));\r
-\r
-\r
- D3DXIMAGE_INFO image_inf;\r
- osd->BeginPainting();\r
-// D3DXGetImageInfoFromFile(fileName,&image_inf);\r
- D3DXGetImageInfoFromFileInMemory((void*)buffer,buflength,&image_inf);\r
- RECT dest_rec={x,y,x+image_inf.Width,\r
- y+image_inf.Height};\r
-/* if (D3DXLoadSurfaceFromFile(\r
- d3dsurface,\r
- NULL,\r
- &dest_rec,\r
- fileName,\r
- NULL,\r
- D3DX_FILTER_NONE,\r
- 0,\r
- &image_inf)!=D3D_OK) {\r
- Log::getInstance()->log("Surface", Log::DEBUG, "Could not open jpeg!");\r
-\r
- }*/\r
-/* if (D3DXLoadSurfaceFromResource(\r
- d3dsurface,\r
- NULL,\r
- &dest_rec,\r
- NULL,\r
- fileName,\r
- NULL,\r
- D3DX_FILTER_NONE,\r
- 0,\r
- &image_inf)!=D3D_OK) {\r
- Log::getInstance()->log("Surface", Log::DEBUG, "Could not open jpeg!");\r
-\r
- }*\r
- if (D3DXLoadSurfaceFromFileInMemory(\r
- d3dsurface,\r
- NULL,\r
- &dest_rec,\r
- (void*)buffer,\r
- buflength,\r
- NULL,\r
- D3DX_FILTER_NONE,\r
- 0,\r
- &image_inf)!=D3D_OK) {\r
- Log::getInstance()->log("Surface", Log::DEBUG, "Could not open jpeg!");\r
-\r
- }\r
- osd->EndPainting();\r
- *width=image_inf.Width;\r
- *height=image_inf.Height;\r
-\r
-}*/\r
-\r
+/*
+ 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, const DrawStyle& 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
+ }
+ ULONG col=c.rgba();
+
+ 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;line<cheight;line++) {
+ unsigned int*row=((unsigned int*)(((char*)lockrect.pBits)+lockrect.Pitch*line));
+ for (column=0;column<cwidth;column++) {
+ row[column]=col;
+ }
+ }
+
+ if (d3dsurface->UnlockRect()!=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,const DrawStyle& c)
+{
+ fillblt(x1, y, x2-x1, 1, c);
+}
+
+void SurfaceWin::drawVertLine(int x, int y1, int y2, const DrawStyle& 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;
+
+}*/
+
-/*\r
- Copyright 2006 Marten Richter\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef SURFACEWIN_H\r
-#define SURFACEWIN_H\r
-\r
-#include "defines.h"\r
-#include "surface.h"\r
-#include <winsock2.h>\r
-#include <d3d9.h>\r
-\r
-class SurfaceWin : public Surface\r
-{\r
- public:\r
- SurfaceWin(int id = 0);\r
- ~SurfaceWin();\r
-\r
- int create(UINT width, UINT height);\r
- void display();\r
-\r
- void startFastDraw();\r
- void endFastDraw();\r
-\r
- int fillblt(int x, int y, int width, int height, const DrawStyle& c);\r
- void drawHorzLine(int x1, int x2, int y, const DrawStyle& c);\r
- void drawVertLine(int x, int y1, int y2, const DrawStyle& c);\r
- void drawBitmap(int x, int y, const Bitmap& bm);\r
- int updateToScreen(int sx, int sy, int w, int h, int dx, int dy);\r
- void readPixel(int x, int y, unsigned char* r, unsigned char* g, unsigned char* b);\r
- void screenShot(const char* fileName);\r
- void ReleaseSurface();\r
- int blt(int fd, unsigned long shandle, int sx, int sy, int width, int height, unsigned long dhandle, int dx, int dy);\r
- void drawJpeg(const char *fileName,int x, int y,int *width, int *height);\r
-/* void drawJpeg(char *buffer,ULONG buflength,DWORD x, DWORD y,DWORD *width, DWORD *height);*/\r
- LPDIRECT3DSURFACE9 getD3dsurface() {WaitForSingleObject(event,INFINITE);\r
- return d3dsurface;};\r
- LPDIRECT3DTEXTURE9 getD3dtexture() {return d3dtexture;};\r
- private:\r
- LPDIRECT3DSURFACE9 d3dsurface;\r
- LPDIRECT3DTEXTURE9 d3dtexture;\r
- D3DLOCKED_RECT lockrect;\r
- UINT sheight,swidth;\r
- HANDLE event;\r
- protected:\r
- void drawPixel(int x, int y, Colour& c, bool fastdraw=false);\r
- void drawPixel(int x, int y, unsigned int c, bool fastdraw=false);\r
-};\r
-\r
-#endif\r
+/*
+ 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 <winsock2.h>
+#include <d3d9.h>
+
+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, const DrawStyle& c);
+ void drawHorzLine(int x1, int x2, int y, const DrawStyle& c);
+ void drawVertLine(int x, int y1, int y2, const DrawStyle& 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;
+ protected:
+ void drawPixel(int x, int y, Colour& c, bool fastdraw=false);
+ void drawPixel(int x, int y, unsigned int c, bool fastdraw=false);
+};
+
+#endif
-/*\r
- Copyright 2008 Marten Richter\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-\r
-/* Portions from vdr osdteletext plugin "txtrender.c": */\r
-/***************************************************************************\r
- * *\r
- * txtrender.c - Teletext display abstraction and teletext code *\r
- * renderer *\r
- * *\r
- * This program is free software; you can redistribute it and/or modify *\r
- * it under the terms of the GNU General Public License as published by *\r
- * the Free Software Foundation; either version 2 of the License, or *\r
- * (at your option) any later version. *\r
- * *\r
- * Changelog: *\r
- * 2005-03 initial version (c) Udo Richter *\r
- * *\r
- ***************************************************************************/\r
-\r
-#include "teletextdecodervbiebu.h"\r
-#include "teletxt/tables.h"\r
-#include "message.h"\r
-#include "command.h"\r
-#include "video.h"\r
-\r
-#ifdef WIN32\r
-#include <windows.h>\r
-#endif\r
-\r
-TeletextDecoderVBIEBU::TeletextDecoderVBIEBU()\r
-{\r
- selectedpage=0x100;\r
- ourpage=false;\r
- flags=lang=0; \r
- \r
- CleanPage();\r
- FirstG0CodePage=0;\r
- SecondG0CodePage=0;\r
- dirty=false;\r
- txtview=NULL;\r
- firstlineupdate=0;\r
- gotcha=false;\r
- char digits[]={'1','0','0'};\r
- setKeyinDigits(digits,false);\r
- isrecording=false;\r
- for (int i=0;i<10;i++) record_pages[i]=-1;\r
- \r
-\r
-}\r
-\r
-TeletextDecoderVBIEBU::~TeletextDecoderVBIEBU()\r
-{\r
-\r
-\r
-}\r
-\r
-long long TeletextDecoderVBIEBU::SetStartOffset(long long curreftime, bool *rsync)\r
-{\r
- return 0; \r
-}\r
-\r
-void TeletextDecoderVBIEBU::ResetTimeOffsets()\r
-{\r
-\r
-}\r
-\r
-\r
-\r
-void TeletextDecoderVBIEBU::ResetDecoder()\r
-{\r
- gotcha=false;\r
- ourpage=false;\r
- firstlineupdate=0;\r
- selectedpage=0x100;\r
- CleanPage();\r
- char digits[]={'1','0','0'};\r
- setKeyinDigits(digits,false);\r
- for (int i=0;i<10;i++) record_pages[i]=-1;\r
- \r
-}\r
-\r
-void TeletextDecoderVBIEBU::setPage(unsigned int newpage)\r
-{\r
- selectedpage=newpage;\r
- gotcha=false;\r
- ourpage=false;\r
- firstlineupdate=0;\r
- for (int i=0;i<25;i++) {\r
- memset(curpage[i],0,40);\r
- }\r
-}\r
-\r
-void TeletextDecoderVBIEBU::CleanPage()\r
-{\r
- int x,y;\r
- for (int i=0;i<25;i++) {\r
- memset(curpage[i],0,40);\r
- }\r
- for (y=0;y<25;y++) {\r
- for (x=0;x<40;x++) {\r
- cTeletextChar c;\r
- c.SetFGColor(ttcWhite);\r
- c.SetBGColor(ttcBlack);\r
- c.SetCharset(CHARSET_LATIN_G0);\r
- c.SetChar(' ');\r
- if (flags&0x60) {\r
- c.SetBoxedOut(true); \r
- }\r
- setChar(x,y,c);\r
- }\r
- } \r
-\r
-}\r
-void TeletextDecoderVBIEBU::setKeyinDigits(char digits[3],bool inKeying)\r
-{\r
- int x;\r
- inkeying=inKeying;\r
- for (x=0;x<3;x++) {\r
- cTeletextChar c;\r
- c.SetFGColor(ttcWhite);\r
- c.SetBGColor(ttcBlack);\r
- c.SetCharset(CHARSET_LATIN_G0);\r
- c.SetChar(digits[x]);\r
- if (flags&0x60) {\r
- c.SetBoxedOut(true); \r
- }\r
- setChar(x+3,0,c);\r
- keyindigits[x]=digits[x];\r
- }\r
-}\r
-\r
-void TeletextDecoderVBIEBU::PrepareMediaSample(const MediaPacketList& mplist, UINT samplepos)\r
-{\r
- mediapacket = mplist.front();\r
-}\r
-\r
-static ULLONG TxtPTSDifference(ULLONG pts1, ULLONG pts2)\r
-{\r
- // Assume pts1, pts2 < 2^33; calculate pts1 - pts2\r
- if (pts1 > pts2)\r
- return pts1 - pts2;\r
- else\r
- return -pts1 + pts2;\r
-}\r
-\r
-UINT TeletextDecoderVBIEBU::DeliverMediaSample(UCHAR* buffer, UINT *samplepos)\r
-{\r
- if (mediapacket.type != MPTYPE_TELETEXT)\r
- {\r
- *samplepos= 0;\r
- return 1; //Skip it! \r
- }\r
- unsigned int headerstrip;\r
- unsigned char txtdata[43];\r
- headerstrip=buffer[mediapacket.pos_buffer+8]+9;\r
- //headerstrip+=4; //substream id\r
- unsigned int datapos=0;\r
- unsigned int datatype=buffer[mediapacket.pos_buffer+headerstrip+0];\r
- //Chris should we use here the pts data from mediapacket ?\r
- // in this case the data fields should also be added in mediamvp\r
- if (mediapacket.synched)\r
- { // An entry exists in the work list\r
- ULLONG nowPTS = Video::getInstance()->getCurrentTimestamp();\r
-\r
- ULLONG ptsdifference=TxtPTSDifference(mediapacket.pts, nowPTS);\r
-\r
- if (ptsdifference >= (120LL*90000LL)) {\r
- *samplepos=0;\r
- return 1;//bad data skip it\r
- }\r
- if (nowPTS < (mediapacket.pts-4000) ) {\r
- *samplepos=0;\r
- return 0;\r
- } \r
- }\r
- \r
- if (( datatype>=0x10 && datatype <=0x1F) ||\r
- ( datatype>=0x99 && datatype <=0x9B)) {\r
- //EBU VBI DATA\r
- datapos++;\r
- while (datapos< (mediapacket.length-headerstrip)) {\r
- unsigned int unit_id=buffer[mediapacket.pos_buffer+headerstrip+datapos];\r
- datapos++;\r
- unsigned int unit_length=buffer[mediapacket.pos_buffer+headerstrip+datapos];\r
- datapos++;\r
- switch (unit_id) {\r
- case 0x02:\r
- case 0x03: {//Teletext with and without subtitles\r
- //call teletext decoder\r
- unsigned int field=buffer[mediapacket.pos_buffer+headerstrip+datapos];\r
-\r
- if ((datapos+44)< (mediapacket.length-headerstrip)) {\r
- for (int i=0;i<42;i++) {\r
- txtdata[i]=invtab[buffer[mediapacket.pos_buffer+headerstrip+datapos+2+i]];\r
- }\r
- DecodeTeletext(txtdata,field);\r
- }\r
- }break;\r
- case 0xC0: //inverted Teletext\r
- //call teletext decoder\r
- break;\r
- \r
- case 0xC3: //VPS\r
- //what to do with this, in the moment we do not need it\r
- break;\r
- case 0xC4: //WSS\r
- //what to do with this, in the moment we do not need it\r
- break;\r
- case 0xC5: //CC\r
- //what to do with this, in the moment we do not need it\r
- break;\r
- case 0xC6: //monochrome 4:2:2 samples, what is that? \r
- //what to do with this, in the moment we do not need it\r
- break;\r
- default:\r
- // case 0x00,0x01,0x04..0x7f,0x80..0xbf,0xc1,0xc2,0xc7..0xfe,0xff: //discard\r
- //discard\r
- break;\r
- };\r
- datapos+=unit_length;\r
- // while (buffer[mediapacket.pos_buffer+headerstrip+datapos]==0xFF &&(datapos< (mediapacket.length-headerstrip))) {\r
- // datapos++; //stuffing bytes\r
- // }\r
-\r
- \r
- }\r
- *samplepos=0;\r
- return 1;\r
-\r
-\r
- } else {\r
- *samplepos= 0;\r
- return 1; //Skip it! and discard data \r
- }\r
-\r
- return 0;\r
-\r
-}\r
-// This part is inspired by the vdr-plugin-osdteletext of Udo Richter and Marcel Wiesweg\r
-void TeletextDecoderVBIEBU::DecodeTeletext(const UCHAR* buffer, unsigned int field) //needs to be exactly 42 byte long!!\r
-{\r
- UCHAR hdrbuf[5];\r
- for (int i=0;i<5;i++) hdrbuf[i]=(unhamtab[buffer[2*i]]&0xF) | ((unhamtab[buffer[2*i+1]]&0xF)<< 4);\r
- int header=hdrbuf[0];\r
- int magazin=header & 0x7;\r
- int line = (header>>3) & 0x1f;\r
- if (magazin==0) magazin=8;\r
- if (line==0)\r
- {\r
- if (ourpage) {\r
- gotcha=true;\r
- inkeying=false;\r
- RenderTeletextCode(false);\r
- } else {\r
- RenderTeletextCode(true);\r
- }\r
- int pagenumber=hdrbuf[1];\r
- int pagemagazin=magazin<<8 | pagenumber;\r
- int pagesubnumber=(hdrbuf[2]) || ((hdrbuf[3]<<8) & 0x3f7f);\r
-\r
- if (pagemagazin == selectedpage) ourpage=true;\r
- else ourpage=false;\r
- if (isrecording) {\r
- for (int i=0;i<10;i++) {\r
- if (pagemagazin==record_pages[i]) break;\r
- if (record_pages[i]==-1) {\r
- record_pages[i]=pagemagazin;\r
- break;\r
- }\r
- }\r
- }\r
- if (hdrbuf[3] &0x80) { //This is a subtitle\r
- for (int i=0;i<10;i++) {\r
- if (pagemagazin==record_pages[i]) break;\r
- if (record_pages[i]==-1) {\r
- record_pages[i]=pagemagazin;\r
- break;\r
- }\r
- }\r
- }\r
-\r
-\r
- if (ourpage) {\r
- lang=((hdrbuf[4]>>5) & 0x07);\r
- flags=hdrbuf[2] & 0x80;\r
- flags|=(hdrbuf[3]&0x40)|((hdrbuf[3]>>2)&0x20); //??????\r
- flags|=((hdrbuf[4]<<4)&0x10)|((hdrbuf[4]<<2)&0x08)|(hdrbuf[4]&0x04)|((hdrbuf[4]>>1)&0x02)|((hdrbuf[4]>>4)&0x01);\r
- for (int i=0;i<25;i++) {\r
- memset(curpage[i],0,40);\r
- }\r
- }\r
- \r
- memcpy(curpage[line],buffer+2,40);\r
- } else if (ourpage && (line<=25)) {\r
- memcpy(curpage[line],buffer+2,40);\r
- \r
- }\r
-}\r
-\r
-\r
-/* from osdteletext plugin: (slightly adapted for vomp)*/\r
-// Font tables\r
-\r
-// teletext uses 7-bit numbers to identify a font set.\r
-// There are three font sets involved:\r
-// Primary G0, Secondary G0, and G2 font set.\r
-\r
-// Font tables are organized in blocks of 8 fonts:\r
-\r
-enumCharsets FontBlockG0_0000[8] = {\r
- CHARSET_LATIN_G0_EN,\r
- CHARSET_LATIN_G0_DE,\r
- CHARSET_LATIN_G0_SV_FI,\r
- CHARSET_LATIN_G0_IT,\r
- CHARSET_LATIN_G0_FR,\r
- CHARSET_LATIN_G0_PT_ES,\r
- CHARSET_LATIN_G0_CZ_SK,\r
- CHARSET_LATIN_G0\r
-};\r
-\r
-enumCharsets FontBlockG2Latin[8]={\r
- CHARSET_LATIN_G2,\r
- CHARSET_LATIN_G2,\r
- CHARSET_LATIN_G2,\r
- CHARSET_LATIN_G2,\r
- CHARSET_LATIN_G2,\r
- CHARSET_LATIN_G2,\r
- CHARSET_LATIN_G2,\r
- CHARSET_LATIN_G2\r
-};\r
-\r
-enumCharsets FontBlockG0_0001[8] = {\r
- CHARSET_LATIN_G0_PL,\r
- CHARSET_LATIN_G0_DE,\r
- CHARSET_LATIN_G0_SV_FI,\r
- CHARSET_LATIN_G0_IT,\r
- CHARSET_LATIN_G0_FR,\r
- CHARSET_LATIN_G0,\r
- CHARSET_LATIN_G0_CZ_SK,\r
- CHARSET_LATIN_G0\r
-};\r
-\r
-enumCharsets FontBlockG0_0010[8] = {\r
- CHARSET_LATIN_G0_EN,\r
- CHARSET_LATIN_G0_DE,\r
- CHARSET_LATIN_G0_SV_FI,\r
- CHARSET_LATIN_G0_IT,\r
- CHARSET_LATIN_G0_FR,\r
- CHARSET_LATIN_G0_PT_ES,\r
- CHARSET_LATIN_G0_TR,\r
- CHARSET_LATIN_G0\r
-};\r
-\r
-\r
-enumCharsets FontBlockG0_0011[8] = {\r
- CHARSET_LATIN_G0,\r
- CHARSET_LATIN_G0,\r
- CHARSET_LATIN_G0,\r
- CHARSET_LATIN_G0,\r
- CHARSET_LATIN_G0,\r
- CHARSET_LATIN_G0_SR_HR_SL,\r
- CHARSET_LATIN_G0,\r
- CHARSET_LATIN_G0_RO\r
-};\r
-\r
-enumCharsets FontBlockG0_0100[8] = {\r
- CHARSET_CYRILLIC_G0_SR_HR,\r
- CHARSET_LATIN_G0_DE,\r
- CHARSET_LATIN_G0_EE,\r
- CHARSET_LATIN_G0_LV_LT,\r
- CHARSET_CYRILLIC_G0_RU_BG,\r
- CHARSET_CYRILLIC_G0_UK,\r
- CHARSET_LATIN_G0_CZ_SK,\r
- CHARSET_INVALID\r
-};\r
-\r
-enumCharsets FontBlockG2_0100[8] = {\r
- CHARSET_CYRILLIC_G2,\r
- CHARSET_LATIN_G2,\r
- CHARSET_LATIN_G2,\r
- CHARSET_LATIN_G2,\r
- CHARSET_CYRILLIC_G2,\r
- CHARSET_CYRILLIC_G2,\r
- CHARSET_LATIN_G2,\r
- CHARSET_INVALID\r
-};\r
-\r
-enumCharsets FontBlockG0_0110[8] = {\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_LATIN_G0_TR,\r
- CHARSET_GREEK_G0\r
-};\r
-\r
-enumCharsets FontBlockG2_0110[8] = {\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_LATIN_G2,\r
- CHARSET_GREEK_G2\r
-};\r
-\r
-enumCharsets FontBlockG0_1000[8] = {\r
- CHARSET_LATIN_G0_EN,\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_LATIN_G0_FR,\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_ARABIC_G0\r
-};\r
-\r
-enumCharsets FontBlockG2_1000[8] = {\r
- CHARSET_ARABIC_G2,\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_ARABIC_G2,\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_ARABIC_G2\r
-};\r
-\r
-enumCharsets FontBlockG0_1010[8] = {\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_HEBREW_G0,\r
- CHARSET_INVALID,\r
- CHARSET_ARABIC_G0,\r
-};\r
-\r
-enumCharsets FontBlockG2_1010[8] = {\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_ARABIC_G2,\r
- CHARSET_INVALID,\r
- CHARSET_ARABIC_G2,\r
-};\r
-\r
-enumCharsets FontBlockInvalid[8] = {\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_INVALID,\r
- CHARSET_INVALID\r
-};\r
-\r
-\r
-\r
-// The actual font table definition:\r
-// Split the 7-bit number into upper 4 and lower 3 bits,\r
-// use upper 4 bits for outer array,\r
-// use lower 3 bits for inner array\r
-\r
-struct structFontBlock {\r
- enumCharsets *G0Block;\r
- enumCharsets *G2Block;\r
-};\r
- \r
-structFontBlock FontTable[16] = {\r
- { FontBlockG0_0000, FontBlockG2Latin }, // 0000 block\r
- { FontBlockG0_0001, FontBlockG2Latin }, // 0001 block\r
- { FontBlockG0_0010, FontBlockG2Latin }, // 0010 block\r
- { FontBlockG0_0011, FontBlockG2Latin }, // 0011 block\r
- { FontBlockG0_0100, FontBlockG2_0100 }, // 0100 block\r
- { FontBlockInvalid, FontBlockInvalid }, // 0101 block\r
- { FontBlockG0_0110, FontBlockG2_0110 }, // 0110 block\r
- { FontBlockInvalid, FontBlockInvalid }, // 0111 block\r
- { FontBlockG0_1000, FontBlockG2_1000 }, // 1000 block\r
- { FontBlockInvalid, FontBlockInvalid }, // 1001 block\r
- { FontBlockG0_1010, FontBlockG2_1010 }, // 1010 block\r
- { FontBlockInvalid, FontBlockInvalid }, // 1011 block\r
- { FontBlockInvalid, FontBlockInvalid }, // 1100 block\r
- { FontBlockInvalid, FontBlockInvalid }, // 1101 block\r
- { FontBlockInvalid, FontBlockInvalid }, // 1110 block\r
- { FontBlockInvalid, FontBlockInvalid } // 1111 block\r
-};\r
-\r
-inline enumCharsets GetG0Charset(int codepage) {\r
- return FontTable[codepage>>3].G0Block[codepage&7];\r
-}\r
-inline enumCharsets GetG2Charset(int codepage) {\r
- return FontTable[codepage>>3].G2Block[codepage&7];\r
-}\r
-\r
-enum enumSizeMode {\r
- // Possible size modifications of characters\r
- sizeNormal,\r
- sizeDoubleWidth,\r
- sizeDoubleHeight,\r
- sizeDoubleSize\r
-};\r
-\r
-void TeletextDecoderVBIEBU::RenderTeletextCode(bool renderfirstlineonly) {\r
- int x,y;\r
- bool EmptyNextLine=false;\r
- // Skip one line, in case double height chars were/will be used\r
-\r
- // Get code pages:\r
- int LocalG0CodePage=(FirstG0CodePage & 0x78) \r
- | ((lang & 0x04)>>2) | (lang & 0x02) | ((lang & 0x01)<<2);\r
- enumCharsets FirstG0=GetG0Charset(LocalG0CodePage);\r
- enumCharsets SecondG0=GetG0Charset(SecondG0CodePage);\r
- // Reserved for later use:\r
- // enumCharsets FirstG2=GetG2Charset(LocalG0CodePage);\r
- \r
- for (y=0;y<24;(EmptyNextLine?y+=2:y++)) {\r
- // Start of line: Set start of line defaults\r
- \r
- // Hold Mosaics mode: Remember last mosaic char/charset \r
- // for next spacing code\r
- bool HoldMosaics=false;\r
- unsigned char HoldMosaicChar=' ';\r
- enumCharsets HoldMosaicCharset=FirstG0;\r
-\r
- enumSizeMode Size=sizeNormal;\r
- // Font size modification\r
- bool SecondCharset=false;\r
- // Use primary or secondary G0 charset\r
- bool GraphicCharset=false;\r
- // Graphics charset used?\r
- bool SeparateGraphics=false;\r
- // Use separated vs. contiguous graphics charset\r
- bool NoNextChar=false;\r
- // Skip display of next char, for double-width\r
- EmptyNextLine=false;\r
- // Skip next line, for double-height\r
-\r
- cTeletextChar c;\r
- // auto.initialized to everything off\r
- c.SetFGColor(ttcWhite);\r
- c.SetBGColor(ttcBlack);\r
- c.SetCharset(FirstG0);\r
- \r
- if (y==0 && (flags&0x10)) {\r
- if (!inkeying ) c.SetBoxedOut(true);\r
- \r
- }\r
- if (flags&0x60) {\r
- if (!(inkeying && y==0) ) c.SetBoxedOut(true);\r
- \r
- }\r
- if (y==0) {\r
- \r
- for (x=0;x<8;x++) {\r
- cTeletextChar c2=c;\r
- \r
- if (x>=3 && x<6){\r
- c2.SetChar(keyindigits[x-3]);\r
-\r
- }\r
- else\r
- c2.SetChar(' ');\r
- setChar(x,0,c2);\r
- }\r
- }\r
-\r
- // Pre-scan for double-height and double-size codes\r
- for (x=0;x<40;x++) {\r
- if (y==0 && x<8) x=8;\r
- if ((curpage[y][x] & 0x7f)==0x0D || (curpage[y][x] & 0x7f)==0x0F)\r
- EmptyNextLine=true;\r
- }\r
-\r
- // Move through line\r
- for (x=0;x<40;x++) {\r
- unsigned char ttc=curpage[y][x] & 0x7f;\r
- // skip parity check\r
-\r
- if (y==0 && x<8) continue;\r
- if (y==0 && x<31 && renderfirstlineonly && gotcha && !inkeying) continue;\r
- // no displayable data here...\r
- \r
-/* // Debug only: Output line data and spacing codes\r
- if (y==6) {\r
- if (ttc<0x20)\r
- printf("%s ",names[ttc]);\r
- else\r
- printf("%02x ",ttc);\r
- if (x==39) printf("\n");\r
- }\r
-*/ \r
- \r
- // Handle all 'Set-At' spacing codes\r
- switch (ttc) {\r
- case 0x09: // Steady\r
- c.SetBlink(false);\r
- break;\r
- case 0x0C: // Normal Size\r
- if (Size!=sizeNormal) {\r
- Size=sizeNormal;\r
- HoldMosaicChar=' ';\r
- HoldMosaicCharset=FirstG0;\r
- } \r
- break;\r
- case 0x18: // Conceal\r
- c.SetConceal(true);\r
- break;\r
- case 0x19: // Contiguous Mosaic Graphics\r
- SeparateGraphics=false;\r
- if (GraphicCharset)\r
- c.SetCharset(CHARSET_GRAPHICS_G1);\r
- break;\r
- case 0x1A: // Separated Mosaic Graphics\r
- SeparateGraphics=true;\r
- if (GraphicCharset)\r
- c.SetCharset(CHARSET_GRAPHICS_G1_SEP);\r
- break;\r
- case 0x1C: // Black Background\r
- c.SetBGColor(ttcBlack);\r
- break;\r
- case 0x1D: // New Background\r
- c.SetBGColor(c.GetFGColor());\r
- break;\r
- case 0x1E: // Hold Mosaic\r
- HoldMosaics=true; \r
- break;\r
- }\r
-\r
- // temporary copy of character data:\r
- cTeletextChar c2=c;\r
- // c2 will be text character or space character or hold mosaic\r
- // c2 may also have temporary flags or charsets\r
- \r
- if (ttc<0x20) {\r
- // Spacing code, display space or hold mosaic\r
- if (HoldMosaics) {\r
- c2.SetChar(HoldMosaicChar);\r
- c2.SetCharset(HoldMosaicCharset);\r
- } else {\r
- c2.SetChar(' ');\r
- }\r
- } else {\r
- // Character code \r
- c2.SetChar(ttc);\r
- if (GraphicCharset) {\r
- if (ttc&0x20) {\r
- // real graphics code, remember for HoldMosaics\r
- HoldMosaicChar=ttc;\r
- HoldMosaicCharset=c.GetCharset();\r
- } else {\r
- // invalid code, pass-through to G0\r
- c2.SetCharset(SecondCharset?SecondG0:FirstG0);\r
- } \r
- }\r
- }\r
- \r
- // Handle double-height and double-width extremes\r
- if (y>=23) {\r
- if (Size==sizeDoubleHeight) Size=sizeNormal;\r
- if (Size==sizeDoubleSize) Size=sizeDoubleWidth;\r
- }\r
- if (x>=38) {\r
- if (Size==sizeDoubleWidth) Size=sizeNormal;\r
- if (Size==sizeDoubleSize) Size=sizeDoubleHeight;\r
- }\r
- \r
- // Now set character code\r
- \r
- if (NoNextChar) {\r
- // Suppress this char due to double width last char\r
- NoNextChar=false;\r
- } else {\r
- switch (Size) {\r
- case sizeNormal:\r
- // Normal sized\r
- setChar(x,y,c2);\r
- if (EmptyNextLine && y<23) {\r
- // Clean up next line\r
- setChar(x,y+1,c2.ToChar(' ').ToCharset(FirstG0));\r
- }\r
- break;\r
- case sizeDoubleWidth:\r
- // Double width\r
- setChar(x,y,c2.ToDblWidth(dblw_Left));\r
- setChar(x+1,y,c2.ToDblWidth(dblw_Right));\r
- if (EmptyNextLine && y<23) {\r
- // Clean up next line\r
- setChar(x ,y+1,c2.ToChar(' ').ToCharset(FirstG0));\r
- setChar(x+1,y+1,c2.ToChar(' ').ToCharset(FirstG0));\r
- }\r
- NoNextChar=true;\r
- break;\r
- case sizeDoubleHeight:\r
- // Double height\r
- setChar(x,y,c2.ToDblHeight(dblh_Top));\r
- setChar(x,y+1,c2.ToDblHeight(dblh_Bottom));\r
- break;\r
- case sizeDoubleSize:\r
- // Double Size\r
- setChar(x , y,c2.ToDblHeight(dblh_Top ).ToDblWidth(dblw_Left ));\r
- setChar(x+1, y,c2.ToDblHeight(dblh_Top ).ToDblWidth(dblw_Right));\r
- setChar(x ,y+1,c2.ToDblHeight(dblh_Bottom).ToDblWidth(dblw_Left ));\r
- setChar(x+1,y+1,c2.ToDblHeight(dblh_Bottom).ToDblWidth(dblw_Right));\r
- NoNextChar=true;\r
- break;\r
- }\r
- }\r
- \r
- // Handle all 'Set-After' spacing codes\r
- if (ttc>=0x00 && ttc<=0x07) { // Set FG color\r
- if (GraphicCharset) {\r
- // Actual switch from graphics charset\r
- HoldMosaicChar=' ';\r
- HoldMosaicCharset=FirstG0;\r
- }\r
- c.SetFGColor((enumTeletextColor)ttc);\r
- c.SetCharset(SecondCharset?SecondG0:FirstG0);\r
- GraphicCharset=false;\r
- c.SetConceal(false);\r
- } else if (ttc==0x08) {\r
- c.SetBlink(true);\r
- } else if (ttc==0x0A) {\r
- c.SetBoxedOut(true);\r
- } else if (ttc==0x0B) {\r
- // Start Box\r
- c.SetBoxedOut(false);\r
- } else if (ttc==0x0D) {\r
- if (Size!=sizeDoubleHeight) {\r
- Size=sizeDoubleHeight;\r
- HoldMosaicChar=' ';\r
- HoldMosaicCharset=FirstG0;\r
- } \r
- } else if (ttc==0x0E) {\r
- if (Size!=sizeDoubleWidth) {\r
- Size=sizeDoubleWidth;\r
- HoldMosaicChar=' ';\r
- HoldMosaicCharset=FirstG0;\r
- } \r
- } else if (ttc==0x0E) {\r
- if (Size!=sizeDoubleSize) {\r
- Size=sizeDoubleSize;\r
- HoldMosaicChar=' ';\r
- HoldMosaicCharset=FirstG0;\r
- } \r
- } else if (ttc>=0x10 && ttc<=0x17) { \r
- if (!GraphicCharset) {\r
- // Actual switch to graphics charset\r
- HoldMosaicChar=' ';\r
- HoldMosaicCharset=FirstG0;\r
- }\r
- c.SetFGColor((enumTeletextColor)(ttc-0x10));\r
- c.SetCharset(SeparateGraphics?CHARSET_GRAPHICS_G1_SEP:CHARSET_GRAPHICS_G1);\r
- GraphicCharset=true;\r
- c.SetConceal(false);\r
- } else if (ttc==0x1B) {\r
- SecondCharset=!SecondCharset;\r
- if (!GraphicCharset) c.SetCharset(SecondCharset?SecondG0:FirstG0);\r
- } else if (ttc==0x1F) {\r
- HoldMosaics=false;\r
- }\r
- \r
- } // end for x\r
- if (renderfirstlineonly) break;\r
- } // end for y\r
- \r
- for (x=0;x<40;x++) {\r
- // Clean out last line\r
- cTeletextChar c;\r
- c.SetFGColor(ttcWhite);\r
- c.SetBGColor(ttcBlack);\r
- c.SetCharset(FirstG0);\r
- c.SetChar(' ');\r
- if (flags&0x60) {\r
- c.SetBoxedOut(true); \r
- }\r
- setChar(x,24,c);\r
- } \r
- for (y=0;y<25;y++) {\r
- for (x=0;x<40;x++) {\r
- if (isDirty(x,y)) {\r
- dirty=true;\r
- break;\r
- }\r
- if (dirty) break;\r
- }\r
- if (dirty) break;\r
- }\r
- \r
- if (dirty && txtview!=NULL ) {\r
- \r
- if ( !renderfirstlineonly) {\r
- Message* m= new Message();\r
- m->message = Message::TELETEXTUPDATE;\r
- m->to = txtview;\r
- m->from = this;\r
- m->parameter = 0;\r
- Command::getInstance()->postMessageFromOuterSpace(m);\r
- } else if (firstlineupdate==10) {\r
- Message* m= new Message();\r
- m->message = Message::TELETEXTUPDATEFIRSTLINE;\r
- m->to = txtview;\r
- m->from = this;\r
- m->parameter = 0;\r
- Command::getInstance()->postMessageFromOuterSpace(m);\r
- firstlineupdate=0;\r
- } else firstlineupdate++;\r
- \r
- \r
- }\r
- \r
-}\r
+/*
+ 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 <windows.h>
+#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++;
+
+
+ }
+
+}
-/*\r\r
- Copyright 2008 Marten Richter\r\r
- This file is part of VOMP.\r\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r\r
-\r\r
-#include "tfeed.h"\r\r
-#include "log.h"\r#include "demuxer.h"\r#include "callback.h"\r\r
-\r\r
-TFeed::TFeed(Callback* tcb)\r: cb(*tcb)\r{\r teletextEnabled = 1;\r}\r\r
-\r\r
-int TFeed::init()\r{\r return 1;\r}\r\r
-\r\r
-int TFeed::shutdown()\r{\r\r
- // FIXME\r return 1;\r}\r\r
-\r\r
-void TFeed::disable()\r\r
-{\r teletextEnabled = 0;\r
-}\r\r
-\r\r
-void TFeed::enable()\r
-{\r teletextEnabled = 1;\r\r
-}\r\r
-\r\r
-int TFeed::start()\r
-{\r
- teletextEnabled = 1;\r
- return threadStart();\r
-}\r\r
-\r\r
-void TFeed::stop()\r
-{\r
- threadCancel();\r
-}\r\r
-\r\r
-void TFeed::threadMethod()\r
-{\r
- bool tlen;\r\r
- while(1)\r {\r
- threadCheckExit();\r
- tlen = Demuxer::getInstance()->writeTeletext();\r\r
- if (tlen)\r
- {\r
- cb.call(this);\r
-// Log::getInstance()->log("Tfeed", Log::DEBUG, "written");\r
- }\r
- else\r
- {\r
- //MILLISLEEP(100);\r
- MILLISLEEP(20); //Performance Issue Marten\r
- }\r
- }\r
+/*\r
+ Copyright 2008 Marten Richter\r
+ This file is part of VOMP.\r
+ 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.\r
+ 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.\r
+ 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.
+*/\r
+\r
+#include "tfeed.h"\r
+#include "log.h"\r#include "demuxer.h"\r#include "callback.h"\r
+\r
+TFeed::TFeed(Callback* tcb)\r: cb(*tcb)\r{\r teletextEnabled = 1;\r}\r
+\r
+int TFeed::init()\r{\r return 1;\r}\r
+\r
+int TFeed::shutdown()\r{\r
+ // FIXME\r return 1;\r}\r
+\r
+void TFeed::disable()\r
+{\r teletextEnabled = 0;
+}\r
+\r
+void TFeed::enable()
+{\r teletextEnabled = 1;\r
+}\r
+\r
+int TFeed::start()
+{
+ teletextEnabled = 1;
+ return threadStart();
+}\r
+\r
+void TFeed::stop()
+{
+ threadCancel();
+}\r
+\r
+void TFeed::threadMethod()
+{
+ bool tlen;\r
+ while(1)\r {
+ threadCheckExit();
+ tlen = Demuxer::getInstance()->writeTeletext();\r
+ if (tlen)
+ {
+ cb.call(this);
+// Log::getInstance()->log("Tfeed", Log::DEBUG, "written");
+ }
+ else
+ {
+ //MILLISLEEP(100);
+ MILLISLEEP(20); //Performance Issue Marten
+ }
+ }
}\r
\ No newline at end of file
-/*\r Copyright 2008 Marten Richter\r\r
- This file is part of VOMP.\r\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r\r\r
-#ifndef TFEED_H\r#define TFEED_H\r\r
-#include <stdio.h>\r#include <time.h>\r\r
-#include "threadsystem.h"\r\r
-class Callback;\r\r
-class TFeed: public Thread_TYPE {\rpublic:\r TFeed(Callback* tcb);\r int init();\r int shutdown();\r int start();\r void stop();\r void enable();\r void disable();\r\rprivate:\r\r void threadMethod();\r void threadPostStopCleanup() {\r };\r\r int teletextEnabled;\r Callback& cb;\r};\r\r
-\r\r
-#endif\r\r
+/*\r Copyright 2008 Marten Richter\r
+ This file is part of VOMP.\r
+ 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.\r
+ 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.\r
+ 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.
+*/\r\r
+#ifndef TFEED_H\r#define TFEED_H\r
+#include <stdio.h>\r#include <time.h>\r
+#include "threadsystem.h"\r
+class Callback;\r
+class TFeed: public Thread_TYPE {\rpublic:\r TFeed(Callback* tcb);\r int init();\r int shutdown();\r int start();\r void stop();\r void enable();\r void disable();\r\rprivate:\r\r void threadMethod();\r void threadPostStopCleanup() {\r };\r\r int teletextEnabled;\r Callback& cb;\r};\r
+\r
+#endif\r
-/*\r
- Copyright 2011 Marten Richter\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef WIN32\r
-#ifdef __ANDROID__\r
-#include "threadpandroid.h"\r
-#else\r
-#include "threadp.h"\r
-#endif\r
-#else\r
-#include "threadwin.h"\r
-#endif\r
+/*
+ Copyright 2011 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 WIN32
+#ifdef __ANDROID__
+#include "threadpandroid.h"
+#else
+#include "threadp.h"
+#endif
+#else
+#include "threadwin.h"
+#endif
-/*\r
- Copyright 2004-2005 Chris Tallon, Andreas Vogel\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#include "vcolourtuner.h"\r
-\r
-#include "wsymbol.h"\r
-#include "remote.h"\r
-#include "colour.h"\r
-#include "video.h"\r
-#include "vinfo.h"\r
-#include "boxstack.h"\r
-#include "i18n.h"\r
-#include "log.h"\r
-#include "mediaoptions.h"\r
+/*
+ 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"
#ifdef VOMP_PLATTFORM_MVP
#include "surfacemvp.h"
#endif
-\r
-#define PICTUREFILE "/colourtest.jpg"\r
-\r
-int VColourTuner::rfactor=100;\r
-int VColourTuner::gfactor=100;\r
-int VColourTuner::bfactor=100;\r
-\r
-VColourTuner::VColourTuner()\r
-{\r
- int sw= Video::getInstance()->getScreenWidth();\r
- int sh= Video::getInstance()->getScreenHeight();\r
- setSize(sw-80,sh-40);\r
- setPosition((sw-area.w)/2, (sh-area.h)/2);\r
- createBuffer();\r
- setTitleBarOn(0);\r
- picture.setPosition(160,60);\r
- add(&picture);\r
- drawPicture=true;\r
- vrfactor=rfactor;\r
- vbfactor=bfactor;\r
- vgfactor=gfactor;\r
- hasChanged=false;\r
- Log::getInstance()->log("VColourTuner",Log::DEBUG,"created %p",this);\r
-}\r
-\r
-VColourTuner::~VColourTuner()\r
-{\r
- Log::getInstance()->log("VColourTuner",Log::DEBUG,"deleted %p",this);\r
-}\r
-\r
-void VColourTuner::drawBox(int x, int y, int w, int h, DrawStyle &c) {\r
- for (int row=y;row<y+h;row++)\r
- for (int col=x;col<x+w;col++) {\r
- surface->drawPixel(col,row,c);\r
- }\r
-}\r
-\r
-\r
-void VColourTuner::draw()\r
-{\r
- //do not call base classes draw to avoid drawing the picture...\r
- Log::getInstance()->log("VColourTuner::draw",Log::DEBUG,"dp %s, rf=%d, gf=%d, bf=%d",\r
- (drawPicture?"true":"false"),vrfactor,vgfactor,vbfactor);\r
- char valbuf[20];\r
- int x=20;\r
- int y=20;\r
- int bw=50;\r
- int bh=50;\r
- int picx=picture.getX();\r
- DrawStyle bc=DrawStyle(140,140,140);\r
- fillColour(bc);\r
- bc=DrawStyle(255,255,255);\r
- drawText(tr("Colour Tuning"), x+20, y+5, DrawStyle::LIGHTTEXT);\r
- drawBox(x, y+50, bw, bh, DrawStyle::RED);\r
- drawBox(x, y+130, bw, bh, DrawStyle::GREEN);\r
- drawBox(x, y+190, bw, bh, DrawStyle::BLUE);\r
- drawBox(x, y+270, bw, bh, bc);\r
- sprintf(valbuf,"%03d%%",vrfactor);\r
- drawText(valbuf,x+bw+x,y+50, DrawStyle::LIGHTTEXT);\r
- drawText("<1 2>",x+bw+x,y+74, DrawStyle::LIGHTTEXT);\r
- sprintf(valbuf,"%03d%%",vgfactor);\r
- drawText(valbuf,x+bw+x,y+120, DrawStyle::LIGHTTEXT);\r
- drawText("<4 5>",x+bw+x,y+144, DrawStyle::LIGHTTEXT);\r
- sprintf(valbuf,"%03d%%",vbfactor);\r
- drawText(valbuf,x+bw+x,y+190, DrawStyle::LIGHTTEXT);\r
- drawText("<7 8>",x+bw+x,y+214, DrawStyle::LIGHTTEXT);\r
- sprintf(valbuf,"%03d%%",(vbfactor+vgfactor+vrfactor)/3);\r
- drawText(valbuf,x+bw+x,y+270, DrawStyle::LIGHTTEXT);\r
- drawText("<3 6>",x+bw+x,y+294, DrawStyle::LIGHTTEXT);\r
- drawText("9 norm",x+bw+x,y+318, DrawStyle::LIGHTTEXT);\r
- drawText(tr("OK to save, BACK to cancel"), x+20, area.h - 50, DrawStyle::LIGHTTEXT);\r
- if (drawPicture) {\r
- hasChanged=false;\r
- picture.init(PICTUREFILE);\r
- picture.draw();\r
- drawPicture=false;\r
- }\r
- int picy=picture.getY();\r
- int pich=picture.getHeight();\r
- if (hasChanged) drawText(tr("0 to draw picture"), picx+30, picy+pich+10, DrawStyle::LIGHTTEXT);\r
-}\r
-\r
-int VColourTuner::handleCommand(int command)\r
-{\r
- int rt=0;\r
- switch(command) {\r
- case Remote::ONE:\r
- updateFactor(1,-1);\r
- rt=2;\r
- hasChanged=true;\r
- break;\r
- case Remote::TWO:\r
- updateFactor(1,1);\r
- rt=2;\r
- hasChanged=true;\r
- break;\r
- case Remote::FOUR:\r
- updateFactor(2,-1);\r
- rt=2;\r
- hasChanged=true;\r
- break;\r
- case Remote::FIVE:\r
- updateFactor(2,1);\r
- rt=2;\r
- hasChanged=true;\r
- break;\r
- case Remote::SEVEN:\r
- updateFactor(3,-1);\r
- rt=2;\r
- hasChanged=true;\r
- break;\r
- case Remote::EIGHT:\r
- updateFactor(3,1);\r
- hasChanged=true;\r
- rt=2;\r
- break;\r
- case Remote::THREE:\r
- updateFactor(4,-1);\r
- hasChanged=true;\r
- rt=2;\r
- break;\r
- case Remote::SIX:\r
- updateFactor(4,1);\r
- hasChanged=true;\r
- rt=2;\r
- break;\r
- case Remote::NINE:\r
- updateFactor(5,0);\r
- hasChanged=true;\r
- rt=2;\r
- break;\r
- case Remote::ZERO:\r
- drawPicture=true;\r
- rt=2;\r
- break;\r
- case Remote::BACK:\r
- vrfactor=rfactor;\r
- vgfactor=gfactor;\r
- vbfactor=bfactor;\r
-#ifndef WIN32\r
-#ifndef _MIPS_ARCH\r
-#ifndef __ANDROID__\r
- ((Surface_TYPE *)surface)->initConversionTables(vrfactor,vgfactor,vbfactor);\r
-#endif\r
-#endif\r
-#endif\r
- rt=4;\r
- break;\r
- case Remote::OK:\r
- rfactor=vrfactor;\r
- gfactor=vgfactor;\r
- bfactor=vbfactor;\r
- MediaOptions::getInstance()->setIntOption("FactorRed",rfactor);\r
- MediaOptions::getInstance()->setIntOption("FactorGreen",gfactor);\r
- MediaOptions::getInstance()->setIntOption("FactorBlue",bfactor);\r
- rt=4;\r
- break;\r
- }\r
- if (rt == 2) {\r
-#ifndef WIN32\r
-#ifndef _MIPS_ARCH\r
-#ifndef __ANDROID__\r
- ((Surface_TYPE *)surface)->initConversionTables(vrfactor,vgfactor,vbfactor);\r
-#endif\r
-#endif\r
-#endif\r
- bool updateAll=drawPicture;\r
- draw();\r
- if (updateAll) {\r
- BoxStack::getInstance()->update(this);\r
- }\r
- else {\r
- Region r;\r
- r.x=0;\r
- r.w=picture.getX()-1;\r
- r.y=0;\r
- r.h=area.h;\r
- BoxStack::getInstance()->update(this,&r);\r
- r.x=picture.getX();\r
- r.y=picture.getY();\r
- r.h=area.h-r.y;\r
- r.w=area.w-picture.getX();\r
- BoxStack::getInstance()->update(this,&r);\r
- }\r
- }\r
- return rt;\r
-}\r
-\r
-\r
-\r
-void VColourTuner::processMessage(Message* m)\r
-{\r
- if (m->message == Message::MOUSE_MOVE)\r
- {\r
- \r
- }\r
- else if (m->message == Message::MOUSE_LBDOWN)\r
- {\r
- //check if press is outside this view! then simulate cancel\r
- int x=(m->parameter>>16)-getScreenX();\r
- int y=(m->parameter&0xFFFF)-getScreenY();\r
- if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight())\r
- {\r
- BoxStack::getInstance()->handleCommand(Remote::BACK); //simulate cancel press\r
- }\r
- else if (y>=(int)area.h-24 && y<=(int)area.h-6)\r
- {\r
- ;\r
- }\r
- }\r
-}\r
-\r
-void VColourTuner::updateFactor(int color, int amount) {\r
- switch (color) {\r
- case 1:\r
- vrfactor+=amount;\r
- if (vrfactor < 20 ) vrfactor=20;\r
- if (vrfactor > 200) vrfactor=200;\r
- break;\r
- case 2:\r
- vgfactor+=amount;\r
- if (vgfactor < 20 ) vgfactor=20;\r
- if (vgfactor > 200) vgfactor=200;\r
- break;\r
- case 3:\r
- vbfactor+=amount;\r
- if (vbfactor < 20 ) vbfactor=20;\r
- if (vbfactor > 200) vbfactor=200;\r
- break;\r
- case 4:\r
- updateFactor(1,amount);\r
- updateFactor(2,amount);\r
- updateFactor(3,amount);\r
- break;\r
- case 5:\r
- while ((vrfactor+vbfactor+vgfactor) > 300) updateFactor(4,-1);\r
- while ((vrfactor+vbfactor+vgfactor) < 300) updateFactor(4,1);\r
- }\r
-}\r
-\r
-void VColourTuner::initFactors(){\r
- MediaOptions * options=MediaOptions::getInstance();\r
- int rf=options->getIntOption("FactorRed");\r
- int gf=options->getIntOption("FactorGreen");\r
- int bf=options->getIntOption("FactorBlue");\r
- if (rf >= 20 && bf >= 20 && gf >= 20)\r
- rfactor=rf;\r
- gfactor=gf;\r
- bfactor=bf;\r
- Log::getInstance()->log("VColourTuner",Log::DEBUG,"setting initial factors r=%d,g=%d,b=%d",rf,gf,bf);\r
-#ifndef __ANDROID__\r
-#ifndef WIN32\r
-#ifndef _MIPS_ARCH\r
- Surface_TYPE::initConversionTables(rfactor,gfactor,bfactor);\r
-#endif\r
-#endif\r
-#endif\r
-}\r
+
+#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, DrawStyle &c) {
+ for (int row=y;row<y+h;row++)
+ for (int col=x;col<x+w;col++) {
+ surface->drawPixel(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();
+ DrawStyle bc=DrawStyle(140,140,140);
+ fillColour(bc);
+ bc=DrawStyle(255,255,255);
+ drawText(tr("Colour Tuning"), x+20, y+5, DrawStyle::LIGHTTEXT);
+ drawBox(x, y+50, bw, bh, DrawStyle::RED);
+ drawBox(x, y+130, bw, bh, DrawStyle::GREEN);
+ drawBox(x, y+190, bw, bh, DrawStyle::BLUE);
+ drawBox(x, y+270, bw, bh, bc);
+ sprintf(valbuf,"%03d%%",vrfactor);
+ drawText(valbuf,x+bw+x,y+50, DrawStyle::LIGHTTEXT);
+ drawText("<1 2>",x+bw+x,y+74, DrawStyle::LIGHTTEXT);
+ sprintf(valbuf,"%03d%%",vgfactor);
+ drawText(valbuf,x+bw+x,y+120, DrawStyle::LIGHTTEXT);
+ drawText("<4 5>",x+bw+x,y+144, DrawStyle::LIGHTTEXT);
+ sprintf(valbuf,"%03d%%",vbfactor);
+ drawText(valbuf,x+bw+x,y+190, DrawStyle::LIGHTTEXT);
+ drawText("<7 8>",x+bw+x,y+214, DrawStyle::LIGHTTEXT);
+ sprintf(valbuf,"%03d%%",(vbfactor+vgfactor+vrfactor)/3);
+ drawText(valbuf,x+bw+x,y+270, DrawStyle::LIGHTTEXT);
+ drawText("<3 6>",x+bw+x,y+294, DrawStyle::LIGHTTEXT);
+ drawText("9 norm",x+bw+x,y+318, DrawStyle::LIGHTTEXT);
+ drawText(tr("OK to save, BACK to cancel"), x+20, area.h - 50, DrawStyle::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, DrawStyle::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
+}
-/*\r
- Copyright 2004-2008 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#include "vdr.h"\r
-\r
-#include "recman.h"\r
-#include "tcp.h"\r
-#include "log.h"\r
-#include "recinfo.h"\r
-#include "dsock.h"\r
-#include "channel.h"\r
-#include "event.h"\r
-#include "wol.h"\r
-#include "vdrrequestpacket.h"\r
-#include "vdrresponsepacket.h"\r
-#include "command.h"\r
-#include "media.h"\r
-#include "mediaprovider.h"\r
-#include "mediaproviderids.h"\r
-#include "vdrcommand.h"\r
-#include "video.h"\r
-#include "osd.h"\r
-\r
-#define VOMP_PROTOCOLL_VERSION 0x00000100\r
-\r
-VDR* VDR::instance = NULL;\r
-//prepare a request\r
-//will create a request package from a command variable and fill this\r
-//caller has to destroy buffer\r
-static SerializeBuffer * prepareRequest(VDR_Command *cmd) {\r
- SerializeBuffer *buf=new SerializeBuffer(512,false,true);\r
- if (cmd->serialize(buf) != 0) {\r
- delete buf;\r
- return NULL;\r
- }\r
- return buf;\r
-}\r
-\r
-//handle request/response\r
-//TODO avoid copy of buffer (needs extension of requestpacket)\r
-//TODO avoid command 2x (needs some more restructuring)\r
-SerializeBuffer * VDR::doRequestResponse(SerializeBuffer *rq,int cmd) {\r
- VDR_RequestPacket *rt=new VDR_RequestPacket;\r
- if (! rt) {\r
- delete rq;\r
- return NULL;\r
- }\r
- if (! rt->init(cmd,true,rq->getCurrent()-rq->getStart())) {\r
- delete rq;\r
- delete rt;\r
- return NULL;\r
- }\r
- if (! rt->copyin(rq->getStart(),(ULONG)(rq->getCurrent()-rq->getStart()))) {\r
- delete rq;\r
- delete rt;\r
- return NULL;\r
- }\r
- delete rq;\r
- VDR_ResponsePacket *rp=RequestResponse(rt);\r
- logger->log("doRequestResponse",Log::DEBUG,"got response %p",rp);\r
- if ( !rp) {\r
- delete rt;\r
- return NULL;\r
- }\r
- SerializeBuffer *buf=new SerializeBuffer(rp->getUserData(),rp->getUserDataLength(),true,true,false);\r
- delete rp;\r
- return buf;\r
-}\r
-\r
-\r
-\r
-//deserialize a received response\r
-//delete the package\r
-//return !=0 on error\r
-static int decodeResponse(SerializeBuffer *rp,VDR_Command *c) {\r
- ULONG expected=c->command;\r
- if (c->deserialize(rp) != 0) {\r
- delete rp;\r
- Log::getInstance()->log("VDR", Log::ERR, "decodeResponse unable to deserialize for command %lu",expected);\r
- return -1;\r
- }\r
- delete rp;\r
- if (c->command != expected) {\r
- Log::getInstance()->log("VDR", Log::ERR, "decodeResponse unexpected response received 0x%lx, expected 0x%lx",c->command,expected);\r
- return -1;\r
- }\r
- Log::getInstance()->log("VDR", Log::DEBUG, "decodeResponse successfully decoded command 0x%lx",expected);\r
- return 0;\r
-}\r
-\r
-\r
-\r
-VDR::VDR()\r
-{\r
- if (instance) return;\r
- instance = this;\r
- initted = 0;\r
- findingServer = 0;\r
- tcp = NULL;\r
- connected = false;\r
- maxChannelNumber = 0;\r
- channelNumberWidth = 1;\r
- TEMP_SINGLE_VDR_PR = NULL;\r
- providerId=MPROVIDERID_VDR;\r
- subRange=MPROVIDERRANGE_VDR;\r
- MediaPlayerRegister::getInstance()->registerMediaProvider(this,MPROVIDERID_VDR,MPROVIDERRANGE_VDR);\r
-}\r
-\r
-VDR::~VDR()\r
-{\r
- instance = NULL;\r
- if (initted) shutdown();\r
-}\r
-\r
-VDR* VDR::getInstance()\r
-{\r
- return instance;\r
-}\r
-\r
-int VDR::init(int tport)\r
-{\r
- if (initted) return 0;\r
- initted = 1;\r
- port = tport;\r
- logger = Log::getInstance();\r
- return 1;\r
-}\r
-\r
-int VDR::shutdown()\r
-{\r
- if (!initted) return 0;\r
- initted = 0;\r
- disconnect();\r
- return 1;\r
-}\r
-\r
-void VDR::findServers(vector<VDRServer>& servers)\r
-{\r
- Wol* wol = Wol::getInstance();\r
- findingServer = 1;\r
- char* message = "VOMP";\r
-\r
- DatagramSocket ds(port);\r
- int haveAtLeastOne = 0;\r
- int retval;\r
- int waitType = 1;\r
- bool firstloop = true;\r
- while(findingServer)\r
- {\r
- if (waitType == 1)\r
- {\r
- ds.shutdown();\r
- ds.init();\r
- logger->log("VDR", Log::NOTICE, "Broadcasting for server");\r
- ds.send("255.255.255.255", 3024, message, strlen(message));\r
- if(!firstloop) wol->doWakeUp();\r
- }\r
- retval = ds.waitforMessage(waitType);\r
-\r
- if (retval == 2) // we got a reply\r
- {\r
- if (!strcmp(ds.getData(), "VOMP")) // echo.....\r
- {\r
- waitType = 2;\r
- }\r
- else\r
- {\r
- VDRServer newServer;\r
- newServer.ip = new char[16];\r
- strcpy(newServer.ip, ds.getFromIPA());\r
-\r
- if (ds.getDataLength() == 0)\r
- {\r
- newServer.name = new char[1];\r
- newServer.name[0] = '\0';\r
- }\r
- else\r
- {\r
- newServer.name = new char[strlen(ds.getData())+1];\r
- strcpy(newServer.name, ds.getData());\r
- }\r
-\r
- servers.push_back(newServer);\r
- waitType = 2;\r
- haveAtLeastOne = 1;\r
- }\r
- }\r
- else\r
- {\r
- if (haveAtLeastOne) break;\r
- waitType = 1;\r
- firstloop = false;\r
-/* For DEBUGGING *\r
- { VDRServer newServer;\r
- newServer.ip = new char[16];\r
- strcpy(newServer.ip, "192.168.1.7");\r
- newServer.name = new char[6];\r
- strcpy(newServer.name,"debug");\r
- servers.push_back(newServer);\r
- waitType = 2;\r
- haveAtLeastOne = 1;}/**/\r
-\r
-\r
-\r
- }\r
- }\r
- logger->log("VDR", Log::NOTICE, "END loop");\r
- sort(servers.begin(), servers.end(), ServerSorter());\r
-}\r
-\r
-void VDR::cancelFindingServer()\r
-{\r
- findingServer = 0;\r
-}\r
-\r
-void VDR::setServerIP(char* newIP)\r
-{\r
- strcpy(serverIP, newIP);\r
-}\r
-\r
-int VDR::connect()\r
-{\r
- maxChannelNumber = 0;\r
- channelNumberWidth = 1;\r
-\r
- if (tcp) delete tcp;\r
- tcp = new TCP();\r
- if (tcp->connectTo(serverIP, 3024))\r
- {\r
- connected = true;\r
- threadStart();\r
- return 1;\r
- }\r
- else\r
- {\r
- return 0;\r
- }\r
-}\r
-\r
-void VDR::disconnect()\r
-{\r
- threadCancel();\r
- if (tcp) delete tcp;\r
- tcp = NULL;\r
- connected = false;\r
- logger->log("VDR", Log::DEBUG, "Disconnect");\r
-}\r
-\r
-void VDR::setReceiveWindow(size_t size)\r
-{\r
- if (connected) tcp->setReceiveWindow(size);\r
-}\r
-\r
-///////////////////////////////////////////////////////\r
-\r
-void VDR::threadMethod()\r
-{\r
- logger->log("VDR", Log::DEBUG, "VDR RUN"); \r
-\r
- threadSetKillable(); // FIXME - change this to deal with the EDRs\r
- \r
- ULONG channelID;\r
- \r
- ULONG requestID;\r
- ULONG userDataLength;\r
- UCHAR* userData;\r
-\r
- ULONG streamID;\r
- ULONG flag;\r
-\r
- VDR_ResponsePacket* vresp;\r
- \r
- ULONG timeNow = 0;\r
- ULONG lastKAsent = 0;\r
- ULONG lastKArecv = time(NULL);\r
- int readSuccess;\r
-\r
- while(1)\r
- {\r
- timeNow = time(NULL);\r
- \r
- readSuccess = tcp->readData((UCHAR*)&channelID, sizeof(ULONG)); // 2s timeout atm\r
-\r
- if (!readSuccess)\r
- {\r
- //logger->log("VDR", Log::DEBUG, "Net read timeout");\r
- if (!tcp->isConnected()) { connectionDied(); return; } // return to stop this thread\r
- }\r
- \r
- // Error or timeout.\r
-\r
- if (!lastKAsent) // have not sent a KA\r
- {\r
- if (lastKArecv < (timeNow - 5))\r
- {\r
- logger->log("VDR", Log::DEBUG, "Sending KA packet");\r
- if (!sendKA(timeNow))\r
- {\r
- logger->log("VDR", Log::DEBUG, "Could not send KA, calling connectionDied");\r
- connectionDied();\r
- return;\r
- }\r
- lastKAsent = timeNow;\r
- }\r
- }\r
- else\r
- {\r
- if (lastKAsent <= (timeNow - 10))\r
- {\r
- logger->log("VDR", Log::DEBUG, "lastKA over 10s ago, calling connectionDied");\r
- connectionDied();\r
- return;\r
- } \r
- }\r
-\r
- if (!readSuccess) continue; // no data was read but the connection is ok.\r
- \r
- // Data was read\r
- \r
- channelID = ntohl(channelID);\r
- \r
- if (channelID == CHANNEL_REQUEST_RESPONSE)\r
- {\r
- if (!tcp->readData((UCHAR*)&requestID, sizeof(ULONG))) break;\r
- requestID = ntohl(requestID);\r
- if (!tcp->readData((UCHAR*)&userDataLength, sizeof(ULONG))) break;\r
- userDataLength = ntohl(userDataLength);\r
- if (userDataLength > 5000000) break; // how big can these packets get?\r
- userData = NULL;\r
- if (userDataLength > 0)\r
- {\r
- userData = (UCHAR*)malloc(userDataLength);\r
- if (!userData) break;\r
- if (!tcp->readData(userData, userDataLength)) break;\r
- }\r
-\r
- vresp = new VDR_ResponsePacket(); \r
- vresp->setResponse(requestID, userData, userDataLength);\r
- logger->log("VDR", Log::DEBUG, "Rxd a response packet, requestID=%lu, len=%lu", requestID, userDataLength);\r
-\r
- if (!edFindAndCall(vresp)) // makes ED lock, find receiver for vresp (using ed_cb_find() ) and then call (using ed_cb_call() )\r
- {\r
- // If edFindAndCall returns true, edr was called and vresp was handed off.\r
- // else, delete vresp here.\r
- delete vresp;\r
- }\r
- }\r
- else if (channelID == CHANNEL_STREAM)\r
- {\r
- if (!tcp->readData((UCHAR*)&streamID, sizeof(ULONG))) break;\r
- streamID = ntohl(streamID);\r
-\r
- if (!tcp->readData((UCHAR*)&flag, sizeof(ULONG))) break;\r
- flag = ntohl(flag);\r
-\r
- if (!tcp->readData((UCHAR*)&userDataLength, sizeof(ULONG))) break; \r
- userDataLength = ntohl(userDataLength);\r
- userData = NULL;\r
- if (userDataLength > 0)\r
- {\r
- userData = (UCHAR*)malloc(userDataLength);\r
- if (!userData) break;\r
- if (!tcp->readData(userData, userDataLength)) break;\r
- }\r
-\r
- vresp = new VDR_ResponsePacket(); \r
- vresp->setStream(streamID, flag, userData, userDataLength);\r
-// logger->log("VDR", Log::DEBUG, "Rxd a stream packet, streamID=%lu, flag=%lu, len=%lu", streamID, flag, userDataLength);\r
-\r
- if (!edFindAndCall(vresp)) // makes ED lock, find receiver for vresp (using ed_cb_find() ) and then call (using ed_cb_call() )\r
- {\r
- // If edFindAndCall returns true, edr was called and vresp was handed off.\r
- // else, delete vresp here.\r
- delete vresp;\r
- }\r
- }\r
- else if (channelID == CHANNEL_KEEPALIVE)\r
- {\r
- ULONG KAreply = 0;\r
- if (!tcp->readData((UCHAR*)&KAreply, sizeof(ULONG))) break;\r
- KAreply = (ULONG)ntohl(KAreply);\r
- if (KAreply == lastKAsent) // successful KA response\r
- {\r
- lastKAsent = 0;\r
- lastKArecv = KAreply;\r
- logger->log("VDR", Log::DEBUG, "Rxd correct KA reply");\r
- }\r
- }\r
- else\r
- {\r
- logger->log("VDR", Log::ERR, "Rxd a response packet on channel %lu !!", channelID);\r
- break;\r
- }\r
- threadCheckExit();\r
-\r
-\r
- // Who deletes vresp?\r
- // If RR, the individual protocol functions must delete vresp.\r
- // If stream, the data and length is taken out in ed_cb_call and vresp is deleted there.\r
- }\r
- \r
- connectionDied();\r
-}\r
-\r
-void VDR::connectionDied()\r
-{\r
- // Called from within threadMethod to do cleanup if it decides the connection has died\r
-\r
- connected = false; // though actually it could still be connected until someone calls vdr->disconnect\r
-\r
- // Need to wake up any waiting channel 1 request-response threads\r
- // Normally this is done by a packet coming in with channelid and requestid \r
- // Instead, go through the list and for each channel 1 edr, make an empty vresp\r
- // An empty vresp will have userData == NULL, which means vresp->noResponse() == true\r
-\r
- // If it's a stream receiver, generate a stream packet with flag == connection_lost\r
-\r
- edLock();\r
- VDR_PacketReceiver* vdrpr;\r
- VDR_ResponsePacket* vresp;\r
- while(receivers.size())\r
- {\r
- vdrpr = (VDR_PacketReceiver*) *(receivers.begin());\r
- if (vdrpr->receiverChannel == CHANNEL_REQUEST_RESPONSE)\r
- {\r
- vresp = new VDR_ResponsePacket();\r
- vresp->setResponse(vdrpr->requestSerialNumber, NULL, 0);\r
- logger->log("VDR", Log::DEBUG, "Timeouts: created blank response packet for request serial %lu", vdrpr->requestSerialNumber);\r
- edUnlock();\r
- if (!edFindAndCall(vresp)) // makes ED lock, find receiver for vresp (using ed_cb_find() ) and then call (using ed_cb_call() )\r
- {\r
- // If edFindAndCall returns true, edr was called and vresp was handed off.\r
- // else, delete vresp here.\r
- logger->log("VDR", Log::ERR, "Timeouts: no waiting thread found for request serial %lu !!!", vdrpr->requestSerialNumber);\r
- delete vresp;\r
- }\r
- edLock();\r
- }\r
- else if (vdrpr->receiverChannel == CHANNEL_STREAM)\r
- {\r
- vresp = new VDR_ResponsePacket();\r
- vresp->setStream(vdrpr->streamID, 2 /* connection-lost flag */ , NULL, 0);\r
- logger->log("VDR", Log::DEBUG, "Timeouts: created blank response packet for streamid %lu", vdrpr->streamID);\r
- edUnlock();\r
- if (!edFindAndCall(vresp)) // makes ED lock, find receiver for vresp (using ed_cb_find() ) and then call (using ed_cb_call() )\r
- {\r
- // If edFindAndCall returns true, edr was called and vresp was handed off.\r
- // else, delete vresp here.\r
- logger->log("VDR", Log::ERR, "Timeouts: no waiting stream receiver found for streamid %lu !!!", vdrpr->streamID);\r
- delete vresp;\r
- }\r
- edLock();\r
- \r
- for(EDRL::iterator i = receivers.begin(); i != receivers.end(); i++)\r
- if ((VDR_PacketReceiver*)*i == vdrpr) { receivers.erase(i); break; }\r
- }\r
- }\r
- edUnlock();\r
- // Ok, all event receviers should be dealt with. just in case there weren't any, inform command\r
- logger->log("VDR", Log::DEBUG, "edUnlock at end of connectionDied");\r
-\r
- Command::getInstance()->connectionLost();\r
-}\r
-\r
-bool VDR::ed_cb_find(EDReceiver* edr, void* userTag)\r
-{\r
- // edr is a VDR_PacketReceiver object made in VDR::RequestResponse\r
- // userTag is a VDR_ResponsePacket made in threadMethod\r
-\r
- VDR_PacketReceiver* vdrpr = (VDR_PacketReceiver*)edr;\r
- VDR_ResponsePacket* vresp = (VDR_ResponsePacket*)userTag;\r
- \r
- // Is vresp for vdrpr ?\r
- \r
- ULONG packetChannel = vresp->getChannelID();\r
- if (vdrpr->receiverChannel != packetChannel) return false;\r
-\r
- if (packetChannel == CHANNEL_REQUEST_RESPONSE)\r
- {\r
- if (vdrpr->requestSerialNumber == vresp->getRequestID()) return true;\r
- }\r
- else if (packetChannel == CHANNEL_STREAM)\r
- {\r
- if (vdrpr->streamID == vresp->getStreamID()) return true;\r
- }\r
- \r
- return false;\r
-}\r
-\r
-VDR_ResponsePacket* VDR::RequestResponse(VDR_RequestPacket* vrp)\r
-{\r
- //logger->log("VDR", Log::DEBUG, "RR %lu", vrp->getOpcode());\r
-\r
- if (!connected)\r
- {\r
- logger->log("VDR", Log::DEBUG, "RR when !connected");\r
- VDR_ResponsePacket* vresp = new VDR_ResponsePacket();\r
- return vresp; // "no-response" return\r
- }\r
-\r
- // ED make new VDR and register\r
- // make a VDR_PacketReceiver\r
- // - init with serial number of request packet\r
-\r
- VDR_PacketReceiver vdrpr;\r
-// vdrpr.requestTime = time(NULL);\r
- vdrpr.receiverChannel = VDR::CHANNEL_REQUEST_RESPONSE;\r
- vdrpr.requestSerialNumber = vrp->getSerial();\r
-\r
- edRegister(&vdrpr);\r
- \r
- edLock(); \r
-\r
- if ((ULONG)tcp->sendData(vrp->getPtr(), vrp->getLen()) != vrp->getLen())\r
- {\r
- edUnlock();\r
- edUnregister(&vdrpr);\r
- VDR_ResponsePacket* vresp = new VDR_ResponsePacket();\r
- return vresp; // "no-response" return\r
- }\r
-\r
- // Sleep and block this thread. The sleep unlocks the mutex\r
- logger->log("VDR", Log::DEBUG, "RR sleep - opcode %lu", vrp->getOpcode());\r
- edSleepThisReceiver(&vdrpr);\r
- logger->log("VDR", Log::DEBUG, "RR unsleep");\r
- \r
- // Woken because a response packet has arrived, mutex will be locked\r
- \r
- edUnlock();\r
- return vdrpr.save_vresp;\r
-}\r
-\r
-bool VDR::sendKA(ULONG timeStamp)\r
-{\r
- char buffer[8];\r
-\r
- int pos=0;\r
- ULONG ul=CHANNEL_KEEPALIVE;\r
- buffer[pos++]=(ul>>24)&0xff;\r
- buffer[pos++]=(ul>>16)&0xff;\r
- buffer[pos++]=(ul>>8)&0xff;\r
- buffer[pos++]=ul &0xff;\r
- ul=timeStamp;\r
- buffer[pos++]=(ul>>24)&0xff;\r
- buffer[pos++]=(ul>>16)&0xff;\r
- buffer[pos++]=(ul>>8)&0xff;\r
- buffer[pos++]=ul &0xff;\r
- if ((ULONG)tcp->sendData(buffer, 8) != 8) return false;\r
- return true;\r
-}\r
-\r
-/////////////////////////////////////////////////////////////////////////////\r
-\r
-// Here VDR takes a break for the VDR_PacketReceiver helper class\r
-\r
-bool VDR_PacketReceiver::call(void* userTag)\r
-{\r
- if (receiverChannel == VDR::CHANNEL_REQUEST_RESPONSE)\r
- {\r
- // It's a RR. Save vresp and, signal the waiting thread and return.\r
- // VDR::RequestResponse will be blocking waiting for this to happen.\r
- // That function has a pointer to this object and can read save_vresp.\r
- save_vresp = (VDR_ResponsePacket*)userTag;\r
- return true; // Signals ED to remove edr from receivers and wake up edr thread\r
- }\r
- \r
- if (receiverChannel == VDR::CHANNEL_STREAM)\r
- {\r
- // It's a stream packet.\r
- VDR_ResponsePacket* vresp = (VDR_ResponsePacket*)userTag;\r
- streamReceiver->streamReceive(vresp->getFlag(), vresp->getUserData(), vresp->getUserDataLength());\r
- delete vresp;\r
- return false;\r
- }\r
-\r
- abort(); // unknown receiverChannel, should not happen\r
-}\r
-\r
-/////////////////////////////////////////////////////////////////////////////\r
-\r
-int VDR::doLogin(unsigned int* v_server,unsigned int* v_client)\r
-{\r
- VDR_RequestPacket vrp;\r
- if (!vrp.init(VDR_LOGIN, true, 6)) return 0;\r
-\r
- char* mactemp[6];\r
- tcp->getMAC((char*)mactemp);\r
- if (!vrp.copyin((UCHAR*)mactemp, 6)) return 0;\r
-\r
- VDR_ResponsePacket* vresp = RequestResponse(&vrp);\r
- if (vresp->noResponse()) { delete vresp; return 0; }\r
-\r
- ULONG vdrTime = vresp->extractULONG();\r
- logger->log("VDR", Log::DEBUG, "vdrtime = %lu", vdrTime);\r
- long vdrTimeOffset = vresp->extractLONG();\r
- logger->log("VDR", Log::DEBUG, "offset = %i", vdrTimeOffset);\r
-\r
- unsigned int version=vresp->extractULONG();\r
-\r
- *v_server=version;\r
- *v_client=VOMP_PROTOCOLL_VERSION;\r
-\r
- delete vresp;\r
-\r
- if (version!=VOMP_PROTOCOLL_VERSION) {\r
-\r
- return 0;\r
-\r
- }\r
-\r
- // Set the time and zone on the MVP only\r
-\r
-#if !defined(WIN32) && !defined(__ANDROID__)\r
- struct timespec currentTime;\r
- currentTime.tv_sec = vdrTime;\r
- currentTime.tv_nsec = 0;\r
-\r
- int b = clock_settime(CLOCK_REALTIME, ¤tTime);\r
-\r
- logger->log("VDR", Log::DEBUG, "set clock = %u", b);\r
-\r
- // now make a TZ variable and set it\r
- char sign;\r
- int hours;\r
- int minutes;\r
- if (vdrTimeOffset > 0) sign = '-';\r
- else sign = '+';\r
-\r
- vdrTimeOffset = abs(vdrTimeOffset);\r
-\r
- hours = (int)vdrTimeOffset / 3600;\r
- minutes = vdrTimeOffset % 3600;\r
-\r
- logger->log("VDR", Log::DEBUG, "%c %i %i", sign, hours, minutes);\r
-\r
- minutes = (int)minutes / 60;\r
-\r
- logger->log("VDR", Log::DEBUG, "%c %i %i", sign, hours, minutes);\r
-\r
- char newTZ[30];\r
- sprintf(newTZ, "MVP%c%i:%i", sign, hours, minutes);\r
- setenv("TZ", newTZ, 1);\r
-\r
- logger->log("VDR", Log::DEBUG, "Timezone data: %s", newTZ);\r
-#endif\r
-\r
- setCharset(Osd::getInstance()->charSet());\r
-\r
- return 1;\r
-}\r
-\r
-bool VDR::LogExtern(const char* logString)\r
-{\r
- if (!connected) return false;\r
- int stringLength = strlen(logString);\r
- int packetLength = stringLength + 8;\r
- char *buffer=new char[packetLength + 1];\r
- int pos=0;\r
- ULONG ul=CHANNEL_NETLOG;\r
- buffer[pos++]=(ul>>24)&0xff;\r
- buffer[pos++]=(ul>>16)&0xff;\r
- buffer[pos++]=(ul>>8)&0xff;\r
- buffer[pos++]=ul &0xff;\r
- ul=stringLength;\r
- buffer[pos++]=(ul>>24)&0xff;\r
- buffer[pos++]=(ul>>16)&0xff;\r
- buffer[pos++]=(ul>>8)&0xff;\r
- buffer[pos++]=ul &0xff;\r
-\r
- strcpy(&buffer[8], logString);\r
- \r
- if ((ULONG)tcp->sendData(buffer, packetLength) != packetLength) {\r
- delete [] buffer;\r
- return false;\r
- }\r
- delete [] buffer;\r
- return true;\r
-}\r
-\r
-bool VDR::setCharset(int charset)\r
-{\r
- VDR_RequestPacket vrp;\r
- if (!vrp.init(VDR_SETCHARSET, true, sizeof(ULONG))) return false;\r
- if (!vrp.addULONG(charset)) return false;\r
-\r
- VDR_ResponsePacket* vresp = RequestResponse(&vrp);\r
- if (vresp->noResponse()) { delete vresp; return false; }\r
-\r
- ULONG success = vresp->extractULONG();\r
- delete vresp;\r
-\r
- if (!success) return false;\r
-\r
- return true;\r
-}\r
-\r
-bool VDR::getRecordingsList(RecMan* recman)\r
-{\r
- VDR_RequestPacket vrp;\r
- if (!vrp.init(VDR_GETRECORDINGLIST, true, 0)) return false;\r
-\r
- VDR_ResponsePacket* vresp = RequestResponse(&vrp);\r
- if (vresp->noResponse()) { delete vresp; return false; }\r
-\r
- ULONG totalSpace = vresp->extractULONG();\r
- ULONG freeSpace = vresp->extractULONG();\r
- ULONG percent = vresp->extractULONG();\r
-\r
- recman->setStats(totalSpace, freeSpace, percent);\r
-\r
- ULONG start;\r
- char* name;\r
- char* fileName;\r
-\r
- while (!vresp->end())\r
- {\r
-\r
- start = vresp->extractULONG();\r
- name = vresp->extractString();\r
- fileName = vresp->extractString();\r
- recman->addEntry(start, name, fileName);\r
- delete[] name;\r
- delete[] fileName;\r
- }\r
- delete vresp;\r
-\r
- return true;\r
-}\r
-\r
-int VDR::deleteRecording(char* fileName)\r
-{\r
- VDR_RequestPacket vrp;\r
- if (!vrp.init(VDR_DELETERECORDING, true, strlen(fileName) + 1)) return 0;\r
- if (!vrp.addString(fileName)) return 0;\r
- \r
- VDR_ResponsePacket* vresp = RequestResponse(&vrp);\r
- if (vresp->noResponse()) { delete vresp; return 0; }\r
- \r
- int toReturn = (int)vresp->extractULONG();\r
- delete vresp;\r
-\r
- return toReturn;\r
-}\r
-\r
-char* VDR::moveRecording(char* fileName, char* newPath)\r
-{\r
- VDR_RequestPacket vrp;\r
- if (!vrp.init(VDR_MOVERECORDING, true, strlen(fileName) + 1 + strlen(newPath) + 1)) return NULL;\r
- if (!vrp.addString(fileName)) return NULL;\r
- if (!vrp.addString(newPath)) return NULL;\r
- \r
- VDR_ResponsePacket* vresp = RequestResponse(&vrp);\r
- if (vresp->noResponse()) { delete vresp; return NULL; }\r
- \r
- char* toReturn = NULL;\r
- int success = (int)vresp->extractULONG();\r
- if (success == 1)\r
- {\r
- toReturn = vresp->extractString();\r
- }\r
-\r
- delete vresp;\r
-\r
- return toReturn;\r
-}\r
-\r
-ChannelList* VDR::getChannelsList(ULONG type)\r
-{\r
- VDR_RequestPacket vrp;\r
- if (!vrp.init(VDR_GETCHANNELLIST, true, 0)) return NULL;\r
-\r
- VDR_ResponsePacket* vresp = RequestResponse(&vrp);\r
- if (vresp->noResponse()) { delete vresp; return NULL; }\r
- \r
- ChannelList* chanList = new ChannelList();\r
-\r
- bool h264support=Video::getInstance()->supportsh264();\r
-\r
- while (!vresp->end())\r
- {\r
- Channel* chan = new Channel();\r
- chan->number = vresp->extractULONG();\r
- chan->type = vresp->extractULONG();\r
- chan->name = vresp->extractString();\r
- chan->vstreamtype = vresp->extractULONG();\r
-\r
- if (chan->type == type && (chan->vstreamtype!=0x1b || h264support))\r
- {\r
- chanList->push_back(chan);\r
- logger->log("VDR", Log::DEBUG, "Have added a channel to list. %lu %lu %s", chan->number, chan->type, chan->name);\r
- if (chan->number > maxChannelNumber) maxChannelNumber = chan->number;\r
- }\r
- else\r
- {\r
- delete chan;\r
- }\r
- }\r
-\r
- delete vresp;\r
-\r
- if (maxChannelNumber > 99999)\r
- channelNumberWidth = 6;\r
- else if (maxChannelNumber > 9999)\r
- channelNumberWidth = 5;\r
- else if (maxChannelNumber > 999)\r
- channelNumberWidth = 4;\r
- else if (maxChannelNumber > 99)\r
- channelNumberWidth = 3;\r
- else if (maxChannelNumber > 9)\r
- channelNumberWidth = 2;\r
- else\r
- channelNumberWidth = 1;\r
-\r
- return chanList;\r
-}\r
-\r
-int VDR::streamChannel(ULONG number, StreamReceiver* tstreamReceiver)\r
-{\r
- VDR_RequestPacket vrp;\r
- if (!vrp.init(VDR_STREAMCHANNEL, true, sizeof(ULONG))) return 0;\r
- if (!vrp.addULONG(number)) return 0;\r
- \r
- \r
- VDR_PacketReceiver* vdrpr = new VDR_PacketReceiver();\r
- vdrpr->receiverChannel = VDR::CHANNEL_STREAM;\r
- vdrpr->streamID = vrp.getSerial();\r
- vdrpr->streamReceiver = tstreamReceiver;\r
- edRegister(vdrpr);\r
- TEMP_SINGLE_VDR_PR = vdrpr;\r
- \r
- VDR_ResponsePacket* vresp = RequestResponse(&vrp);\r
- if (vresp->noResponse())\r
- {\r
- delete vresp;\r
- edUnregister(vdrpr);\r
- delete vdrpr;\r
- return 0;\r
- }\r
- \r
- int toReturn = (int)vresp->extractULONG();\r
- logger->log("VDR", Log::DEBUG, "VDR said %lu to start streaming request", toReturn);\r
- delete vresp;\r
-\r
- return toReturn;\r
-}\r
-\r
-int VDR::stopStreaming()\r
-{\r
- VDR_RequestPacket vrp;\r
- if (!vrp.init(VDR_STOPSTREAMING, true, 0)) return 0;\r
-\r
- if (TEMP_SINGLE_VDR_PR) // this block only needs to be done if it was a live stream\r
- // TEMP_SINGLE_VDR_PR will not be set unless we are streaming a channel\r
- {\r
- edUnregister(TEMP_SINGLE_VDR_PR);\r
- delete TEMP_SINGLE_VDR_PR;\r
- TEMP_SINGLE_VDR_PR = NULL;\r
- }\r
-\r
- VDR_ResponsePacket* vresp = RequestResponse(&vrp);\r
- if (vresp->noResponse()) { delete vresp; return 0; }\r
- \r
- int toReturn = (int)vresp->extractULONG();\r
- delete vresp;\r
-\r
- return toReturn;\r
-}\r
-\r
-UCHAR* VDR::getBlock(ULLONG position, UINT maxAmount, UINT* amountReceived)\r
-{\r
- VDR_RequestPacket vrp;\r
- if (!vrp.init(VDR_GETBLOCK, true, sizeof(ULLONG) + sizeof(ULONG))) return NULL;\r
- if (!vrp.addULLONG(position)) return NULL;\r
- if (!vrp.addULONG(maxAmount)) return NULL;\r
-\r
- VDR_ResponsePacket* vresp = RequestResponse(&vrp);\r
- if (vresp->noResponse()) { delete vresp; return NULL; }\r
-\r
- if (vresp->serverError())\r
- {\r
- logger->log("VDR", Log::DEBUG, "Detected getblock 0");\r
- delete vresp;\r
- return NULL;\r
- }\r
-\r
- // Special handling for getblock\r
- UCHAR* toReturn = vresp->getUserData();\r
- *amountReceived = vresp->getUserDataLength();\r
- \r
- delete vresp;\r
- \r
- return toReturn;\r
-}\r
-\r
-ULLONG VDR::streamRecording(char* fileName, ULONG* totalFrames, bool* IsPesRecording)\r
-{\r
- VDR_RequestPacket vrp;\r
- if (!vrp.init(VDR_STREAMRECORDING, true, strlen(fileName) + 1)) return 0;\r
- if (!vrp.addString(fileName)) return 0;\r
-\r
- VDR_ResponsePacket* vresp = RequestResponse(&vrp);\r
- if (vresp->noResponse()) { delete vresp; return 0; }\r
- \r
- ULLONG lengthBytes = vresp->extractULLONG();\r
- ULONG lengthFrames = vresp->extractULONG();\r
- UCHAR isPesRecording = vresp->extractUCHAR();\r
- delete vresp;\r
-\r
- *totalFrames = lengthFrames;\r
- *IsPesRecording = (isPesRecording);//convert Uchar to bool\r
-\r
- logger->log("VDR", Log::DEBUG, "VDR said length is: %llu %lu, IsPesRecording %x", lengthBytes, lengthFrames, *IsPesRecording);\r
-\r
- return lengthBytes;\r
-}\r
-\r
-ULLONG VDR::positionFromFrameNumber(ULONG frameNumber)\r
-{\r
- VDR_RequestPacket vrp;\r
- if (!vrp.init(VDR_POSFROMFRAME, true, sizeof(ULONG))) return 0;\r
- if (!vrp.addULONG(frameNumber)) return 0;\r
-\r
- VDR_ResponsePacket* vresp = RequestResponse(&vrp);\r
- if (vresp->noResponse()) { delete vresp; return 0; }\r
- \r
- ULLONG position = vresp->extractULLONG();\r
- delete vresp;\r
- \r
- logger->log("VDR", Log::DEBUG, "VDR said new position is: %llu", position);\r
-\r
- return position;\r
-}\r
-\r
-ULONG VDR::frameNumberFromPosition(ULLONG position)\r
-{\r
- VDR_RequestPacket vrp;\r
- if (!vrp.init(VDR_FRAMEFROMPOS, true, sizeof(ULLONG))) return 0;\r
- if (!vrp.addULLONG(position)) return 0;\r
-\r
- VDR_ResponsePacket* vresp = RequestResponse(&vrp);\r
- if (vresp->noResponse()) { delete vresp; return 0; }\r
- \r
- ULONG framenumber = vresp->extractULONG();\r
- delete vresp;\r
- \r
- logger->log("VDR", Log::DEBUG, "VDR said new framenumber is: %u", framenumber);\r
-\r
- return framenumber;\r
-}\r
-\r
-bool VDR::getNextIFrame(ULONG frameNumber, ULONG direction, ULLONG* rfilePosition, ULONG* rframeNumber, ULONG* rframeLength)\r
-{\r
- VDR_RequestPacket vrp;\r
- if (!vrp.init(VDR_GETNEXTIFRAME, true, sizeof(ULONG)*2)) return false;\r
- if (!vrp.addULONG(frameNumber)) return false;\r
- if (!vrp.addULONG(direction)) return false;\r
-\r
- VDR_ResponsePacket* vresp = RequestResponse(&vrp);\r
- if (vresp->noResponse()) { delete vresp; return false; }\r
- \r
- if (vresp->serverError())\r
- {\r
- logger->log("VDR", Log::DEBUG, "Detected getNextIFrame error");\r
- delete vresp;\r
- return false;\r
- }\r
-\r
- *rfilePosition = vresp->extractULLONG();\r
- *rframeNumber = vresp->extractULONG();\r
- *rframeLength = vresp->extractULONG();\r
-\r
- delete vresp;\r
-\r
- logger->log("VDR", Log::DEBUG, "VDR GNIF said %llu %lu %lu", *rfilePosition, *rframeNumber, *rframeLength);\r
-\r
- return true;\r
-}\r
-\r
-EventList* VDR::getChannelSchedule(ULONG number)\r
-{\r
- time_t now;\r
- time(&now);\r
- return getChannelSchedule(number, now, 24 * 60 * 60);\r
-}\r
-\r
-EventList* VDR::getChannelSchedule(ULONG number, time_t start, ULONG duration)\r
-{\r
-// retrieve event list (vector of events) from vdr within filter window. duration is in seconds\r
-\r
- VDR_RequestPacket vrp;\r
- if (!vrp.init(VDR_GETCHANNELSCHEDULE, true, sizeof(ULONG)*3)) return NULL;\r
- if (!vrp.addULONG(number)) return NULL;\r
- if (!vrp.addULONG(start)) return NULL;\r
- if (!vrp.addULONG(duration)) return NULL;\r
-\r
- VDR_ResponsePacket* vresp = RequestResponse(&vrp);\r
- if (vresp->noResponse()) { delete vresp; return NULL; }\r
- \r
- // received a ulong(0) - schedules error in the plugin\r
- if (vresp->serverError())\r
- {\r
- delete vresp;\r
- return NULL;\r
- }\r
-\r
- EventList* eventList = new EventList();\r
-\r
- while (!vresp->end())\r
- {\r
- Event* event = new Event();\r
- event->id = vresp->extractULONG();\r
- event->time = vresp->extractULONG();\r
- event->duration = vresp->extractULONG();\r
- event->title = vresp->extractString();\r
- event->subtitle = vresp->extractString();\r
- event->description = vresp->extractString();\r
- eventList->push_back(event);\r
- }\r
-\r
- delete vresp;\r
-\r
- logger->log("VDR", Log::DEBUG, "Success got to end of getChannelSchedule");\r
- return eventList;\r
-}\r
-\r
-int VDR::configSave(const char* section, const char* key, const char* value)\r
-{\r
- VDR_RequestPacket vrp;\r
- if (!vrp.init(VDR_CONFIGSAVE, false, 0)) return 0;\r
- if (!vrp.addString(section)) return 0;\r
- if (!vrp.addString(key)) return 0;\r
- if (!vrp.addString(value)) return 0;\r
-\r
- VDR_ResponsePacket* vresp = RequestResponse(&vrp);\r
- if (vresp->noResponse()) { delete vresp; return 0; }\r
-\r
- int toReturn = (int)vresp->extractULONG();\r
- delete vresp;\r
-\r
- return toReturn;\r
-}\r
-\r
-char* VDR::configLoad(const char* section, const char* key)\r
-{\r
- VDR_RequestPacket vrp;\r
- if (!vrp.init(VDR_CONFIGLOAD, false, 0)) return NULL;\r
- if (!vrp.addString(section)) return NULL;\r
- if (!vrp.addString(key)) return NULL;\r
-\r
- VDR_ResponsePacket* vresp = RequestResponse(&vrp);\r
- if (vresp->noResponse()) { delete vresp; return NULL; }\r
- \r
- char* toReturn = vresp->extractString();\r
- delete vresp;\r
-\r
- return toReturn;\r
-}\r
-\r
-RecTimerList* VDR::getRecTimersList()\r
-{\r
- VDR_RequestPacket vrp;\r
- if (!vrp.init(VDR_GETTIMERS, true, 0)) return NULL;\r
-\r
- VDR_ResponsePacket* vresp = RequestResponse(&vrp);\r
- if (vresp->noResponse()) { delete vresp; return NULL; }\r
-\r
- RecTimerList* recTimerList = new RecTimerList();\r
-\r
- ULONG numTimers = vresp->extractULONG();\r
- if (numTimers > 0)\r
- {\r
- RecTimer* newRecTimer;\r
- char* tempString;\r
-\r
- while (!vresp->end())\r
- {\r
- newRecTimer = new RecTimer();\r
- newRecTimer->active = vresp->extractULONG();\r
- newRecTimer->recording = vresp->extractULONG();\r
- newRecTimer->pending = vresp->extractULONG();\r
- newRecTimer->priority = vresp->extractULONG();\r
- newRecTimer->lifeTime = vresp->extractULONG();\r
- newRecTimer->channelNumber = vresp->extractULONG();\r
- newRecTimer->startTime = vresp->extractULONG();\r
- newRecTimer->stopTime = vresp->extractULONG();\r
- newRecTimer->day = vresp->extractULONG();\r
- newRecTimer->weekDays = vresp->extractULONG();\r
-\r
- tempString = vresp->extractString();\r
- newRecTimer->setFile(tempString);\r
- delete[] tempString;\r
-\r
- recTimerList->push_back(newRecTimer);\r
- logger->log("VDR", Log::DEBUG, "TL: %lu %lu %lu %lu %lu %lu %lu %lu %s",\r
- newRecTimer->active, newRecTimer->recording, newRecTimer->pending, newRecTimer->priority, newRecTimer->lifeTime,\r
- newRecTimer->channelNumber, newRecTimer->startTime, newRecTimer->stopTime, newRecTimer->getFile());\r
- }\r
- }\r
-\r
- delete vresp;\r
-\r
- sort(recTimerList->begin(), recTimerList->end(), RecTimerSorter());\r
-\r
- return recTimerList;\r
-}\r
-\r
-ULONG VDR::setEventTimer(char* timerString)\r
-{\r
- VDR_RequestPacket vrp;\r
- if (!vrp.init(VDR_SETTIMER, true, strlen(timerString) + 1)) return 0;\r
- if (!vrp.addString(timerString)) return 0;\r
-\r
- VDR_ResponsePacket* vresp = RequestResponse(&vrp);\r
- if (vresp->noResponse()) { delete vresp; return 0; }\r
- \r
- ULONG toReturn = vresp->extractULONG();\r
- delete vresp;\r
-\r
- return toReturn;\r
-}\r
-\r
-RecInfo* VDR::getRecInfo(char* fileName)\r
-{\r
- VDR_RequestPacket vrp;\r
- if (!vrp.init(VDR_GETRECINFO, true, strlen(fileName) + 1)) return NULL;\r
- if (!vrp.addString(fileName)) return NULL;\r
- VDR_ResponsePacket* vresp = RequestResponse(&vrp);\r
-\r
- if (vresp->noResponse()) { delete vresp; return NULL; }\r
-\r
- if (vresp->serverError())\r
- {\r
- logger->log("VDR", Log::DEBUG, "Could not get rec info");\r
- delete vresp;\r
- return NULL;\r
- }\r
-\r
- RecInfo* recInfo = new RecInfo();\r
-\r
- recInfo->timerStart = vresp->extractULONG();\r
- recInfo->timerEnd = vresp->extractULONG();\r
- recInfo->resumePoint = vresp->extractULONG();\r
- recInfo->summary = vresp->extractString();\r
-\r
- ULONG numComponents = vresp->extractULONG();\r
- if (numComponents)\r
- {\r
- recInfo->setNumComponents(numComponents);\r
- for (ULONG i = 0; i < numComponents; i++)\r
- {\r
- recInfo->streams[i] = vresp->extractUCHAR();\r
- recInfo->types[i] = vresp->extractUCHAR();\r
- recInfo->languages[i] = vresp->extractString();\r
- recInfo->descriptions[i] = vresp->extractString();\r
- }\r
- }\r
- recInfo->fps=vresp->extractdouble();\r
- \r
-\r
- recInfo->print();\r
-\r
- delete vresp;\r
- return recInfo;\r
-}\r
-\r
-// FIXME obselete\r
-ULLONG VDR::rescanRecording(ULONG* totalFrames)\r
-{\r
- VDR_RequestPacket vrp;\r
- if (!vrp.init(VDR_RESCANRECORDING, true, 0)) return 0;\r
-\r
- VDR_ResponsePacket* vresp = RequestResponse(&vrp);\r
- if (vresp->noResponse()) { delete vresp; return 0; }\r
- \r
- ULLONG lengthBytes = vresp->extractULLONG();\r
- ULONG lengthFrames = vresp->extractULONG();\r
- delete vresp;\r
- \r
- logger->log("VDR", Log::DEBUG, "VDR said length is: %llu %lu", lengthBytes, lengthFrames);\r
-\r
- *totalFrames = lengthFrames;\r
- return lengthBytes;\r
-}\r
-\r
-MarkList* VDR::getMarks(char* fileName)\r
-{\r
- VDR_RequestPacket vrp;\r
- if (!vrp.init(VDR_GETMARKS, true, strlen(fileName) + 1)) return NULL;\r
- if (!vrp.addString(fileName)) return NULL;\r
-\r
- VDR_ResponsePacket* vresp = RequestResponse(&vrp);\r
- if (vresp->noResponse()) { delete vresp; return NULL; }\r
- \r
- if (vresp->serverError())\r
- {\r
- delete vresp;\r
- return NULL;\r
- }\r
-\r
- MarkList* markList = new MarkList();\r
-\r
- while (!vresp->end())\r
- {\r
- Mark* mark = new Mark();\r
- mark->pos = vresp->extractULONG();\r
-\r
- markList->push_back(mark);\r
- logger->log("VDR", Log::DEBUG, "Have added a mark to list. %lu", mark->pos);\r
- }\r
-\r
- delete vresp;\r
- \r
- return markList;\r
-}\r
-\r
-void VDR::getChannelPids(Channel* channel)\r
-{\r
- VDR_RequestPacket vrp;\r
- if (!vrp.init(VDR_GETCHANNELPIDS, true, sizeof(ULONG))) return ;\r
- if (!vrp.addULONG(channel->number)) return ;\r
-\r
- VDR_ResponsePacket* vresp = RequestResponse(&vrp);\r
- if (vresp->noResponse()) { delete vresp; return ; }\r
- \r
- // Format of response\r
- // vpid\r
- // number of apids\r
- // {\r
- // apid\r
- // lang string\r
- // }\r
-\r
- channel->vpid = vresp->extractULONG();\r
- channel->vstreamtype = vresp->extractULONG();\r
- channel->numAPids = vresp->extractULONG();\r
-\r
- for (ULONG i = 0; i < channel->numAPids; i++)\r
- {\r
- apid newapid;\r
- newapid.pid = vresp->extractULONG();\r
- char * name=vresp->extractString();\r
- strncpy(newapid.desc,name,9);\r
- delete [] name;\r
- channel->apids.push_back(newapid);\r
- }\r
-\r
- channel->numDPids = vresp->extractULONG();\r
-\r
- for (ULONG i = 0; i < channel->numDPids; i++)\r
- {\r
- apid newdpid;\r
- newdpid.pid = vresp->extractULONG();\r
- char * name=vresp->extractString();\r
- strncpy(newdpid.desc,name,9);\r
- delete [] name;\r
- channel->dpids.push_back(newdpid);\r
- }\r
-\r
- channel->numSPids = vresp->extractULONG();\r
-\r
- for (ULONG i = 0; i < channel->numSPids; i++)\r
- {\r
- apid newspid;\r
- newspid.pid = vresp->extractULONG();\r
- char * name=vresp->extractString();\r
- strncpy(newspid.desc,name,9);\r
- delete [] name;\r
- channel->spids.push_back(newspid);\r
- }\r
- channel->tpid = vresp->extractULONG();\r
- // extension\r
- for (ULONG i = 0; i < channel->numAPids; i++)\r
- {\r
- channel->apids[i].type = vresp->extractULONG();\r
- }\r
- for (ULONG i = 0; i < channel->numDPids; i++)\r
- {\r
- channel->dpids[i].type = vresp->extractULONG();\r
- }\r
- for (ULONG i = 0; i < channel->numSPids; i++)\r
- {\r
- channel->spids[i].type = vresp->extractULONG();\r
- channel->spids[i].data1 = vresp->extractULONG();\r
- channel->spids[i].data2 = vresp->extractULONG();\r
- }\r
-\r
- delete vresp;\r
- \r
- return ;\r
-}\r
-\r
-\r
-MediaList * VDR::getRootList() {\r
- return getMediaList(NULL);\r
-}\r
-/**\r
- * media List Request:\r
- * mediaURI\r
- * Media List response:\r
- * mediaList\r
-*/\r
-MediaList* VDR::getMediaList(const MediaURI * root)\r
-{\r
- logger->log("VDR", Log::DEBUG, "getMediaList %s,d=%s, prov=%d", (root?root->getName():"NULL"), \r
- ((root && root->hasDisplayName())?root->getDisplayName():"NULL"),\r
- (root?root->getProvider():providerId));\r
- MediaURI remoteURI(root);\r
- VDR_GetMediaListRequest request(&remoteURI);\r
- SerializeBuffer *vrp=prepareRequest(&request);\r
- if (!vrp) {\r
- logger->log("VDR", Log::ERR, "getMediaList unable to create command");\r
- return NULL;\r
- }\r
- \r
- SerializeBuffer* vresp = doRequestResponse(vrp,request.command);\r
- if (!vresp) {\r
- Command::getInstance()->connectionLost();\r
- return NULL;\r
- }\r
- \r
- MediaList *rt=new MediaList(NULL);\r
- ULONG rtflags=0;\r
- VDR_GetMediaListResponse resp(&rtflags,rt);\r
- if (decodeResponse(vresp,&resp) != 0) {\r
- return NULL;\r
- }\r
- return rt;\r
-}\r
-\r
-/**\r
- * get image Request:\r
- * uri,x,y, channel\r
- * get media response:\r
- * 4 flags\r
- * 8 len of image\r
-*/\r
-int VDR::openMedium(ULONG channel,const MediaURI *uri, ULLONG * size, ULONG x, ULONG y)\r
-{\r
- MediaURI remoteURI(uri);\r
- VDR_OpenMediumRequest request(&channel,&remoteURI,&x,&y);\r
- *size=0;\r
- SerializeBuffer *vrp=prepareRequest(&request);\r
- if (!vrp) {\r
- logger->log("VDR", Log::ERR, "openMedium unable to create command");\r
- return -1;\r
- }\r
- SerializeBuffer* vresp = doRequestResponse(vrp,request.command);\r
- if (!vresp) {\r
- Command::getInstance()->connectionLost();\r
- return -1;\r
- }\r
- ULONG flags=0;\r
- VDR_OpenMediumResponse response(&flags,size);\r
- if (decodeResponse(vresp,&response) != 0) {\r
- return -1;\r
- }\r
- logger->log("VDR", Log::DEBUG, "openMedia len=%llu", *size);\r
- return 0;\r
-}\r
-\r
-/**\r
- * getMediaBlock - no separate response class - simple data block\r
- * resp\r
- * packet\r
- */\r
-int VDR::getMediaBlock(ULONG channel, ULLONG position, ULONG maxAmount, ULONG* amountReceived, unsigned char **buffer)\r
-{\r
- *amountReceived=0;\r
- VDR_GetMediaBlockRequest request(&channel,&position,&maxAmount);\r
- SerializeBuffer *vrp=prepareRequest(&request);\r
- if (!vrp) {\r
- logger->log("VDR", Log::ERR, "getMediaBlock unable to create command");\r
- return -1;\r
- }\r
- SerializeBuffer* vresp = doRequestResponse(vrp,request.command);\r
- if (!vresp) {\r
- Command::getInstance()->connectionLost();\r
- return -1;\r
- }\r
- \r
- // Special handling for getblock\r
- *amountReceived = (ULONG)(vresp->getEnd()-vresp->getStart());\r
- *buffer = vresp->steelBuffer();\r
- delete vresp;\r
- return 0;\r
-}\r
-\r
-/**\r
- * VDR_GETMEDIAINFO\r
- * channel\r
- * rt\r
- * flags\r
- * info\r
- */\r
-\r
-int VDR::getMediaInfo(ULONG channel, MediaInfo * result) {\r
- if (! result) return -1;\r
- VDR_GetMediaInfoRequest request(&channel);\r
- SerializeBuffer *vrp=prepareRequest(&request);\r
- if (!vrp) {\r
- logger->log("VDR", Log::ERR, "getMediaInfo unable to create command");\r
- return -1;\r
- }\r
- SerializeBuffer* vresp = doRequestResponse(vrp,request.command);\r
- if (!vresp) {\r
- Command::getInstance()->connectionLost();\r
- return -1;\r
- }\r
-\r
- ULONG flags=0;\r
- VDR_GetMediaInfoResponse response(&flags,result);\r
- if (decodeResponse(vresp,&response) != 0) {\r
- return -1;\r
- }\r
- return 0;\r
-}\r
-\r
-/**\r
- * VDR_CLOSECHANNEL\r
- * channel\r
- * rt\r
- * flags\r
- */\r
-\r
-int VDR::closeMediaChannel(ULONG channel) {\r
- VDR_CloseMediaChannelRequest request(&channel);\r
- SerializeBuffer *vrp=prepareRequest(&request);\r
- if (!vrp) {\r
- logger->log("VDR", Log::ERR, "closeMediaChannel unable to create command");\r
- return -1;\r
- }\r
- SerializeBuffer* vresp = doRequestResponse(vrp,request.command);\r
- if (!vresp) {\r
- Command::getInstance()->connectionLost();\r
- return -1;\r
- }\r
- ULONG flags;\r
- VDR_CloseMediaChannelResponse response(&flags);\r
- if (decodeResponse(vresp,&response) != 0) return -1;\r
- return (flags != 0)?-1:0;\r
-}\r
-\r
-\r
-\r
-\r
-int VDR::deleteTimer(RecTimer* delTimer)\r
-{\r
- logger->log("VDR", Log::DEBUG, "Delete timer called");\r
- \r
- VDR_RequestPacket vrp;\r
- if (!vrp.init(VDR_DELETETIMER, false, 0)) return 0;\r
- if (!vrp.addULONG(delTimer->channelNumber)) return 0;\r
- if (!vrp.addULONG(delTimer->weekDays)) return 0; \r
- if (!vrp.addULONG(delTimer->day)) return 0;\r
- if (!vrp.addULONG(delTimer->startTime)) return 0; \r
- if (!vrp.addULONG(delTimer->stopTime)) return 0; \r
- \r
- VDR_ResponsePacket* vresp = RequestResponse(&vrp);\r
- if (vresp->noResponse()) { delete vresp; return 0; }\r
- \r
- int toReturn = (int)vresp->extractULONG();\r
- delete vresp;\r
-\r
- return toReturn;\r
-}\r
-\r
-I18n::lang_code_list VDR::getLanguageList()\r
-{\r
- I18n::lang_code_list CodeList;\r
- CodeList["en"] = "English"; // Default entry\r
- VDR_RequestPacket vrp;\r
- if (!vrp.init(VDR_GETLANGUAGELIST, false, 0)) return CodeList;\r
- VDR_ResponsePacket* vresp = RequestResponse(&vrp);\r
- if (vresp->noResponse() || vresp->end())\r
- {\r
- delete vresp;\r
- return CodeList;\r
- }\r
- CodeList.clear();\r
- while (!vresp->end())\r
- {\r
- char* c_code = vresp->extractString();\r
- char* c_name = vresp->extractString();\r
- string code = c_code;\r
- string name = c_name;\r
- CodeList[code] = name;\r
- delete[] c_code;\r
- delete[] c_name;\r
- }\r
- delete vresp;\r
- return CodeList;\r
-}\r
-\r
-int VDR::getLanguageContent(const std::string code, I18n::trans_table& texts)\r
-{\r
- VDR_RequestPacket vrp;\r
- if (!vrp.init(VDR_GETLANGUAGECONTENT, false, 0)) return 0;\r
- if (!vrp.addString(code.c_str())) return 0;\r
- VDR_ResponsePacket* vresp = RequestResponse(&vrp);\r
- if (vresp->noResponse()) { delete vresp; return 0; }\r
- texts.clear();\r
- while (!vresp->end())\r
- {\r
- char* c_key = vresp->extractString();\r
- char* c_text = vresp->extractString();\r
- string key = c_key;\r
- string text = c_text;\r
- texts[key] = text;\r
- delete[] c_key;\r
- delete[] c_text;\r
- }\r
- delete vresp;\r
- return 1;\r
-}\r
+/*
+ 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"
+
+#define VOMP_PROTOCOLL_VERSION 0x00000100
+
+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<VDRServer>& 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(unsigned int* v_server,unsigned int* v_client)
+{
+ 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);
+
+ unsigned int version=vresp->extractULONG();
+
+ *v_server=version;
+ *v_client=VOMP_PROTOCOLL_VERSION;
+
+ delete vresp;
+
+ if (version!=VOMP_PROTOCOLL_VERSION) {
+
+ return 0;
+
+ }
+
+ // 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();
+ char * name=vresp->extractString();
+ strncpy(newapid.desc,name,9);
+ delete [] name;
+ channel->apids.push_back(newapid);
+ }
+
+ channel->numDPids = vresp->extractULONG();
+
+ for (ULONG i = 0; i < channel->numDPids; i++)
+ {
+ apid newdpid;
+ newdpid.pid = vresp->extractULONG();
+ char * name=vresp->extractString();
+ strncpy(newdpid.desc,name,9);
+ delete [] name;
+ channel->dpids.push_back(newdpid);
+ }
+
+ channel->numSPids = vresp->extractULONG();
+
+ for (ULONG i = 0; i < channel->numSPids; i++)
+ {
+ apid newspid;
+ newspid.pid = vresp->extractULONG();
+ char * name=vresp->extractString();
+ strncpy(newspid.desc,name,9);
+ delete [] name;
+ channel->spids.push_back(newspid);
+ }
+ channel->tpid = vresp->extractULONG();
+ // extension
+ for (ULONG i = 0; i < channel->numAPids; i++)
+ {
+ channel->apids[i].type = vresp->extractULONG();
+ }
+ for (ULONG i = 0; i < channel->numDPids; i++)
+ {
+ channel->dpids[i].type = vresp->extractULONG();
+ }
+ for (ULONG i = 0; i < channel->numSPids; i++)
+ {
+ channel->spids[i].type = vresp->extractULONG();
+ channel->spids[i].data1 = vresp->extractULONG();
+ channel->spids[i].data2 = 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;
+}
-/*\r
- Copyright 2004-2005 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-\r
-// FIXME - This and the protocol are overly complicated now. Sorry.\r
-// I'll clean it up in a couple of releases time...\r
-\r
-\r
-#ifndef VDR_H\r
-#define VDR_H\r
-\r
-#include <stdio.h>\r
-#include <time.h>\r
-#include <vector>\r
-#include <algorithm>\r
-\r
-#include "threadsystem.h"\r
-\r
-#include "defines.h"\r
-#include "rectimer.h"\r
-#include "mark.h"\r
-#include "mediaprovider.h"\r
-#include "eventdispatcher.h"\r
-#include "i18n.h"\r
-#include "log.h"\r
-\r
-class TCP;\r
-class Log;\r
-class RecInfo;\r
-class Event;\r
-class Channel;\r
-class VDR_RequestPacket;\r
-class VDR_ResponsePacket;\r
-class SerializeBuffer;\r
-\r
-using namespace std;\r
-\r
-typedef vector<Event*> EventList;\r
-typedef vector<Channel*> ChannelList;\r
-typedef vector<RecTimer*> RecTimerList;\r
-\r
-struct VDRServer\r
-{\r
- char* ip;\r
- char* name;\r
-};\r
-\r
-struct RecTimerSorter // : public binary_function<double, double, bool>\r
-{\r
- bool operator() (const RecTimer* a, const RecTimer* b)\r
- {\r
- return a->startTime < b->startTime;\r
- }\r
-};\r
-\r
-struct ServerSorter\r
-{\r
- bool operator() (const VDRServer& a, const VDRServer& b)\r
- {\r
- if (strcmp(b.name, a.name) > 0) return true;\r
- return false;\r
- }\r
-};\r
-\r
-class RecMan;\r
-\r
-class StreamReceiver\r
-{\r
- public:\r
- virtual void streamReceive(ULONG, void*, ULONG)=0;\r
-};\r
-\r
-class VDR_PacketReceiver : public EDReceiver // implementation in vdr.cc\r
-{\r
- public:\r
- virtual bool call(void* userTag);\r
-\r
- friend class VDR;\r
- protected:\r
-// ULONG requestTime;\r
- ULONG receiverChannel;\r
- \r
- // If receiverChannel == 1:\r
- ULONG requestSerialNumber; // set by RequestResponse, used in ed_cb_find\r
- VDR_ResponsePacket* save_vresp; // set by ed_cb_call, used in RequestResponse\r
- \r
- // If receiverChannel == 2:\r
- ULONG streamID;\r
- StreamReceiver* streamReceiver;\r
-};\r
-\r
-class VDR : public Thread_TYPE, public EventDispatcher, public MediaProvider, public ExternLogger\r
-{\r
-\r
- public:\r
- const static ULONG VIDEO = 1;\r
- const static ULONG RADIO = 2;\r
- \r
- const static ULONG CHANNEL_REQUEST_RESPONSE = 1;\r
- const static ULONG CHANNEL_STREAM = 2;\r
- const static ULONG CHANNEL_KEEPALIVE = 3;\r
- const static ULONG CHANNEL_NETLOG = 4;\r
- \r
- VDR();\r
- ~VDR();\r
- static VDR* getInstance();\r
-\r
- int init(int port);\r
- int shutdown();\r
-\r
- void findServers(vector<VDRServer>& servers);\r
- void cancelFindingServer();\r
- void setServerIP(char*);\r
- void setReceiveWindow(size_t size);\r
- int connect();\r
- void disconnect();\r
- bool isConnected() { return connected; }\r
- ULONG getChannelNumberWidth() { return channelNumberWidth; }\r
-\r
- // protocol functions\r
- // for the following, if result == false then the connection has died\r
- // doLogin\r
- // getRecordingList\r
- // getChannelsList\r
- // getChannelSchedule\r
- // getRecTimersList\r
- // isConnected can be called after the following to determine if still ok\r
- // deleteRecording\r
- // streamRecording\r
- // positionFromFrameNumber\r
- // streamChannel\r
- // getBlock\r
- // stopStreaming\r
- // configLoad\r
- // configSave\r
- // setEventTimer\r
-\r
- int doLogin(unsigned int* v_server,unsigned int* v_client);\r
- bool getRecordingsList(RecMan* recman);\r
- RecInfo* getRecInfo(char* fileName);\r
- int deleteRecording(char* fileName);\r
- char* moveRecording(char* fileName, char* newPath);\r
- ULLONG streamRecording(char* fileName, ULONG* lengthFrames, bool* IsPesRecording);\r
- ULLONG positionFromFrameNumber(ULONG frameNumber);\r
- ULONG frameNumberFromPosition(ULLONG position);\r
- bool getNextIFrame(ULONG frameNumber, ULONG direction, ULLONG* rfilePosition, ULONG* rframeNumber, ULONG* rframeLength);\r
- // Direction: 0=backwards, 1=forwards\r
- MarkList* getMarks(char* fileName);\r
- int deleteTimer(RecTimer* delTimer);\r
- ChannelList* getChannelsList(ULONG type);\r
- int streamChannel(ULONG number, StreamReceiver*);\r
- int streamChannel(ULONG number);\r
- void getChannelPids(Channel* channel);\r
- UCHAR* getBlock(ULLONG position, UINT maxAmount, UINT* amountReceived);\r
- //get image blocks separate - we can do this in parallel\r
- int stopStreaming();\r
- EventList* getChannelSchedule(ULONG number);\r
- EventList* getChannelSchedule(ULONG number, time_t start, ULONG duration);\r
- int configSave(const char* section, const char* key, const char* value);\r
- char* configLoad(const char* section, const char* key);\r
- ULONG setEventTimer(char* timerString);\r
- RecTimerList* getRecTimersList();\r
- bool LogExtern(const char* buffer);\r
- \r
- bool setCharset(int charset); // 1 latin 2 UTF-8\r
-\r
- /**\r
- * the MediaProvider functions\r
- *\r
- */\r
- virtual MediaList* getRootList();\r
- virtual MediaList* getMediaList(const MediaURI * parent);\r
- virtual int openMedium(ULONG channel,const MediaURI *uri,ULLONG * size, ULONG xsize,ULONG ysize);\r
- virtual int getMediaBlock(ULONG channel, unsigned long long offset, unsigned long len, unsigned long * outlen,\r
- unsigned char ** buffer);\r
- virtual int getMediaInfo(ULONG channel, struct MediaInfo * result);\r
- virtual int closeMediaChannel(ULONG channel);\r
-\r
-\r
- I18n::lang_code_list getLanguageList();\r
- int getLanguageContent(const string code, I18n::trans_table&);\r
-\r
- // end protocol functions\r
-\r
-\r
- // obselete\r
- ULLONG rescanRecording(ULONG* lengthFrames); // FIXME obselete\r
-\r
-\r
-\r
- private:\r
- static VDR* instance;\r
-\r
- VDR_ResponsePacket* RequestResponse(VDR_RequestPacket* request);\r
- UCHAR* getBlock(ULLONG position, UINT maxAmount, UINT* amountReceived, ULONG cmd);\r
- \r
- void connectionDied();\r
- bool sendKA(ULONG timeStamp);\r
- \r
- Log* logger;\r
- int initted;\r
- int findingServer;\r
- TCP* tcp;\r
- int port;\r
- char serverIP[16];\r
- bool connected;\r
- ULONG maxChannelNumber;\r
- ULONG channelNumberWidth;\r
- VDR_PacketReceiver* TEMP_SINGLE_VDR_PR;\r
-\r
-\r
- ULONG providerId;\r
- ULONG subRange;\r
- SerializeBuffer * doRequestResponse(SerializeBuffer *in,int cmd);\r
- protected:\r
- \r
- // Thread\r
- void threadMethod();\r
- void threadPostStopCleanup() {};\r
-\r
- // EventDispatcher\r
- virtual bool ed_cb_find(EDReceiver* edr, void* userTag);\r
-};\r
-\r
-#endif\r
-\r
-/*\r
-\r
-index.vdr file format for video:\r
-\r
-For every video frame:\r
-{\r
- File offset 4 bytes\r
- Picture type 1 byte\r
- File number 1 byte\r
- Zero 2 bytes\r
-}\r
-\r
-Picture types:\r
-\r
-#define NO_PICTURE 0\r
-#define I_FRAME 1\r
-#define P_FRAME 2\r
-#define B_FRAME 3\r
-\r
-\r
-\r
-Packet formats\r
-\r
-Packet format for an RR channel request:\r
-\r
-4 bytes = channel ID = 1 (request/response channel)\r
-4 bytes = request ID (from serialNumber)\r
-4 bytes = opcode\r
-4 bytes = length of the rest of the packet\r
-? bytes = rest of packet. depends on packet\r
-\r
-\r
-Packet format for an RR channel response:\r
-\r
-4 bytes = channel ID = 1 (request/response channel)\r
-4 bytes = request ID (from serialNumber)\r
-4 bytes = length of the rest of the packet\r
-? bytes = rest of packet. depends on packet\r
-\r
-\r
-Packet format for a stream packet:\r
-\r
-4 bytes = channel ID = 2 (stream channel)\r
-4 bytes = stream ID (from requestID)\r
-4 bytes = length of the stream data (rest of packet)\r
-? bytes = stream data\r
-\r
-*/\r
-\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.
+*/
+
+
+// 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 <stdio.h>
+#include <time.h>
+#include <vector>
+#include <algorithm>
+
+#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<Event*> EventList;
+typedef vector<Channel*> ChannelList;
+typedef vector<RecTimer*> RecTimerList;
+
+struct VDRServer
+{
+ char* ip;
+ char* name;
+};
+
+struct RecTimerSorter // : public binary_function<double, double, bool>
+{
+ 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<VDRServer>& 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(unsigned int* v_server,unsigned int* v_client);
+ 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
+
+*/
+
-/*\r
- Copyright 2004-2005 Chris Tallon, Andreas Vogel\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef VDRCOMMAND_H\r
-#define VDRCOMMAND_H\r
-\r
-#include "defines.h"\r
-#include "serialize.h"\r
-#include "media.h"\r
-\r
-/**\r
- * data holder for VDR commands\r
- * it's only important to add serializable objects\r
- * in the same order on both sides\r
- */\r
-\r
-//until we really have response - commands we simply take\r
-//the request+this flag for responses\r
-//not really necessary but for checks it's better to have a command ID at least in some responses\r
-const static ULONG VDR_RESPONSE_FLAG =0x1000000;\r
-\r
-//as this header is only included by vdr.cc the constants are this way private\r
-//but can easily be used on the server side as well\r
-\r
-const static ULONG VDR_LOGIN = 1;\r
-const static ULONG VDR_GETRECORDINGLIST = 2;\r
-const static ULONG VDR_DELETERECORDING = 3;\r
-const static ULONG VDR_GETCHANNELLIST = 5;\r
-const static ULONG VDR_STREAMCHANNEL = 6;\r
-const static ULONG VDR_GETBLOCK = 7;\r
-const static ULONG VDR_STOPSTREAMING = 8;\r
-const static ULONG VDR_STREAMRECORDING = 9;\r
-const static ULONG VDR_GETCHANNELSCHEDULE = 10;\r
-const static ULONG VDR_CONFIGSAVE = 11;\r
-const static ULONG VDR_CONFIGLOAD = 12;\r
-const static ULONG VDR_RESCANRECORDING = 13; // FIXME obselete\r
-const static ULONG VDR_GETTIMERS = 14;\r
-const static ULONG VDR_SETTIMER = 15;\r
-const static ULONG VDR_POSFROMFRAME = 16;\r
-const static ULONG VDR_FRAMEFROMPOS = 17;\r
-const static ULONG VDR_MOVERECORDING = 18;\r
-const static ULONG VDR_GETNEXTIFRAME = 19;\r
-const static ULONG VDR_GETRECINFO = 20;\r
-const static ULONG VDR_GETMARKS = 21;\r
-const static ULONG VDR_GETCHANNELPIDS = 22;\r
-const static ULONG VDR_DELETETIMER = 23;\r
-const static ULONG VDR_GETLANGUAGELIST = 33;\r
-const static ULONG VDR_GETLANGUAGECONTENT = 34;\r
-const static ULONG VDR_SETCHARSET = 37;\r
-const static ULONG VDR_GETMEDIALIST = 30;\r
-const static ULONG VDR_OPENMEDIA = 31;\r
-const static ULONG VDR_GETMEDIABLOCK = 32;\r
-const static ULONG VDR_GETMEDIAINFO = 35;\r
-const static ULONG VDR_CLOSECHANNEL = 36;\r
-\r
-class VDR_Command : public SerializableList {\r
- public:\r
- VDR_Command(const ULONG cmd) {\r
- command=cmd;\r
- addParam(&command);\r
- }\r
- virtual ~VDR_Command(){}\r
- ULONG command;\r
-};\r
-\r
-class VDR_GetMediaListRequest : public VDR_Command {\r
- public:\r
- VDR_GetMediaListRequest(MediaURI *root) :VDR_Command(VDR_GETMEDIALIST) {\r
- addParam(root);\r
- }\r
-};\r
-\r
-class VDR_GetMediaListResponse : public VDR_Command {\r
- public:\r
- VDR_GetMediaListResponse(ULONG *flags,MediaList *m) : VDR_Command(VDR_GETMEDIALIST|VDR_RESPONSE_FLAG){\r
- addParam(flags);\r
- addParam(m);\r
- }\r
-};\r
-\r
-class VDR_OpenMediumRequest : public VDR_Command {\r
- public:\r
- VDR_OpenMediumRequest(ULONG *channel,MediaURI *u,ULONG *xsize, ULONG *ysize) :\r
- VDR_Command(VDR_OPENMEDIA) {\r
- addParam(channel);\r
- addParam(u);\r
- addParam(xsize);\r
- addParam(ysize);\r
- }\r
-};\r
-class VDR_OpenMediumResponse : public VDR_Command {\r
- public:\r
- VDR_OpenMediumResponse(ULONG *flags,ULLONG *size) :\r
- VDR_Command(VDR_OPENMEDIA|VDR_RESPONSE_FLAG) {\r
- addParam(flags);\r
- addParam(size);\r
- }\r
-};\r
-class VDR_GetMediaBlockRequest : public VDR_Command {\r
- public:\r
- VDR_GetMediaBlockRequest(ULONG * channel, ULLONG *pos, ULONG *max):\r
- VDR_Command(VDR_GETMEDIABLOCK) {\r
- addParam(channel);\r
- addParam(pos);\r
- addParam(max);\r
- }\r
-};\r
-\r
-//no response class for GetMediaBlock\r
-\r
-\r
-class VDR_CloseMediaChannelRequest : public VDR_Command {\r
- public:\r
- VDR_CloseMediaChannelRequest(ULONG * channel):\r
- VDR_Command(VDR_CLOSECHANNEL) {\r
- addParam(channel);\r
- }\r
-};\r
-\r
-class VDR_CloseMediaChannelResponse : public VDR_Command {\r
- public:\r
- VDR_CloseMediaChannelResponse(ULONG * flags):\r
- VDR_Command(VDR_CLOSECHANNEL|VDR_RESPONSE_FLAG) {\r
- addParam(flags);\r
- }\r
-};\r
-\r
-class VDR_GetMediaInfoRequest : public VDR_Command {\r
- public:\r
- VDR_GetMediaInfoRequest(ULONG * channel):\r
- VDR_Command(VDR_GETMEDIAINFO) {\r
- addParam(channel);\r
- }\r
-};\r
-class VDR_GetMediaInfoResponse : public VDR_Command {\r
- public:\r
- VDR_GetMediaInfoResponse(ULONG * flags,MediaInfo *info):\r
- VDR_Command(VDR_GETMEDIAINFO|VDR_RESPONSE_FLAG) {\r
- addParam(flags);\r
- addParam(info);\r
- }\r
-};\r
-\r
-\r
-\r
-\r
-#endif\r
+/*
+ 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
-/*\r
- Copyright 2007 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#include "vdrresponsepacket.h"\r
-\r
-#include "vdr.h"\r
-#include "tcp.h"\r
-\r
-VDR_ResponsePacket::VDR_ResponsePacket()\r
-{\r
- userDataLength = 0;\r
- packetPos = 0;\r
- userData = NULL;\r
- ownBlock = true;\r
- \r
- channelID = 0;\r
- \r
- requestID = 0;\r
- streamID = 0;\r
- \r
- flag = 0;\r
-}\r
-\r
-VDR_ResponsePacket::~VDR_ResponsePacket()\r
-{\r
- if (!ownBlock) return; // don't free if it's a getblock\r
- \r
- if (userData) free(userData);\r
-}\r
-\r
-void VDR_ResponsePacket::setResponse(ULONG trequestID, UCHAR* tuserData, ULONG tuserDataLength)\r
-{\r
- channelID = VDR::CHANNEL_REQUEST_RESPONSE;\r
- requestID = trequestID;\r
- userData = tuserData;\r
- userDataLength = tuserDataLength;\r
-}\r
-\r
-void VDR_ResponsePacket::setStream(ULONG tstreamID, ULONG tflag, UCHAR* tuserData, ULONG tuserDataLength)\r
-{\r
- channelID = VDR::CHANNEL_STREAM;\r
- streamID = tstreamID;\r
- flag = tflag;\r
- userData = tuserData;\r
- userDataLength = tuserDataLength;\r
-}\r
-\r
-bool VDR_ResponsePacket::end()\r
-{\r
- return (packetPos >= userDataLength);\r
-}\r
-\r
-void VDR_ResponsePacket::dumpUD()\r
-{\r
- TCP::dump(userData, userDataLength);\r
-}\r
-\r
-int VDR_ResponsePacket::serverError()\r
-{\r
- if ((packetPos == 0) && (userDataLength == 4) && !ntohl(*(ULONG*)userData)) return 1;\r
- else return 0;\r
-}\r
-\r
-char* VDR_ResponsePacket::extractString()\r
-{\r
- if (serverError()) return NULL;\r
-\r
- int length = strlen((char*)&userData[packetPos]);\r
- if ((packetPos + length) > userDataLength) return NULL;\r
- char* str = new char[length + 1];\r
- strcpy(str, (char*)&userData[packetPos]);\r
- packetPos += length + 1;\r
- return str;\r
-}\r
-\r
-UCHAR VDR_ResponsePacket::extractUCHAR()\r
-{\r
- if ((packetPos + sizeof(UCHAR)) > userDataLength) return 0;\r
- UCHAR uc = userData[packetPos];\r
- packetPos += sizeof(UCHAR);\r
- return uc;\r
-}\r
-\r
-ULONG VDR_ResponsePacket::extractULONG()\r
-{\r
- if ((packetPos + sizeof(ULONG)) > userDataLength) return 0;\r
- ULONG ul = userData[packetPos++]<<24;\r
- ul|= userData[packetPos++]<<16;\r
- ul|= userData[packetPos++]<<8;\r
- ul|= userData[packetPos++];\r
- //packetPos += sizeof(ULONG);\r
- return ul;\r
-}\r
-\r
-ULLONG VDR_ResponsePacket::extractULLONG()\r
-{\r
- if ((packetPos + sizeof(ULLONG)) > userDataLength) return 0;\r
- ULLONG ull= ((ULLONG)userData[packetPos++])<<56;\r
- ull|= ((ULLONG)userData[packetPos++])<<48;\r
- ull|= ((ULLONG)userData[packetPos++])<<40;\r
- ull|= ((ULLONG)userData[packetPos++])<<32;\r
- ull|= ((ULLONG)userData[packetPos++])<<24;\r
- ull|= ((ULLONG)userData[packetPos++])<<16;\r
- ull|= ((ULLONG)userData[packetPos++])<<8;\r
- ull|= ((ULLONG)userData[packetPos++]);\r
- return ull;\r
-}\r
-\r
-double VDR_ResponsePacket::extractdouble()\r
-{\r
- if ((packetPos + sizeof(ULLONG)) > userDataLength) return 0;\r
- ULLONG ull = extractULLONG();\r
- double d;\r
- memcpy(&d,&ull,sizeof(double));\r
- return d;\r
-}\r
-\r
-long VDR_ResponsePacket::extractLONG()\r
-{\r
- if ((packetPos + sizeof(long)) > userDataLength) return 0;\r
- long l = userData[packetPos++]<<24;\r
- l|= userData[packetPos++]<<16;\r
- l|= userData[packetPos++]<<8;\r
- l|= userData[packetPos++];\r
- return l;\r
-}\r
-\r
-UCHAR* VDR_ResponsePacket::getUserData()\r
-{\r
- ownBlock = false;\r
- return userData;\r
-}\r
-\r
+/*
+ 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;
+}
+
-/*\r
- Copyright 2005 Brian Walton\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-/*\r
- vepg presents a 2 dimensional electronic programme guide with channels down\r
- the y axis and time along the x axis.\r
- Programmes are layed on the x axis as alterate coloured blocks with as much\r
- of the programme title as will fit inside the block shown as text.\r
- Up and down commands step through the channels whilst left and right commands\r
- move through the programmes of the currently selected channel.\r
- When a programme is selected, it highlights in the grid and full programe details\r
- (start time, title and description) are displayed in an area at te top left of the screen.\r
- Any currently programmed timers will display in the grid and in the orogramme detail window as red\r
- It is possible to select a programme to be recorded by pressing the record button.\r
- The video stream currently being viewed is shown as quarter screen in the top right.\r
-*/\r
-\r
-#include "vepg.h"\r
-\r
-#include "remote.h"\r
-#include "vchannellist.h"\r
-#include "command.h"\r
-#include "video.h"\r
-#include "vepgsettimer.h"\r
-#include "timers.h"\r
-#include "wsymbol.h"\r
-#include "message.h"\r
-#include "colour.h"\r
-#include "boxstack.h"\r
-#include "channel.h"\r
-#include "i18n.h"\r
-#include "log.h"\r
-\r
-VEpg* VEpg::instance = NULL;\r
-\r
-VEpg::VEpg(void* tparent, UINT tcurrentChannelIndex, ULONG streamType)\r
-{\r
- instance = this;\r
- currentChannelIndex = tcurrentChannelIndex;\r
-\r
- // PAL / NTSC sizes -----------------------\r
-\r
- int xsize, ysize;\r
- int xpos, ypos;\r
- int summaryLines, summaryLowerPadding;\r
- int chanNameYpos;\r
- //UINT gridRows; // is a class member\r
-\r
- if (Video::getInstance()->getFormat() == Video::PAL)\r
- {\r
- xsize = 632;\r
- ysize = 541;\r
- xpos = 60;\r
- ypos = 16;\r
- summaryLines = 8;\r
- summaryLowerPadding = 18;\r
- chanNameYpos = 244;\r
- gridRows = 7;\r
- }\r
- else\r
- {\r
- xsize = 632;\r
- ysize = 452;\r
- xpos = 50;\r
- ypos = 10;\r
- summaryLines = 6;\r
- summaryLowerPadding = 28;\r
- chanNameYpos = 206;\r
- gridRows = 5;\r
- }\r
-\r
- // initialise variables and pointers\r
- boxstack = BoxStack::getInstance();\r
- parent = tparent;\r
- eventList = NULL;\r
- chanList = VDR::getInstance()->getChannelsList(streamType); //TODO want to be able to display video and radio together\r
- e = 0;\r
-\r
- for(UINT listIndex = 0; listIndex < gridRows; listIndex++)\r
- {\r
- // initialise array of pointers to eventlist structures\r
- eventLista[listIndex] = NULL;\r
- }\r
-\r
- // Create pallet on which to paint our epg view and position it in centre of screen.\r
- // Need to reduce size to deal with overscanning TVs.\r
-\r
- setSize(xsize, ysize);\r
- createBuffer();\r
- setPosition(xpos, ypos);\r
-\r
- // beautify\r
-// DrawStyle transparent = DrawStyle(0, 0, 0, 0);\r
-// setBackgroundColour(transparent);\r
-\r
-// progTitle.setSurface(surface);\r
- progTitle.setPosition(0,0);\r
- progTitle.setSize(300,(getFontHeight() + 4) * 2 + 16); //paragraph line seperation is 4 pixels\r
- progTitle.setBackgroundColour(DrawStyle::TITLEBARBACKGROUND);\r
- progTitle.setTextPos(5, 16);\r
- progTitle.setGap(4);\r
- add(&progTitle);\r
-\r
-// progInfo.setSurface(surface);\r
- progInfo.setBackgroundColour(DrawStyle::VIEWBACKGROUND);\r
- progInfo.setPosition(0, progTitle.getY2());\r
- progInfo.setSize(300,((getFontHeight() + 4) * summaryLines) + summaryLowerPadding);\r
- progInfo.setGap(4);\r
- add(&progInfo);\r
-\r
-// chanName.setSurface(surface);\r
- chanName.setSize(510, (getFontHeight() + 4));\r
- chanName.setPosition(305, chanNameYpos);\r
- DrawStyle t1(0, 0, 0, 90);\r
- chanName.setBackgroundColour(t1);\r
- add(&chanName);\r
-\r
- // create area to display list of channels\r
-// chanListbox.setSurface(surface); // add channel list\r
- chanListbox.setPosition(0, progInfo.getY2() + getFontHeight() + 8); // position channel list\r
- chanListbox.setSize(150, ((getFontHeight() + 2) * gridRows) + 5); //listbox line seperation is 2 pixels\r
- chanListbox.setGap(2);\r
- add(&chanListbox);\r
-\r
- // populate channel list\r
- if (chanList)\r
- {\r
- Channel* chan;\r
- int first = 1;\r
- for (UINT i = 0; i < chanList->size(); i++)\r
- {\r
- chan = (*chanList)[i];\r
- if (i == currentChannelIndex)\r
- first = 1;\r
- chan->index = chanListbox.addOption(chan->name, 0, first);\r
- first = 0;\r
- }\r
- chanName.setText((*chanList)[chanListbox.getCurrentOption()]->name);\r
- }\r
-\r
- listTop = chanListbox.getTopOption();\r
- chanListbox.draw(); // doing this to allow chanListbox.getBottomOption() in updateEventList() to work\r
- time(<ime); // set ltime to now\r
- ltime = prevHour(<ime); // set ltime to previous hour TODO make this half hour?\r
- time(&selTime); // set selTime to now\r
- updateEventList(); // get list of programmes\r
-}\r
-\r
-void VEpg::preDelete()\r
-{\r
- Timers::getInstance()->cancelTimer(this, 1);\r
-}\r
-\r
-VEpg::~VEpg()\r
-{\r
-\r
- instance = NULL;\r
-\r
- for(UINT listIndex = 0; listIndex < gridRows; listIndex++)\r
- {\r
- if (eventLista[listIndex])\r
- {\r
- (eventLista)[listIndex]->clear();\r
- delete eventLista[listIndex];\r
- }\r
- }\r
- // delete [] eventLista; // FIXME\r
-\r
- // destroy dynamically allocated memory\r
-}\r
-\r
-VEpg* VEpg::getInstance()\r
-{\r
- return instance;\r
-}\r
-\r
-void VEpg::setInfo(Event* event)\r
-{\r
- time_t t;\r
- struct tm* btime; // to hold programme start and end time\r
- char timeString[9]; // to hold programme start and end time\r
- int length = strlen(event->title); // calculate length of programme title string\r
- char* title = new char[length + 15]; // create string to hold start time, end time and programme title\r
- btime = localtime((time_t*)&event->time); //get programme start time\r
-#ifndef _MSC_VER\r
- strftime(timeString, 9, "%0H:%0M - ", btime); // and format it as hh:mm -\r
-#else\r
- strftime(timeString, 9, "%H:%M - ", btime); // and format it as hh:mm -\r
-#endif\r
- strcpy(title, timeString); // put it in our buffer\r
- t = event->time + event->duration; //get programme end time\r
- btime = localtime(&t);\r
-#ifndef _MSC_VER\r
- strftime(timeString, 7, "%0H:%0M ", btime); // and format it as hh:mm -\r
-#else\r
- strftime(timeString, 7, "%H:%M ", btime); // and format it as hh:mm -\r
-#endif\r
- strcat(title, timeString); // put it in our buffer\r
- strcat(title, event->title); // then add the programme title\r
- progTitle.setText(title); // sput this sring in our text box\r
- length = strlen(event->description);\r
- char* info = new char[length + 1]; // create programme detail string\r
- strcpy(info, event->description);\r
- progInfo.setText(info); // show programme detail string\r
-// destroy dynamically allocated memory\r
- delete[] info;\r
- delete[] title;\r
-}\r
-\r
-void VEpg::draw()\r
-{\r
-// View::draw(); // draw pallet\r
- // beautify\r
- DrawStyle transparent = DrawStyle(0, 0, 0, 0);\r
- fillColour(transparent);\r
- \r
- \r
- // Moved all the dynamic data drawing to a seperate function\r
-\r
- // Display the status and key stuff at the bottom\r
-\r
- UINT keyx = chanListbox.getRootBoxOffsetX();\r
- UINT keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;\r
- DrawStyle ref1 = DrawStyle(100, 100, 100, 255); //to do global definition for skin\r
- rectangle(keyx, keyy, 605, getFontHeight() * 2 + 14, ref1);\r
-\r
- WSymbol w;\r
- TEMPADD(&w);\r
- \r
- w.nextSymbol = WSymbol::LEFTARROW;\r
- w.setPosition(keyx + 1, keyy + 20);\r
- w.draw();\r
-\r
- w.nextSymbol = WSymbol::UP;\r
- w.setPosition(keyx + 26, keyy + 3);\r
- w.draw();\r
-\r
- w.nextSymbol = WSymbol::DOWN;\r
- w.setPosition(keyx + 26, keyy + 36);\r
- w.draw();\r
-\r
- w.nextSymbol = WSymbol::RIGHTARROW;\r
- w.setPosition(keyx + 50, keyy + 20);\r
- w.draw();\r
-\r
- drawText(tr("OK"), keyx + 18, keyy + 20, DrawStyle::LIGHTTEXT);\r
-\r
- DrawStyle ref2 = DrawStyle(200, 0, 0, 255);\r
- rectangle(keyx + 72, keyy + 4, 104, getFontHeight() + 2, ref2);\r
- drawText(tr("Page up"), keyx + 74, keyy + 5, DrawStyle::LIGHTTEXT);\r
-\r
- DrawStyle ref3 = DrawStyle(0, 200, 0, 255);\r
- rectangle(keyx + 72, keyy + getFontHeight() + 8, 104, getFontHeight() + 2, ref3);\r
- drawText(tr("Page down"), keyx + 74, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);\r
-\r
- DrawStyle ref4 = DrawStyle(200, 200, 0, 255);\r
- rectangle(keyx + 180, keyy + 4, 104, getFontHeight() + 2, ref4);\r
- drawText(tr("-24 hours"), keyx + 182, keyy + 5, DrawStyle::LIGHTTEXT);\r
-\r
- DrawStyle ref5 = DrawStyle(0, 0, 200, 255);\r
- rectangle(keyx + 180, keyy + getFontHeight() + 8, 104, getFontHeight() + 2, ref5);\r
- drawText(tr("+24 hours"), keyx + 182, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);\r
-\r
- DrawStyle ref6 = DrawStyle(180, 180, 180, 255);\r
- rectangle(keyx + 290, keyy + 4, 180, getFontHeight() + 2, ref6);\r
- drawText(tr("Guide / Back: Close"), keyx + 292 , keyy + 5, DrawStyle::LIGHTTEXT);\r
-\r
- DrawStyle ref7 = DrawStyle(180, 180, 180, 255);\r
- rectangle(keyx + 290, keyy + getFontHeight() + 8, 180, getFontHeight() + 2, ref7);\r
- DrawStyle red = DrawStyle(130, 0, 0);\r
- drawText(tr("Rec: Set timer"), keyx + 292, keyy + getFontHeight() + 9, red);\r
-\r
- DrawStyle ref8 = DrawStyle(180, 180, 180, 255);\r
- rectangle(keyx + 474, keyy + 4, 128, getFontHeight() + 2, ref8);\r
- w.nextSymbol = WSymbol::PLAY;\r
- w.setPosition(keyx + 476, keyy + 5);\r
- w.draw();\r
- drawText(tr("Sel channel"), keyx + 496, keyy + 5, DrawStyle::LIGHTTEXT);\r
-\r
- DrawStyle ref9 = DrawStyle(180, 180, 180, 255);\r
- rectangle(keyx + 474, keyy + getFontHeight() + 8, 128, getFontHeight() + 2, ref9);\r
- drawText(tr("Go: Preview"), keyx + 476, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);\r
-\r
-\r
- // Draw all the dynamic data\r
- drawData();\r
-}\r
-\r
-void VEpg::drawData()\r
-{\r
- // Not doing View::draw() every time causes\r
- // things not to be cleared off the surface properly\r
- // So, blank out the data area first\r
-\r
- rectangle(\r
- chanListbox.getRootBoxOffsetX(),\r
- chanListbox.getRootBoxOffsetY() - getFontHeight() - 3,\r
- 155 + WINDOW_WIDTH * MINUTE_SCALE,\r
- chanListbox.getHeight() + getFontHeight() + 4,\r
- DrawStyle::BLACK);\r
-\r
- chanListbox.draw();\r
- drawgrid();\r
- chanName.draw(); // TODO this should be dealt with by vvideolive\r
-\r
- progTitle.draw();\r
- progInfo.draw();\r
-\r
- // Set timer to redraw to move the current time bar\r
- time_t t, dt;\r
- time(&t);\r
- dt = 60 - (t % 60);\r
- if (dt == 0) dt = 60;\r
- dt += t;\r
- Timers::getInstance()->setTimerT(this, 1, dt);\r
-}\r
-\r
-void VEpg::timercall(int clientReference)\r
-{\r
- drawData();\r
- boxstack->update(this);\r
-}\r
-\r
-int VEpg::handleCommand(int command)\r
-{\r
- switch(command)\r
- {\r
- case Remote::DF_UP:\r
- case Remote::UP:\r
- { // cursor up the channel list\r
- chanListbox.up();\r
- drawData();\r
- boxstack->update(this);\r
- return 2;\r
- }\r
- case Remote::DF_DOWN:\r
- case Remote::DOWN:\r
- { // cursor down the channel list\r
- Log::getInstance()->log("VEPG", Log::DEBUG, "Down start");\r
- \r
- chanListbox.down();\r
- drawData();\r
- boxstack->update(this);\r
- Log::getInstance()->log("VEPG", Log::DEBUG, "Down end");\r
-\r
- return 2;\r
- }\r
- case Remote::DF_LEFT:\r
- case Remote::LEFT:\r
- { // cursor left through time\r
- selTime = thisEvent.time - 1;\r
- drawData();\r
- boxstack->update(this);\r
- return 2;\r
- }\r
- case Remote::DF_RIGHT:\r
- case Remote::RIGHT:\r
- {\r
- // cursor right through time\r
- selTime = thisEvent.time + thisEvent.duration;\r
- drawData();\r
- boxstack->update(this);\r
- return 2;\r
- }\r
- case Remote::RED:\r
- {\r
- // cursor up one page\r
- chanListbox.pageUp();\r
- drawData();\r
- boxstack->update(this);\r
- return 2;\r
- }\r
- case Remote::GREEN:\r
- {\r
- // cursor down one page\r
- chanListbox.pageDown();\r
- drawData();\r
- boxstack->update(this);\r
- return 2;\r
- }\r
- case Remote::BLUE:\r
- {\r
- // step forward 24 hours\r
- selTime += 24 * 60 * 60;\r
- drawData();\r
- boxstack->update(this);\r
- return 2;\r
- }\r
- case Remote::YELLOW:\r
- {\r
- // step forward 24 hours\r
- selTime -= 24 * 60 * 60;\r
- drawData();\r
- boxstack->update(this);\r
- return 2;\r
- }\r
- case Remote::RECORD:\r
- {\r
- if (!chanList) return 2;\r
- Log::getInstance()->log("VEPG", Log::DEBUG, "ID %lu TIME %lu DURATION %lu TITLE %s", thisEvent.id, thisEvent.time, thisEvent.duration, thisEvent.title);\r
- VEpgSetTimer* vs = new VEpgSetTimer(&thisEvent, (*chanList)[chanListbox.getCurrentOption()]);\r
- vs->draw();\r
- boxstack->add(vs);\r
- boxstack->update(vs);\r
- return 2;\r
- }\r
- case Remote::PLAY:\r
- case Remote::GO:\r
- case Remote::OK:\r
- {\r
- if (!chanList) return 2;\r
-\r
- // select programme and display menu TODO currently just changes to selected channel\r
-\r
- currentChannelIndex = chanListbox.getCurrentOption();\r
-\r
- if (parent)\r
- {\r
- Message* m = new Message(); // Must be done after this view deleted\r
- m->from = this;\r
- m->to = parent;\r
- m->message = Message::CHANNEL_CHANGE;\r
- m->parameter = (*chanList)[currentChannelIndex]->number;\r
- Command::getInstance()->postMessageNoLock(m);\r
- }\r
- \r
- setCurrentChannel();\r
-\r
- if(command == Remote::GO)\r
- return 2;\r
- // GO just changes channel in preview, PLAY changes channel and returns to normal TV\r
- }\r
- case Remote::BACK:\r
- case Remote::GUIDE:\r
- {\r
- // return to normal TV mode\r
- if (parent) // ptr check done in case being tested from videorec\r
- {\r
- Message* m = new Message(); // Must be done after this view deleted\r
- m->from = this;\r
- m->to = parent;\r
- m->message = Message::EPG_CLOSE;\r
- Command::getInstance()->postMessageNoLock(m);\r
- }\r
- return 4;\r
- }\r
- case Remote::CHANNELUP:\r
- {\r
- if (currentChannelIndex == (chanList->size() - 1)) // at the end\r
- currentChannelIndex = 0;\r
- else\r
- ++currentChannelIndex;\r
- \r
- if (parent)\r
- {\r
- Message* m = new Message(); // Must be done after this view deleted\r
- m->from = this;\r
- m->to = parent;\r
- m->message = Message::CHANNEL_CHANGE;\r
- m->parameter = (*chanList)[currentChannelIndex]->number;\r
- Command::getInstance()->postMessageNoLock(m);\r
- }\r
- \r
- setCurrentChannel();\r
-\r
- return 2;\r
- }\r
- case Remote::CHANNELDOWN:\r
- {\r
- if (currentChannelIndex == 0) // at the start\r
- currentChannelIndex = chanList->size() - 1; // so go to end\r
- else\r
- --currentChannelIndex;\r
-\r
- if (parent)\r
- {\r
- Message* m = new Message(); // Must be done after this view deleted\r
- m->from = this;\r
- m->to = parent;\r
- m->message = Message::CHANNEL_CHANGE;\r
- m->parameter = (*chanList)[currentChannelIndex]->number;\r
- Command::getInstance()->postMessageNoLock(m);\r
- }\r
- \r
- setCurrentChannel();\r
-\r
- return 2;\r
- }\r
- }\r
- // stop command getting to any more views\r
- return 1;\r
-}\r
-\r
-void VEpg::drawgrid() // redraws grid and select programme\r
-{\r
- // draw the grid of programmes\r
- char timeString[20];\r
- time_t t;\r
- time(&t); // set t = now\r
- if(selTime < t)\r
- selTime = t; // don't allow cursor in the past\r
- if(listTop != chanListbox.getTopOption())\r
- {\r
- // chanListbox has scrolled TODO speed up by changing only rows that have changed\r
- listTop = chanListbox.getTopOption();\r
- updateEventList();\r
- }\r
- if ((selTime >= ltime + WINDOW_WIDTH * 60) || (selTime <= ltime))\r
- {\r
- // we have cursored back before left time of window\r
- //TODO check that this and above don't happen together\r
- ltime = prevHour(&selTime);\r
- updateEventList();\r
- }\r
- // draw time scale\r
- DrawStyle white = DrawStyle(255, 255, 255, 255);\r
- \r
- t = ltime;\r
- struct tm* tms;\r
- tms = localtime(&t);\r
- strftime(timeString, 19, "%a %d %b", tms);\r
- int timey = chanListbox.getRootBoxOffsetY() - getFontHeight() - 3;\r
- int timex = 135;\r
- drawTextRJ(timeString, timex - 10, timey, DrawStyle::LIGHTTEXT); // print date\r
- strftime(timeString, 19, "%H:%M", tms);\r
- drawText(timeString, timex, timey, DrawStyle::LIGHTTEXT); // print left time\r
-\r
- rectangle(155, timey + getFontHeight(), 2, 7, white);\r
- t = t + 3600;\r
- tms = localtime(&t);\r
- strftime(timeString, 19, "%H:%M", tms);\r
- drawText(timeString, timex + 180, timey, DrawStyle::LIGHTTEXT); // print middle time\r
- rectangle(335, timey + getFontHeight(), 2, 7, white);\r
- t = t + 3600;\r
- tms = localtime(&t);\r
- strftime(timeString, 19, "%H:%M", tms);\r
- drawText(timeString, timex + 360, timey, DrawStyle::LIGHTTEXT); // print right time\r
- rectangle(515, timey + getFontHeight(), 2, 7, white);\r
- // pointer to selTime\r
- //rectangle(155 + (selTime - ltime) / 20, timey + getFontHeight(), 2, 7, DrawStyle(255, 50, 50, 255));\r
-\r
- // current time line\r
- time(&t);\r
- if ((t >= ltime) && (t < (ltime + 9000)))\r
- {\r
- rectangle(155 + (t - ltime) / 20, timey + getFontHeight(), 2, ((getFontHeight() + 2) * 7) + 7 + 2, DrawStyle::RED);\r
- }\r
-\r
- // TODO should the above two comditional statements be combined to avoid calling updateEventList() twice?\r
- Event* event;\r
- Event noevent; // an event to use if there are gaps in the epg\r
- thisEvent.setdescription(tr("There are no programme details available for this period"));\r
- thisEvent.duration = WINDOW_WIDTH * 60;\r
- thisEvent.time = ltime;\r
- thisEvent.settitle(tr("No programme details"));\r
- thisEvent.id = 0;\r
- bool swapColour = false; // alternate cell colour\r
- bool currentRow = false;\r
- int y = chanListbox.getRootBoxOffsetY() + 5; // vertical position of cell\r
- DrawStyle bg, fg; // background colour of cells in grid\r
- // for each displayed channel, find programmes that fall in 2.5 hour time window\r
- for(UINT listIndex = 0; listIndex < gridRows; listIndex++)\r
- {\r
- if (listTop + (int)listIndex >= chanListbox.getBottomOption())\r
- continue; // ensure nothing populates grid below last channel\r
- currentRow = (listTop + (int)listIndex == chanListbox.getCurrentOption());\r
- noevent.time = ltime;\r
- noevent.duration = WINDOW_WIDTH * 60;\r
- noevent.settitle("");\r
- paintCell(&noevent, y, DrawStyle::NOPROGRAMME, DrawStyle::LIGHTTEXT); // fill row with no programme colour to be painted ove with valid programmes\r
- if (currentRow)\r
- {\r
- thisEvent.setdescription(tr("There are no programme details available for this period"));\r
- thisEvent.duration = WINDOW_WIDTH * 60;\r
- thisEvent.time = ltime;\r
- thisEvent.settitle(tr("No programme details"));\r
- thisEvent.id = 0;\r
- }\r
- if (eventLista[listIndex])\r
- {\r
- sort(eventLista[listIndex]->begin(), eventLista[listIndex]->end(), EventSorter());\r
- for(e = 0; e < (eventLista[listIndex])->size(); e++) // step through events for this channel\r
- {\r
- fg = DrawStyle::LIGHTTEXT;\r
- event = (*eventLista[listIndex])[e];\r
- if (event)\r
- {\r
- UINT end = event->time + event->duration; // programme end time\r
- if(event->time >= UINT(ltime) + (WINDOW_WIDTH * 60)) // programme starts after RHS of window\r
- continue; // that's enough of this channel's events\r
- if(end <= UINT(ltime)) // programme ends before LHS of window\r
- continue; // this event is before the window - let's try the next event\r
- // this event is one we are interested in\r
- bg = (swapColour)?DrawStyle::PROGRAMMEA:DrawStyle::PROGRAMMEB; // alternate cell colour\r
- swapColour = !swapColour; // it wil be the other colour next time\r
- if(event->time <= UINT(selTime) && end > UINT(selTime) && currentRow)\r
- {\r
- // this is the selected programme\r
- thisEvent.setdescription(event->description);\r
- thisEvent.duration = event->duration;\r
- thisEvent.time = event->time;\r
- thisEvent.settitle(event->title);\r
- thisEvent.id = event->id;\r
- if(thisEvent.id == 0)\r
- thisEvent.id = 1;\r
- bg = DrawStyle::SELECTHIGHLIGHT; // highlight cell\r
- fg = DrawStyle::DARKTEXT;\r
- }\r
- else\r
- {\r
- if (currentRow && thisEvent.id == 0)\r
- {\r
- if (end <= UINT(selTime) && end > UINT(thisEvent.time))\r
- thisEvent.time = end;\r
- if (event->time > UINT(selTime) && event->time < thisEvent.time + thisEvent.duration)\r
- thisEvent.duration = event->time - thisEvent.time;\r
- }\r
- }\r
- paintCell(event, y, bg, fg);\r
- }\r
- }\r
- }\r
- else\r
- {\r
- // no event list for this channel. Already painted noevent colour so just highlight if selected\r
- if (currentRow)\r
- {\r
- bg = DrawStyle::SELECTHIGHLIGHT; // highlight cell\r
- fg = DrawStyle::DARKTEXT;\r
- paintCell(&thisEvent, y, bg, fg);\r
- }\r
- else\r
- {\r
- bg = DrawStyle::NOPROGRAMME;\r
- fg = DrawStyle::LIGHTTEXT;\r
- noevent.settitle(tr("No programme details"));\r
- paintCell(&noevent, y, bg, fg);\r
- }\r
- }\r
- y += getFontHeight() + 2;\r
- }\r
- setInfo(&thisEvent);\r
-}\r
-\r
-void VEpg::updateEventList()\r
-{\r
- if (!chanList) return;\r
- Channel* chan;\r
- for(UINT listIndex = 0; listIndex < gridRows; listIndex++)\r
- {\r
- if(listTop + listIndex >= UINT(chanListbox.getBottomOption()))\r
- continue;\r
- chan = (*chanList)[listTop + listIndex];\r
-\r
- 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\r
- }\r
-}\r
-\r
-void VEpg::setCurrentChannel()\r
-{\r
- chanName.setText((*chanList)[currentChannelIndex]->name);\r
- chanName.draw();\r
- Region r;\r
- chanName.getRootBoxRegion(&r);\r
- boxstack->update(this, &r);\r
-}\r
-\r
-void VEpg::paintCell(Event* event, int yOffset, const DrawStyle& bg, const DrawStyle& fg)\r
-{\r
- int w, x, y, h;\r
- w = x = 0; // keep compiler happy\r
-\r
- y =yOffset;\r
- h = getFontHeight(); // TODO if want border around text, need to increae this and wselectlist line height\r
- UINT end = event->time + event->duration; // programme end time\r
- if(event->time <= UINT(ltime) && end > UINT(ltime)) // spans start of displayed window\r
- {\r
- x = 155; // LHS of window\r
- if (end > (UINT(ltime) + (WINDOW_WIDTH * 60)))\r
- w = WINDOW_WIDTH * MINUTE_SCALE; // spans full 2 hour window\r
- else\r
- w = MINUTE_SCALE * (event->time + event->duration - ltime ) / 60; // get width of remaining programme\r
- }\r
- if((event->time >= UINT(ltime)) && (event->time <= UINT(ltime) + (WINDOW_WIDTH * 60))) // starts within window\r
- {\r
- x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);\r
- w = MINUTE_SCALE * event->duration / 60;\r
- //if (w > 155 + MINUTE_SCALE * WINDOW_WIDTH -x)\r
- // w = w + x - 155 - MINUTE_SCALE * WINDOW_WIDTH; // ends outside window\r
- }\r
- if (w > 155 + WINDOW_WIDTH * MINUTE_SCALE - x)\r
- w = 155 + WINDOW_WIDTH * MINUTE_SCALE -x; // limit cells to RHS of window\r
- rectangle(x, y, w, h, bg);\r
- char* tt = new char[strlen(event->title) + 1];\r
- strcpy (tt, event->title);\r
- float textWidth = 0;\r
- unsigned int cur_length=1;\r
- unsigned int text_max=strlen(tt);\r
- UINT textPos;\r
- for (textPos = 0; textPos <text_max; textPos+=cur_length)\r
- {\r
- wchar_t cur_char=getWChar(tt+textPos,&cur_length);\r
- float thisCharWidth = charWidth(cur_char);\r
- if (textWidth + thisCharWidth > w) // text will not fit in cell\r
- {\r
- break;\r
- }\r
- textWidth += thisCharWidth;\r
- }\r
- char* tT = new char[textPos+1];\r
- if(textPos > 0)\r
- {\r
- strncpy(tT, tt, textPos );\r
- tT[textPos ] = '\0';\r
- surface->drawText(tT, x+2, y, fg);\r
- }\r
- delete tT;\r
-\r
-}\r
-\r
-time_t VEpg::prevHour(time_t* t)\r
-{\r
- struct tm* tms;\r
- tms = localtime(t);\r
- tms->tm_sec = 0;\r
- tms->tm_min = 0;\r
- return mktime(tms);\r
-}\r
-\r
-void VEpg::processMessage(Message* m)\r
-{\r
- if (m->message == Message::MOUSE_MOVE)\r
- {\r
- if (chanListbox.mouseMove((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY()))\r
- {\r
- drawData();\r
- boxstack->update(this);\r
- }\r
- }\r
- else if (m->message == Message::MOUSE_LBDOWN)\r
- {\r
- if (chanListbox.mouseLBDOWN((m->parameter>>16)-getScreenX(),(m->parameter&0xFFFF)-getScreenY()))\r
- {\r
- boxstack->handleCommand(Remote::OK); //simulate OK press\r
- }\r
- else\r
- {\r
- //check if press is outside this view! then simulate cancel\r
- int x=(m->parameter>>16)-getScreenX();\r
- int y=(m->parameter&0xFFFF)-getScreenY();\r
- int keyx = chanListbox.getRootBoxOffsetX();\r
- int keyy = chanListbox.getRootBoxOffsetY() + chanListbox.getHeight() + 2;\r
-\r
- if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight())\r
- {\r
- boxstack->handleCommand(Remote::BACK); //simulate cancel press\r
- }\r
- else if (x>=(keyx+72) && y>=(keyy+4) &&x<=(keyx+72+104) &&y<=(keyy+4+getFontHeight() + 2))\r
- {\r
- boxstack->handleCommand(Remote::RED);\r
- }\r
- else if (x>=(keyx+72) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+72+104) &&y<=(keyy+8+2*getFontHeight() + 2))\r
- {\r
- boxstack->handleCommand(Remote::GREEN);\r
- }\r
- else if (x>=(keyx+180) && y>=(keyy+4) &&x<=(keyx+180+104) &&y<=(keyy+4+getFontHeight() + 2))\r
- {\r
- boxstack->handleCommand(Remote::YELLOW);\r
- }\r
- else if (x>=(keyx+180) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+180+104) &&y<=(keyy+8+2*getFontHeight() + 2))\r
- {\r
- boxstack->handleCommand(Remote::BLUE);\r
- }\r
- else if (x>=(keyx+290) && y>=(keyy+4) &&x<=(keyx+180+290) &&y<=(keyy+4+getFontHeight() + 2))\r
- {\r
- boxstack->handleCommand(Remote::BACK);\r
- }\r
- else if (x>=(keyx+290) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+290+180) &&y<=(keyy+8+2*getFontHeight() + 2))\r
- {\r
- boxstack->handleCommand(Remote::RECORD);\r
- }\r
- else if (x>=(keyx+474) && y>=(keyy+4) &&x<=(keyx+128+474) &&y<=(keyy+4+getFontHeight() + 2))\r
- {\r
- boxstack->handleCommand(Remote::PLAY);\r
- }\r
- else if (x>=(keyx+474) && y>=(keyy+ getFontHeight() + 8) &&x<=(keyx+238+474) &&y<=(keyy+8+2*getFontHeight() + 2))\r
- {\r
- boxstack->handleCommand(Remote::GO);\r
- }\r
- else if ( x>=(chanListbox.getRootBoxOffsetX())\r
- && y>=(chanListbox.getRootBoxOffsetY() + 5)\r
- // &&x<=(chanListbox.getOffsetX()+155 + WINDOW_WIDTH * MINUTE_SCALE)\r
- &&y<=(chanListbox.getRootBoxOffsetY() - getFontHeight()\r
- - 3+(int)chanListbox.getHeight() + getFontHeight() + 3)\r
- )\r
- {\r
- int cy=y-(chanListbox.getRootBoxOffsetY() + 5);\r
- int row=cy/(getFontHeight()+2);\r
- int clistTop = chanListbox.getTopOption();\r
- chanListbox.hintSetCurrent(clistTop+row);\r
- int cx=x-155;\r
- time_t ttime = cx*60/MINUTE_SCALE+ltime;\r
- //x = 155 + (MINUTE_SCALE * (event->time - ltime) / 60);\r
-\r
- selTime = ttime;\r
- drawData();\r
- boxstack->update(this);\r
- }\r
- }\r
- }\r
-}\r
-\r
+/*
+ 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
+// DrawStyle transparent = DrawStyle(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(DrawStyle::TITLEBARBACKGROUND);
+ progTitle.setTextPos(5, 16);
+ progTitle.setGap(4);
+ add(&progTitle);
+
+// progInfo.setSurface(surface);
+ progInfo.setBackgroundColour(DrawStyle::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);
+ DrawStyle 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
+ DrawStyle transparent = DrawStyle(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;
+ DrawStyle ref1 = DrawStyle(100, 100, 100, 255); //to do global definition for skin
+ 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, DrawStyle::LIGHTTEXT);
+
+ DrawStyle ref2 = DrawStyle(200, 0, 0, 255);
+ rectangle(keyx + 72, keyy + 4, 104, getFontHeight() + 2, ref2);
+ drawText(tr("Page up"), keyx + 74, keyy + 5, DrawStyle::LIGHTTEXT);
+
+ DrawStyle ref3 = DrawStyle(0, 200, 0, 255);
+ rectangle(keyx + 72, keyy + getFontHeight() + 8, 104, getFontHeight() + 2, ref3);
+ drawText(tr("Page down"), keyx + 74, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
+
+ DrawStyle ref4 = DrawStyle(200, 200, 0, 255);
+ rectangle(keyx + 180, keyy + 4, 104, getFontHeight() + 2, ref4);
+ drawText(tr("-24 hours"), keyx + 182, keyy + 5, DrawStyle::LIGHTTEXT);
+
+ DrawStyle ref5 = DrawStyle(0, 0, 200, 255);
+ rectangle(keyx + 180, keyy + getFontHeight() + 8, 104, getFontHeight() + 2, ref5);
+ drawText(tr("+24 hours"), keyx + 182, keyy + getFontHeight() + 9, DrawStyle::LIGHTTEXT);
+
+ DrawStyle ref6 = DrawStyle(180, 180, 180, 255);
+ rectangle(keyx + 290, keyy + 4, 180, getFontHeight() + 2, ref6);
+ drawText(tr("Guide / Back: Close"), keyx + 292 , keyy + 5, DrawStyle::LIGHTTEXT);
+
+ DrawStyle ref7 = DrawStyle(180, 180, 180, 255);
+ rectangle(keyx + 290, keyy + getFontHeight() + 8, 180, getFontHeight() + 2, ref7);
+ DrawStyle red = DrawStyle(130, 0, 0);
+ drawText(tr("Rec: Set timer"), keyx + 292, keyy + getFontHeight() + 9, red);
+
+ DrawStyle ref8 = DrawStyle(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, DrawStyle::LIGHTTEXT);
+
+ DrawStyle ref9 = DrawStyle(180, 180, 180, 255);
+ rectangle(keyx + 474, keyy + getFontHeight() + 8, 128, getFontHeight() + 2, ref9);
+ drawText(tr("Go: Preview"), keyx + 476, keyy + getFontHeight() + 9, DrawStyle::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,
+ DrawStyle::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
+ DrawStyle white = DrawStyle(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, DrawStyle::LIGHTTEXT); // print date
+ strftime(timeString, 19, "%H:%M", tms);
+ drawText(timeString, timex, timey, DrawStyle::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, DrawStyle::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, DrawStyle::LIGHTTEXT); // print right time
+ rectangle(515, timey + getFontHeight(), 2, 7, white);
+ // pointer to selTime
+ //rectangle(155 + (selTime - ltime) / 20, timey + getFontHeight(), 2, 7, DrawStyle(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, DrawStyle::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
+ DrawStyle 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, DrawStyle::NOPROGRAMME, DrawStyle::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 = DrawStyle::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)?DrawStyle::PROGRAMMEA:DrawStyle::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 = DrawStyle::SELECTHIGHLIGHT; // highlight cell
+ fg = DrawStyle::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 = DrawStyle::SELECTHIGHLIGHT; // highlight cell
+ fg = DrawStyle::DARKTEXT;
+ paintCell(&thisEvent, y, bg, fg);
+ }
+ else
+ {
+ bg = DrawStyle::NOPROGRAMME;
+ fg = DrawStyle::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 DrawStyle& bg, const DrawStyle& 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);
+ float textWidth = 0;
+ unsigned int cur_length=1;
+ unsigned int text_max=strlen(tt);
+ UINT textPos;
+ for (textPos = 0; textPos <text_max; textPos+=cur_length)
+ {
+ wchar_t cur_char=getWChar(tt+textPos,&cur_length);
+ float thisCharWidth = charWidth(cur_char);
+ if (textWidth + thisCharWidth > w) // text will not fit in cell
+ {
+ break;
+ }
+ textWidth += thisCharWidth;
+ }
+ char* tT = new char[textPos+1];
+ if(textPos > 0)
+ {
+ strncpy(tT, tt, textPos );
+ tT[textPos ] = '\0';
+ surface->drawText(tT, x+2, y, fg);
+ }
+ 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);
+ }
+ }
+ }
+}
+
-/*\r
- Copyright 2004-2005 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#include "vfeed.h"\r
-\r
-#include "log.h"\r
-#include "demuxer.h"\r
-#include "callback.h"\r
-\r
-VFeed::VFeed(Callback* tcb)\r
-: cb(*tcb)\r
-{\r
-}\r
-\r
-int VFeed::init()\r
-{\r
- return 1;\r
-}\r
-\r
-int VFeed::shutdown()\r
-{\r
- // FIXME\r
- return 1;\r
-}\r
-\r
-int VFeed::start()\r
-{\r
- return threadStart();\r
-}\r
-\r
-void VFeed::stop()\r
-{\r
- Log::getInstance()->log("VFeed", Log::DEBUG, "Stop1");\r
- threadCancel();\r
- Log::getInstance()->log("VFeed", Log::DEBUG, "Stop2");\r
-}\r
-\r
-void VFeed::release()\r
-{\r
- threadSignal();\r
-}\r
-\r
-void VFeed::threadMethod()\r
-{\r
- bool vlen;\r
-\r
- Log::getInstance()->log("VFeed", Log::DEBUG, "Started");\r
- threadWaitForSignal(); // Don't feed video until audio has started\r
- Log::getInstance()->log("VFeed", Log::DEBUG, "Released");\r
-\r
- while(1)\r
- {\r
- threadCheckExit();\r
- vlen = Demuxer::getInstance()->writeVideo();\r
-\r
- if (vlen)\r
- {\r
- cb.call(this);\r
- }\r
- else\r
- {\r
- //Log::getInstance()->log("VFeed", Log::DEBUG, "No data delay FPS");\r
- MILLISLEEP(5);\r
- }\r
- }\r
-}\r
-\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 "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 FPS");
+ MILLISLEEP(5);
+ }
+ }
+}
+
-/*\r
- Copyright 2004-2005 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-//portions from vdr by Klaus Schmiding HSMF code\r
-\r
-#include "video.h"\r
-\r
-Video* Video::instance = NULL;\r
-\r
-Video::Video()\r
-{\r
- if (instance) return;\r
- instance = this;\r
- initted = 0;\r
-\r
- fdVideo = 0;\r
-\r
- format = 0;\r
- connection = 0;\r
- aspectRatio = 0;\r
- mode = 0;\r
- tvsize = 0;\r
-\r
- screenWidth = 0;\r
- screenHeight = 0;\r
-}\r
-\r
-Video::~Video()\r
-{\r
- instance = NULL;\r
-}\r
-\r
-Video* Video::getInstance()\r
-{\r
- return instance;\r
-}\r
-/*\r
-void Video::setInstance(Video* inst)\r
-{\r
- instance=inst;\r
-}*/\r
-\r
-/*\r
-hmsf Video::framesToHMSF(ULONG frames, double fps)\r
-{\r
- hmsf ret;\r
- / * from vdr *\r
- double Seconds;\r
- ret.frames= int(modf((frames + 0.5) / fps, &Seconds) * fps + 1);\r
- int s = int(Seconds);\r
- ret.seconds=s;\r
- ret.minutes = s / 60 % 60;\r
- ret.hours = s / 3600;\r
-\r
-/ * if (format == NTSC)\r
- {\r
- ret.hours = frames / 108000;\r
- frames %= 108000;\r
- ret.minutes = frames / 1800;\r
- frames %= 1800;\r
- ret.seconds = frames / 30;\r
- ret.frames = frames % 30;\r
- }\r
- else\r
- {\r
- ret.hours = frames / 90000;\r
- frames %= 90000;\r
- ret.minutes = frames / 1500;\r
- frames %= 1500;\r
- ret.seconds = frames / 25;\r
- ret.frames = frames % 25;\r
- }* /\r
- return ret;\r
-}*/\r
-\r
-/*\r
-UINT Video::getFPS()\r
-{\r
- if (format == NTSC) return 30;\r
- else return 25;\r
-}\r
-*/\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.
+*/
+//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;
+}
+*/
-/*\r
- Copyright 2004-2005 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef VIDEO_H\r
-#define VIDEO_H\r
-\r
-#include <stdio.h>\r
-#include "defines.h"\r
-#include "draintarget.h"\r
-#include "abstractoption.h"\r
-\r
-typedef struct _hmsf\r
-{\r
- UINT hours;\r
- UINT minutes;\r
- UINT seconds;\r
- UINT frames;\r
-} hmsf;\r
-\r
-class Video: public DrainTarget, public AbstractOption\r
-{\r
- public:\r
- Video();\r
- virtual ~Video();\r
- static Video* getInstance();\r
- //static void setInstance(Video*);\r
-\r
- virtual int init(UCHAR format)=0;\r
- virtual int shutdown()=0;\r
- virtual int setFormat(UCHAR format)=0;\r
- virtual UCHAR getSupportedFormats() { return COMPOSITERGB | SVIDEO;}; // if it returns zero there are no different formats\r
- virtual UINT supportedTVsize() { return 0;}; // if no selection is allowed\r
- virtual UCHAR supportedTVFormats() { return 0;}; // if no selection is allowed\r
-\r
- virtual int setConnection(UCHAR connection)=0;\r
- virtual int setAspectRatio(UCHAR aspectRatio)=0; // This one does the pin 8 scart widescreen switching\r
- virtual int setMode(UCHAR mode)=0;\r
- virtual int setTVsize(UCHAR size)=0; // Is the TV a widescreen?\r
- virtual void executePendingModeChanges() {}; // This is called if you change the output mode and the device take a while for reinitialization\r
- virtual int setDefaultAspect()=0;\r
- virtual int setSource()=0;\r
- virtual int setPosition(int x, int y)=0;\r
- virtual int sync()=0;\r
- virtual int play()=0;\r
- virtual int stop()=0;\r
- virtual int pause()=0;\r
- virtual int unPause()=0;\r
- virtual int fastForward()=0;\r
- virtual int unFastForward()=0;\r
- virtual int reset()=0;\r
- virtual int blank()=0;\r
- virtual int signalOn()=0;\r
- virtual int signalOff()=0;\r
- virtual int attachFrameBuffer()=0; // What does this do?\r
-// virtual ULONG timecodeToFrameNumber(ULLONG timecode)=0; //Obsolete and not HD compatible\r
- virtual ULLONG getCurrentTimestamp()=0;\r
- virtual bool displayIFrame(const UCHAR* buffer, UINT length)=0;\r
- virtual int EnterIframePlayback() {return 1;}; // Must not be implemented\r
-\r
- virtual bool supportsh264(){return false;};\r
- virtual void seth264mode(bool ish264) {h264=ish264;};\r
- virtual int getTeletextBufferFaktor(){return 1;};\r
-\r
- virtual bool independentAVStartUp() {return true;};\r
-\r
-\r
- //Tells, if the device allows an independent startup of audio and video\r
- // this needs internal buffers in device and is possible for windows and mvp\r
-\r
- virtual bool PTSIFramePlayback() {return false;};\r
- // Tells, if the iframe playback is realized using a manipulation of the pts of the packets\r
- //android does it like this...\r
-\r
- virtual bool blockingDrainTarget() { return false;};\r
- // if the draintargets blocks, the feed has to be stop when pausing\r
-\r
-\r
- virtual void turnVideoOn(){};\r
- virtual void turnVideoOff(){};\r
-// virtual ULLONG frameNumberToTimecode(ULONG timecode) { return 0; };//Obsolete and not HD compatible\r
-\r
-#ifdef DEV\r
- virtual int test() { return 0; }\r
- virtual int test2() { return 0; }\r
-#endif\r
-\r
- int getMode() { return mode; }\r
- UCHAR getFormat() { return format; }\r
- UINT getScreenWidth() { return screenWidth; }\r
- UINT getScreenHeight() { return screenHeight; }\r
- UCHAR getTVsize() { return tvsize; }\r
-\r
-\r
-\r
- //hmsf framesToHMSF(ULONG frames,double fps);\r
- // UINT getFPS(); //removed\r
-\r
- // Video formats - AV_SET_VID_DISP_FMT\r
- const static UCHAR NTSC = 0;\r
- const static UCHAR PAL = 1;\r
- const static UCHAR PAL_M = 2;\r
- const static UCHAR NTSC_J = 4;\r
-\r
- // Video connections - AV_SET_VID_OUTPUT\r
- const static UCHAR COMPOSITERGB = 1;\r
- const static UCHAR SVIDEO = 2;\r
- const static UCHAR HDMI = 4;\r
- const static UCHAR HDMI3D = 16; //For future use\r
-\r
- // Video aspect ratios - AV_SET_VID_RATIO\r
- const static UCHAR ASPECT4X3 = 0;\r
- const static UCHAR ASPECT16X9 = 1;\r
- const static UCHAR ASPECT14X9 = 2; //future use\r
-\r
- // Video modes - AV_SET_VID_MODE\r
- const static UCHAR NORMAL = 0;\r
- const static UCHAR LETTERBOX = 1;\r
-/*\r
- Actual Source Aspect Aspect IOCTL Mode IOCTL MODE A MODE B\r
-\r
- 4:3 4:3 NORMAL fullframe43 fullframe43\r
- 4:3 16:9 NORMAL fullframe43 fullframe43 -- invalid?\r
- 4:3 4:3 LETTERBOX fullframe43 fullframe43\r
- 4:3 16:9 LETTERBOX fullframe43 fullframe43 -- invalid?\r
- 16:9 4:3 NORMAL chop sides fullframe169\r
- 16:9 16:9 NORMAL chop sides fullframe169\r
- 16:9 4:3 LETTERBOX letterbox letterbox\r
- 16:9 16:9 LETTERBOX chop sides fullframe169\r
-\r
- Conclusions\r
-\r
- 1. There are two chip modes - accessible by reopening the fd\r
- 2. The video chip knows the aspect ratio purely from the incoming MPEG\r
- 3. MODE A is for 4:3 TVs, MODE B is for 16:9 TVs\r
-\r
- To switch to MODE A, set the aspect ioctl to 4:3 and reopen the FD.\r
- To switch to MODE B, set the aspect ioctl to 16:9 and reopen the FD.\r
-*/\r
-\r
- const static UCHAR UNKNOWN2 = 2;\r
- const static UCHAR QUARTER = 3;\r
- const static UCHAR EIGHTH = 4;\r
- const static UCHAR ZOOM = 5;\r
- const static UCHAR UNKNOWN6 = 6;\r
-\r
- protected:\r
- static Video* instance;\r
- int initted;\r
- int fdVideo;\r
-\r
- UCHAR tvsize;\r
- UCHAR format;\r
- UCHAR connection;\r
- UCHAR aspectRatio;\r
- UCHAR mode;\r
- bool h264;\r
-\r
- UINT screenWidth;\r
- UINT screenHeight;\r
-};\r
-\r
-#endif\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.
+*/
+
+#ifndef VIDEO_H
+#define VIDEO_H
+
+#include <stdio.h>
+#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 UCHAR getSupportedFormats() { return COMPOSITERGB | SVIDEO;}; // if it returns zero there are no different formats
+ virtual UINT supportedTVsize() { return 0;}; // if no selection is allowed
+ virtual UCHAR supportedTVFormats() { return 0;}; // if no selection is allowed
+
+ 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 void executePendingModeChanges() {}; // This is called if you change the output mode and the device take a while for reinitialization
+ 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;
+ const static UCHAR PAL_M = 2;
+ const static UCHAR NTSC_J = 4;
+
+ // Video connections - AV_SET_VID_OUTPUT
+ const static UCHAR COMPOSITERGB = 1;
+ const static UCHAR SVIDEO = 2;
+ const static UCHAR HDMI = 4;
+ const static UCHAR HDMI3D = 16; //For future use
+
+ // Video aspect ratios - AV_SET_VID_RATIO
+ const static UCHAR ASPECT4X3 = 0;
+ const static UCHAR ASPECT16X9 = 1;
+ const static UCHAR ASPECT14X9 = 2; //future use
+
+ // 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
-/*\r
- Copyright 2004-2005 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#include "videomvp.h"\r
-\r
-// temp\r
-#include "log.h"\r
-\r
-VideoMVP::VideoMVP()\r
-{\r
- if (instance) return;\r
-}\r
-\r
-VideoMVP::~VideoMVP()\r
-{\r
- instance = NULL;\r
-}\r
-\r
-int VideoMVP::init(UCHAR tformat)\r
-{\r
- if (initted) return 0;\r
- initted = 1;\r
-\r
- if ((fdVideo = open("/dev/vdec_dev", O_WRONLY)) < 0) return 0;\r
-\r
- if (!setFormat(tformat)) { shutdown(); return 0; }\r
- if (!setConnection(COMPOSITERGB)) { shutdown(); return 0; }\r
- if (!setAspectRatio(ASPECT4X3)) { shutdown(); return 0; }\r
- if (!setMode(NORMAL)) { shutdown(); return 0; }\r
- if (!setSource()) { shutdown(); return 0; }\r
- if (!attachFrameBuffer()) { shutdown(); return 0; }\r
-\r
- setTVsize(ASPECT4X3);\r
-\r
- if (format == PAL) setLetterboxBorder("38");\r
- else setLetterboxBorder("31");\r
-\r
- stop();\r
-\r
-\r
- return 1;\r
-}\r
-\r
-void VideoMVP::setLetterboxBorder(char* border)\r
-{\r
- FILE* fdlbox = fopen("/proc/lbox_border", "w");\r
- if (!fdlbox) return;\r
- fputs(border, fdlbox);\r
- fclose(fdlbox);\r
-}\r
-\r
-int VideoMVP::setTVsize(UCHAR ttvsize)\r
-{\r
- tvsize = ttvsize;\r
-\r
- // Override the aspect ratio usage, temporarily use to set the video chip mode\r
- if (!setAspectRatio(tvsize)) { shutdown(); return 0; }\r
- close(fdVideo);\r
- if ((fdVideo = open("/dev/vdec_dev", O_WRONLY)) < 0) return 0;\r
- if (!setSource()) { shutdown(); return 0; }\r
- if (!attachFrameBuffer()) { shutdown(); return 0; }\r
-\r
- // Reopening the fd causes the scart aspect line to go back to 4:3\r
- // Set this again to the same as the tv screen size\r
- if (!setAspectRatio(tvsize)) { shutdown(); return 0; }\r
-\r
- // mode == LETTERBOX is invalid if the TV is widescreen\r
- if (tvsize == ASPECT16X9) setMode(NORMAL);\r
-\r
- return 1;\r
-}\r
-\r
-int VideoMVP::setDefaultAspect()\r
-{\r
- return setAspectRatio(tvsize);\r
-}\r
-\r
-int VideoMVP::shutdown()\r
-{\r
- if (!initted) return 0;\r
- initted = 0;\r
- close(fdVideo);\r
- return 1;\r
-}\r
-\r
-int VideoMVP::checkSCART()\r
-{\r
- // Returns 3 for SCART Composite out\r
- // Returns 3 for SCART S-Video out\r
- // Returns 2 for SCART RGB out\r
- // Returns 3 for SCART not plugged in\r
-\r
- // So, as you can have RGB and composite out simultaneously,\r
- // and it can't detect S-Video, what is the point of this?\r
-\r
- int scart;\r
- if (ioctl(fdVideo, AV_CHK_SCART, &scart) != 0) return -10;\r
-\r
- return scart;\r
-}\r
-\r
-int VideoMVP::setFormat(UCHAR tformat)\r
-{\r
- if (!initted) return 0;\r
- if ((tformat != PAL) && (tformat != NTSC)) return 0;\r
- format = tformat;\r
-\r
- if (ioctl(fdVideo, AV_SET_VID_DISP_FMT, format) != 0) return 0;\r
-\r
- if (format == NTSC)\r
- {\r
- screenWidth = 720;\r
- screenHeight = 480;\r
- }\r
- if (format == PAL)\r
- {\r
- screenWidth = 720;\r
- screenHeight = 576;\r
- }\r
-\r
- return 1;\r
-}\r
-\r
-int VideoMVP::setConnection(UCHAR tconnection)\r
-{\r
- if (!initted) return 0;\r
- if ((tconnection != COMPOSITERGB) && (tconnection != SVIDEO)) return 0;\r
- connection = tconnection;\r
-\r
- if (ioctl(fdVideo, AV_SET_VID_OUTPUT, connection) != 0) return 0;\r
- return 1;\r
-}\r
-\r
-int VideoMVP::setAspectRatio(UCHAR taspectRatio)\r
-{\r
- if (!initted) return 0;\r
- if ((taspectRatio != ASPECT4X3) && (taspectRatio != ASPECT16X9)) return 0;\r
- aspectRatio = taspectRatio;\r
-\r
- Log::getInstance()->log("Video", Log::DEBUG, "Setting aspect to %i", aspectRatio);\r
-\r
- if (ioctl(fdVideo, AV_SET_VID_RATIO, aspectRatio) != 0) return 0;\r
- return 1;\r
-}\r
-\r
-int VideoMVP::setMode(UCHAR tmode)\r
-{\r
- if (!initted) return 0;\r
-\r
- if ((tmode == LETTERBOX) && (tvsize == ASPECT16X9)) return 0; // invalid mode\r
-\r
- if ((tmode != NORMAL) && (tmode != LETTERBOX) && (tmode != UNKNOWN2) && (tmode != QUARTER) && (tmode != EIGHTH)\r
- && (tmode != ZOOM) && (tmode != UNKNOWN6)) return 0;\r
- mode = tmode;\r
-\r
- if (ioctl(fdVideo, AV_SET_VID_MODE, mode) != 0) return 0;\r
- return 1;\r
-}\r
-\r
-int VideoMVP::signalOff()\r
-{\r
- if (ioctl(fdVideo, AV_SET_VID_DENC, 0) != 0) return 0;\r
- return 1;\r
-}\r
-\r
-int VideoMVP::signalOn()\r
-{\r
- if (ioctl(fdVideo, AV_SET_VID_DENC, 1) != 0) return 0;\r
- return 1;\r
-}\r
-\r
-int VideoMVP::setSource()\r
-{\r
- if (!initted) return 0;\r
-\r
- // What does this do...\r
- if (ioctl(fdVideo, AV_SET_VID_SRC, 1) != 0) return 0;\r
- return 1;\r
-}\r
-\r
-int VideoMVP::setPosition(int x, int y)\r
-{\r
- if (!initted) return 0;\r
-\r
-// vid_pos_regs_t pos_d;\r
-// pos_d.x = x;\r
-// pos_d.y = y;\r
-\r
- vid_pos_regs_t pos_d;\r
-\r
- memset(&pos_d, 0, sizeof(pos_d));\r
-\r
- pos_d.dest.y = y;\r
- pos_d.dest.x = x;\r
-/*\r
-typedef struct {\r
- int w;\r
- int h;\r
- int scale;\r
- int x1;\r
- int y;\r
- int x;\r
- int y2;\r
- int x3;\r
- int y3;\r
- int x4;\r
- int y4;\r
-} vid_pos_regs_t;\r
-*/\r
-\r
-/*\r
- pos_d.w = 100;\r
- pos_d.h = 30;\r
- pos_d.scale = 2;\r
- pos_d.x1 = 0;\r
- pos_d.y = 100; // Top left X\r
- pos_d.x = 50; // Top left Y\r
- pos_d.y2 = 30;\r
- pos_d.x3 = 60;\r
- pos_d.y3 = 90;\r
- pos_d.x4 = 120;\r
- pos_d.y4 = 150;\r
-*/\r
-\r
- if (ioctl(fdVideo, AV_SET_VID_POSITION, &pos_d) != 0) return 0;\r
- return 1;\r
-}\r
-\r
-int VideoMVP::sync()\r
-{\r
- if (!initted) return 0;\r
-\r
- if (ioctl(fdVideo, AV_SET_VID_SYNC, 2) != 0) return 0;\r
- return 1;\r
-}\r
-\r
-int VideoMVP::play()\r
-{\r
- if (!initted) return 0;\r
-\r
- if (ioctl(fdVideo, AV_SET_VID_PLAY, 0) != 0) return 0;\r
- return 1;\r
-}\r
-\r
-int VideoMVP::stop()\r
-{\r
- if (!initted) return 0;\r
-\r
- if (ioctl(fdVideo, AV_SET_VID_STOP, 0) != 0) return 0;\r
- return 1;\r
-}\r
-\r
-int VideoMVP::reset()\r
-{\r
- if (!initted) return 0;\r
-\r
- if (ioctl(fdVideo, AV_SET_VID_RESET, 0x11) != 0) return 0;\r
- return 1;\r
-}\r
-\r
-int VideoMVP::pause()\r
-{\r
- if (!initted) return 0;\r
-\r
- if (ioctl(fdVideo, AV_SET_VID_PAUSE, 0) != 0) return 0;\r
- return 1;\r
-}\r
-\r
-int VideoMVP::unPause() // FIXME get rid - same as play!!\r
-{\r
- if (!initted) return 0;\r
- if (ioctl(fdVideo, AV_SET_VID_PLAY, 0) != 0) return 0;\r
- return 1;\r
-}\r
-\r
-int VideoMVP::fastForward()\r
-{\r
- if (!initted) return 0;\r
-\r
- if (ioctl(fdVideo, AV_SET_VID_FFWD, 1) != 0) return 0;\r
- return 1;\r
-}\r
-\r
-int VideoMVP::unFastForward()\r
-{\r
- if (!initted) return 0;\r
-\r
-// if (ioctl(fdVideo, AV_SET_VID_RESET, 0x11) != 0) return 0; // don't need this.\r
-\r
- if (ioctl(fdVideo, AV_SET_VID_PLAY, 0) != 0) return 0;\r
- return 1;\r
-}\r
-\r
-int VideoMVP::attachFrameBuffer()\r
-{\r
- if (!initted) return 0;\r
-\r
- if (ioctl(fdVideo, AV_SET_VID_FB, 0) != 0) return 0;\r
- return 1;\r
-}\r
-\r
-int VideoMVP::blank(void)\r
-{\r
- if (ioctl(fdVideo, AV_SET_VID_FB, 1) != 0) return 0;\r
- if (ioctl(fdVideo, AV_SET_VID_FB, 0) != 0) return 0;\r
- return 1;\r
-}\r
-\r
-ULLONG VideoMVP::getCurrentTimestamp()\r
-{\r
- sync_data_t timestamps;\r
- if (ioctl(fdVideo, AV_GET_VID_TIMESTAMPS, ×tamps) == 0)\r
- {\r
- // FIXME are these the right way around?\r
-\r
- timestamps.stc = (timestamps.stc >> 31 ) | (timestamps.stc & 1);\r
- timestamps.pts = (timestamps.pts >> 31 ) | (timestamps.pts & 1);\r
-\r
- return timestamps.stc;\r
- }\r
- else\r
- {\r
- return 0;\r
- }\r
-}\r
-\r
-ULONG VideoMVP::timecodeToFrameNumber(ULLONG timecode)\r
-{\r
- if (format == PAL) return (ULONG)(((double)timecode / (double)90000) * (double)25);\r
- else return (ULONG)(((double)timecode / (double)90000) * (double)30);\r
-}\r
-\r
-#ifdef DEV\r
-int VideoMVP::test()\r
-{\r
- return 0;\r
-\r
-// ULLONG stc = 0;\r
-// return ioctl(fdVideo, AV_SET_VID_STC, &stc);\r
-/*\r
- // reset();\r
- return 1;\r
-*/\r
-}\r
-\r
-int VideoMVP::test2()\r
-{\r
- return 0;\r
-}\r
-#endif\r
-\r
-void VideoMVP::PrepareMediaSample(const MediaPacketList& mplist,UINT samplepos)\r
-{\r
- MediaPacketList::const_iterator iter = mplist.begin();\r
- deliver_start = iter->pos_buffer + samplepos;\r
- mediapacket_len[0] = deliver_length = iter->length;\r
- deliver_count = 1;\r
- while (++iter != mplist.end() &&\r
- iter->pos_buffer == deliver_start + deliver_length)\r
- {\r
- deliver_length += iter->length;\r
- mediapacket_len[deliver_count] = iter->length;\r
- ++deliver_count;\r
- if (deliver_length >= WRITE_LENGTH ||\r
- deliver_count == WRITE_PACKETS) break;\r
- }\r
-}\r
-\r
-UINT VideoMVP::DeliverMediaSample(UCHAR* buffer, UINT* samplepos)\r
-{\r
- int written = ::write(fdVideo, buffer + deliver_start, deliver_length);\r
- if (written == (int)deliver_length) { *samplepos = 0; return deliver_count;}\r
- if (written <= 0) return 0;\r
- // Handle a partial write. Is this possible? Should we bother?\r
- UINT i = 0;\r
- while ((written -= mediapacket_len[i]) >= 0) i++;\r
- *samplepos = mediapacket_len[i] + written;\r
- return i;\r
-}\r
-\r
-void VideoMVP::ResetTimeOffsets()\r
-{\r
-}\r
-\r
-bool VideoMVP::displayIFrame(const UCHAR* buffer, UINT length)\r
-{\r
- write(fdVideo, buffer, length);\r
- return true;\r
-}\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 "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;
+}
-/*\r
- Copyright 2004-2005 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-// Thanks to Jon Gettler and BtB of the MVPMC project for all the hardware information\r
-\r
-\r
-#ifndef VIDEOMVP_H\r
-#define VIDEOMVP_H\r
-\r
-// FIXME - check why so many things include unistd\r
-\r
-#include <stdio.h>\r
-#include <unistd.h>\r
-#include <fcntl.h>\r
-#include <sys/ioctl.h>\r
-#include <string.h>\r
-\r
-#include <stdint.h>\r
-\r
-#include "defines.h"\r
-#include "video.h"\r
-\r
-typedef struct\r
-{\r
- int nleft;\r
- int state;\r
-} vid_state_regs_t;\r
-\r
-typedef struct\r
-{\r
- uint64_t stc;\r
- uint64_t pts;\r
-} sync_data_t;\r
-\r
-typedef struct\r
-{\r
- int y;\r
- int x;\r
- int w;\r
- int h;\r
-} vid_rect_t;\r
-\r
-typedef struct\r
-{\r
- vid_rect_t src;\r
- vid_rect_t dest;\r
-} vid_pos_regs_t;\r
-\r
-struct vid_regs\r
-{\r
- UCHAR dummy[44];\r
-};\r
-\r
-#define AV_SET_VID_STOP _IOW('v', 21, int)\r
-#define AV_SET_VID_PLAY _IOW('v', 22, int)\r
-#define AV_SET_VID_FREEZE _IOW('v', 23, int)\r
-#define AV_SET_VID_RESUME _IOW('v', 24, int)\r
-#define AV_SET_VID_SRC _IOW('v', 25, int)\r
-#define AV_SET_VID_FB _IOW('v', 26, int)\r
-#define AV_GET_VID_STATE _IOR('v', 27, vid_state_regs_t*)\r
-#define AV_SET_VID_PAUSE _IOW('v', 28, int)\r
-#define AV_SET_VID_FFWD _IOW('v', 29, int)\r
-#define AV_SET_VID_SLOMO _IOW('v', 30, int)\r
-#define AV_SET_VID_BLANK _IOW('v', 32, int)\r
-#define AV_SET_VID_POSITION _IOW('v', 36, vid_pos_regs_t*)\r
-#define AV_SET_VID_SCALE_ON _IOW('v', 37, int)\r
-#define AV_SET_VID_SCALE_OFF _IOW('v', 38, int)\r
-#define AV_GET_VID_TIMESTAMPS _IOR('v', 39, sync_data_t*)\r
-#define AV_SET_VID_STC _IOW('v', 40, uint64_t*)\r
-#define AV_SET_VID_RATIO _IOW('v', 41, int)\r
-#define AV_SET_VID_SYNC _IOW('v', 42, int)\r
-#define AV_SET_VID_DISABLE_SYNC _IOW('v', 43, int)\r
-#define AV_SET_VID_DISP_FMT _IOW('v', 45, int)\r
-#define AV_SET_VID_RESET _IOW('v', 51, int)\r
-#define AV_SET_VID_OUTPUT _IOW('v', 57, int)\r
-#define AV_SET_VID_MODE _IOW('v', 58, int)\r
-#define AV_GET_VID_REGS _IOR('v', 61, struct vid_regs*)\r
-#define AV_SET_VID_COLORBAR _IOW('v', 62, int)\r
-#define AV_SET_VID_DENC _IOW('v', 63, int)\r
-#define AV_CHK_SCART _IOW('v', 64, int)\r
-\r
-#define WRITE_LENGTH (32*1024) // Consume up to 32K at a time from Stream\r
-#define WRITE_PACKETS 16 // But no more than 16 packets\r
-\r
-class VideoMVP : public Video\r
-{\r
- public:\r
- VideoMVP();\r
- virtual ~VideoMVP();\r
-\r
- int init(UCHAR format);\r
- int shutdown();\r
-\r
- UCHAR getSupportedFormats() { return COMPOSITERGB | SVIDEO;};\r
- UINT supportedTVsize() { return ASPECT4X3|ASPECT16X9;};\r
- UCHAR supportedTVFormats() { return 0;}; /* We cannot change this here\r
-\r
- int setFormat(UCHAR format);\r
- int setConnection(UCHAR connection);\r
- int setAspectRatio(UCHAR aspectRatio); // This one does the pin 8 scart widescreen switching\r
- int setMode(UCHAR mode);\r
- int setTVsize(UCHAR size); // Is the TV a widescreen?\r
- int setDefaultAspect();\r
- int setSource();\r
- int setPosition(int x, int y);\r
- int sync();\r
- int play();\r
- int stop();\r
- int pause();\r
- int unPause();\r
- int fastForward();\r
- int unFastForward();\r
- int reset();\r
- int blank();\r
- int signalOn();\r
- int signalOff();\r
- int attachFrameBuffer(); // What does this do?\r
- ULONG timecodeToFrameNumber(ULLONG timecode);\r
- ULLONG getCurrentTimestamp();\r
- bool displayIFrame(const UCHAR* buffer, UINT length);\r
-\r
- // Writing Data to Videodevice\r
- virtual void PrepareMediaSample(const MediaPacketList&, UINT samplepos);\r
- virtual UINT DeliverMediaSample( UCHAR* buffer, UINT* samplepos);\r
- virtual long long SetStartOffset(long long curreftime, bool *rsync)\r
- { return 0; };\r
- virtual void ResetTimeOffsets();\r
-\r
-#ifdef DEV\r
- int test();\r
- int test2();\r
-#endif\r
-\r
- private:\r
- int checkSCART();\r
- void setLetterboxBorder(char* border);\r
-\r
- UINT deliver_start, deliver_length, deliver_count;\r
- UINT mediapacket_len[WRITE_PACKETS];\r
-};\r
-\r
-#endif\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.
+*/
+
+// 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 <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <string.h>
+
+#include <stdint.h>
+
+#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();
+ virtual ~VideoMVP();
+
+ int init(UCHAR format);
+ int shutdown();
+
+ UCHAR getSupportedFormats() { return COMPOSITERGB | SVIDEO;};
+ UINT supportedTVsize() { return ASPECT4X3|ASPECT16X9;};
+ UCHAR supportedTVFormats() { return 0;}; /* We cannot change this here
+
+ 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
-/*\r
- Copyright 2004-2005 Chris Tallon 2009,2012 Marten Richter\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-\r
-#ifndef VIDEOOMX_H\r
-#define VIDEOOMX_H\r
-\r
-#include "mutex.h"\r
-\r
-\r
-#include <stdio.h>\r
-#include <unistd.h>\r
-#include <fcntl.h>\r
-#include <sys/ioctl.h>\r
-#include <string.h>\r
-\r
-#include <stdint.h>\r
-\r
-#include <list>\r
-#include <vector>\r
-\r
-#include "defines.h"\r
-#include "video.h"\r
-#include "threadsystem.h"\r
-\r
-#define OMX_SKIP64BIT\r
-\r
-#include <IL/OMX_Core.h>\r
-#include <IL/OMX_Types.h>\r
-#include <IL/OMX_Component.h>\r
-#include <IL/OMX_Broadcom.h>\r
-\r
-\r
-struct VPE_OMX_EVENT {\r
- OMX_IN OMX_HANDLETYPE handle;\r
- OMX_IN OMX_PTR appdata;\r
- OMX_IN OMX_EVENTTYPE event_type;\r
- OMX_IN OMX_U32 data1;\r
- OMX_IN OMX_U32 data2;\r
- OMX_IN OMX_PTR event_data;\r
-};\r
-\r
-\r
-\r
-\r
-class AudioVPE;\r
-\r
-class VideoOMX : public Video\r
-{\r
- friend class AudioOMX;\r
- public:\r
- VideoOMX();\r
- virtual ~VideoOMX();\r
-\r
- int init(UCHAR format);\r
- int shutdown();\r
-\r
- UCHAR getSupportedFormats() { return COMPOSITERGB | HDMI;};\r
- UINT supportedTVsize() { return ASPECT4X3|ASPECT16X9|ASPECT14X9 ;};\r
- UCHAR supportedTVFormats() { return NTSC|PAL|PAL_M|NTSC_J;};\r
-\r
- int setFormat(UCHAR format);\r
- int setConnection(UCHAR connection);\r
- int setAspectRatio(UCHAR aspectRatio); // This one does the pin 8 scart widescreen switching\r
- int setMode(UCHAR mode);\r
- int setTVsize(UCHAR size); // Is the TV a widescreen?\r
-\r
- void executePendingModeChanges();\r
- int setDefaultAspect();\r
- int setSource();\r
- int setPosition(int x, int y);\r
- int sync();\r
- int play();\r
- int stop();\r
- int pause();\r
- int unPause();\r
- int fastForward();\r
- int unFastForward();\r
- int reset();\r
- int blank();\r
- int signalOn();\r
- int signalOff();\r
- int attachFrameBuffer(); // What does this do?\r
- ULONG timecodeToFrameNumber(ULLONG timecode);\r
- ULLONG getCurrentTimestamp();\r
- bool displayIFrame(const UCHAR* bulibaver, UINT length);\r
-\r
- virtual bool dtsTimefix(){return false;} //please we need dts time values\r
- virtual int getTeletextBufferFaktor(){return 5;};\r
-\r
- // Writing Data to Videodevice\r
- virtual void PrepareMediaSample(const MediaPacketList&, UINT samplepos);\r
- virtual UINT DeliverMediaSample(UCHAR* bulibaver, UINT* samplepos);\r
-\r
-\r
- virtual bool supportsh264(){return true;};\r
-\r
- int WriteOutTS(const unsigned char *bulibaver,int length, int type);\r
- void WriteOutPATPMT();\r
-\r
-\r
-\r
- virtual long long SetStartOffset(long long curreftime, bool *rsync);\r
- long long SetStartAudioOffset(long long curreftime, bool *rsync);\r
- virtual void ResetTimeOffsets();\r
-\r
- bool loadOptionsfromServer(VDR* vdr);\r
- bool saveOptionstoServer();\r
- bool addOptionsToPanes(int panenumber,Options *options,WOptionPane* pane);\r
- bool handleOptionChanges(Option* option);\r
-\r
-\r
-#ifdef DEV\r
- int test();\r
- int test2();\r
-#endif\r
-\r
-\r
-\r
- static inline OMX_TICKS intToOMXTicks(long long pts) {\r
- OMX_TICKS temp;\r
- temp.nLowPart=pts;\r
- temp.nHighPart=pts>>32;\r
- return temp;\r
- }\r
-\r
-\r
-\r
- private:\r
- int EnterIframePlayback();\r
- bool iframemode;\r
- bool InIframemode() {return iframemode;};\r
-\r
- UINT DeliverMediaPacket(MediaPacket packet,const UCHAR* bulibaver,UINT *samplepos);\r
-\r
-#define VPE_DECODER_OMX 1\r
-\r
-\r
-\r
- bool offsetnotset;\r
- bool offsetvideonotset;\r
- bool offsetaudionotset;\r
- long long startoffset;\r
- long long lastrefvideotime;\r
- long long lastrefaudiotime;\r
+/*
+ Copyright 2004-2005 Chris Tallon 2009,2012 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 VIDEOOMX_H
+#define VIDEOOMX_H
+
+#include "mutex.h"
+
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <string.h>
+
+#include <stdint.h>
+
+#include <list>
+#include <vector>
+
+#include "defines.h"
+#include "video.h"
+#include "threadsystem.h"
+
+#define OMX_SKIP64BIT
+
+#include <IL/OMX_Core.h>
+#include <IL/OMX_Types.h>
+#include <IL/OMX_Component.h>
+#include <IL/OMX_Broadcom.h>
+
+
+struct VPE_OMX_EVENT {
+ OMX_IN OMX_HANDLETYPE handle;
+ OMX_IN OMX_PTR appdata;
+ OMX_IN OMX_EVENTTYPE event_type;
+ OMX_IN OMX_U32 data1;
+ OMX_IN OMX_U32 data2;
+ OMX_IN OMX_PTR event_data;
+};
+
+
+
+
+class AudioVPE;
+
+class VideoOMX : public Video
+{
+ friend class AudioOMX;
+ public:
+ VideoOMX();
+ virtual ~VideoOMX();
+
+ int init(UCHAR format);
+ int shutdown();
+
+ UCHAR getSupportedFormats() { return COMPOSITERGB | HDMI;};
+ UINT supportedTVsize() { return ASPECT4X3|ASPECT16X9|ASPECT14X9 ;};
+ UCHAR supportedTVFormats() { return NTSC|PAL|PAL_M|NTSC_J;};
+
+ 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?
+
+ void executePendingModeChanges();
+ 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* bulibaver, UINT length);
+
+ virtual bool dtsTimefix(){return false;} //please we need dts time values
+ virtual int getTeletextBufferFaktor(){return 5;};
+
+ // Writing Data to Videodevice
+ virtual void PrepareMediaSample(const MediaPacketList&, UINT samplepos);
+ virtual UINT DeliverMediaSample(UCHAR* bulibaver, UINT* samplepos);
+
+
+ virtual bool supportsh264(){return true;};
+
+ int WriteOutTS(const unsigned char *bulibaver,int length, int type);
+ void WriteOutPATPMT();
+
+
+
+ virtual long long SetStartOffset(long long curreftime, bool *rsync);
+ long long SetStartAudioOffset(long long curreftime, bool *rsync);
+ virtual void ResetTimeOffsets();
+
+ bool loadOptionsfromServer(VDR* vdr);
+ bool saveOptionstoServer();
+ bool addOptionsToPanes(int panenumber,Options *options,WOptionPane* pane);
+ bool handleOptionChanges(Option* option);
+
+
+#ifdef DEV
+ int test();
+ int test2();
+#endif
+
+
+
+ static inline OMX_TICKS intToOMXTicks(long long pts) {
+ OMX_TICKS temp;
+ temp.nLowPart=pts;
+ temp.nHighPart=pts>>32;
+ return temp;
+ }
+
+
+
+ private:
+ int EnterIframePlayback();
+ bool iframemode;
+ bool InIframemode() {return iframemode;};
+
+ UINT DeliverMediaPacket(MediaPacket packet,const UCHAR* bulibaver,UINT *samplepos);
+
+#define VPE_DECODER_OMX 1
+
+
+
+ bool offsetnotset;
+ bool offsetvideonotset;
+ bool offsetaudionotset;
+ long long startoffset;
+ long long lastrefvideotime;
+ long long lastrefaudiotime;
// long long cur_pts;
- long long lastreftimeOMX;\r
- ULLONG lastreftimePTS;\r
-\r
- // long long playbacktimeoffset; //this is the offset between the media time and system clock\r
- // long long pausetimecode;\r
- bool paused;\r
-\r
- /* static long long GetCurrentSystemTime();\r
- static void WaitUntil(long long time);\r
- void FrameWaitforDisplay(long long pts);\r
- bool FrameSkip(long long pts);\r
- bool skipping;\r
- void AdjustAudioPTS(long long pts);*/\r
-\r
-\r
-\r
-\r
- static OMX_ERRORTYPE EventHandler_OMX(OMX_IN OMX_HANDLETYPE handle,OMX_IN OMX_PTR appdata,\r
- OMX_IN OMX_EVENTTYPE event_type,OMX_IN OMX_U32 data1,\r
- OMX_IN OMX_U32 data2,OMX_IN OMX_PTR event_data);\r
- static OMX_ERRORTYPE EmptyBufferDone_OMX(OMX_IN OMX_HANDLETYPE hcomp,OMX_IN OMX_PTR appdata,OMX_IN OMX_BUFFERHEADERTYPE* bulibaver);\r
- static OMX_ERRORTYPE FillBufferDone_OMX(OMX_IN OMX_HANDLETYPE hcomp, OMX_IN OMX_PTR appdata,OMX_IN OMX_BUFFERHEADERTYPE* bulibaver);\r
-\r
- UINT DeliverMediaPacketOMX(MediaPacket packet,\r
- const UCHAR* bulibaver,\r
- UINT *samplepos);\r
-\r
- bool detectIFrame(const UCHAR *buffer,unsigned int length);\r
-\r
- int PrepareInputBufsOMX();\r
- int DestroyInputBufsOMX();\r
-\r
- void AddOmxEvent(VPE_OMX_EVENT new_event);\r
- void ReturnEmptyOMXBuffer(OMX_BUFFERHEADERTYPE* bulibaver);\r
-\r
- int ChangeComponentState(OMX_HANDLETYPE handle,OMX_STATETYPE type);\r
- int CommandFinished(OMX_HANDLETYPE handle,OMX_U32 command,OMX_U32 data2);\r
- int WaitForEvent(OMX_HANDLETYPE handle,OMX_U32 event);\r
- int EnablePort(OMX_HANDLETYPE handle,OMX_U32 port,bool wait);\r
- int DisablePort(OMX_HANDLETYPE handle,OMX_U32 port,bool wait=true);\r
- int clearEvents();\r
-\r
-\r
- int setClockExecutingandRunning();\r
- int initClock();\r
- void destroyClock();\r
- int idleClock();\r
- int getClockAudioandInit(OMX_HANDLETYPE *p_omx_clock,OMX_U32 *p_omx_clock_output_port);\r
- int getClockVideoandInit();\r
- void LockClock() {clock_mutex.Lock();};\r
- void UnlockClock() {clock_mutex.Unlock();};\r
- OMX_ERRORTYPE ProtOMXEmptyThisBuffer(OMX_HANDLETYPE handle, OMX_BUFFERHEADERTYPE* buffer);\r
- void clockPause();\r
- void clockUnpause();\r
-\r
- void interlaceSwitch4Demux();\r
-\r
- Mutex clock_mutex; //clock mutex is now responsible for all omx stuff\r
- long long cur_clock_time;\r
-\r
-\r
-\r
- OMX_HANDLETYPE omx_vid_dec;\r
- OMX_HANDLETYPE omx_vid_sched;\r
- OMX_HANDLETYPE omx_vid_deint;\r
- OMX_HANDLETYPE omx_vid_rend;\r
- OMX_HANDLETYPE omx_clock;\r
- int clock_references;\r
- bool dodeint; //deinterlacer was activated in omx filtergraph\r
- bool deint_first_frame; //handle frame change\r
- void DeinterlaceFix();\r
-\r
-\r
- OMX_U32 omx_codec_input_port;\r
- OMX_U32 omx_codec_output_port;\r
- OMX_U32 omx_deint_input_port;\r
- OMX_U32 omx_deint_output_port;\r
- OMX_U32 omx_rend_input_port;\r
- OMX_U32 omx_shed_input_port;\r
- OMX_U32 omx_shed_output_port;\r
- OMX_U32 omx_shed_clock_port;\r
- OMX_U32 omx_clock_output_port;\r
- // OMX_NALUFORMATSTYPE omx_nalu_format;\r
-\r
-\r
-\r
- int AllocateCodecsOMX();\r
- int DeAllocateCodecsOMX();\r
- int FlushRenderingPipe();\r
-\r
- vector<OMX_BUFFERHEADERTYPE*> input_bufs_omx_all;\r
- list<OMX_BUFFERHEADERTYPE*> input_bufs_omx_free;\r
- Mutex input_bufs_omx_mutex;\r
- OMX_BUFFERHEADERTYPE* cur_input_buf_omx;\r
-\r
- void PutBufferToPres(OMX_BUFFERHEADERTYPE* buffer);\r
-\r
-\r
-\r
- bool omx_running;\r
- bool omx_first_frame;\r
-\r
- Mutex omx_event_mutex;\r
-\r
- list<VPE_OMX_EVENT> omx_events;\r
-\r
- bool omx_mpeg2;\r
- bool omx_h264;\r
- float xpos,ypos;\r
- int deinterlace;\r
- void updateMode();//called internally to adjust for different parameters\r
- void selectVideoMode(int interlaced);\r
- UCHAR tvsystem;\r
- bool signalon;\r
- bool pendingmodechange;\r
- bool hdmi;\r
- int outputinterlaced;\r
-\r
-\r
-\r
- bool firstsynched;\r
-\r
- \r
- vector<MediaPacket> mediapackets;\r
-};\r
-\r
-#endif\r
+ long long lastreftimeOMX;
+ ULLONG lastreftimePTS;
+
+ // long long playbacktimeoffset; //this is the offset between the media time and system clock
+ // long long pausetimecode;
+ bool paused;
+
+ /* static long long GetCurrentSystemTime();
+ static void WaitUntil(long long time);
+ void FrameWaitforDisplay(long long pts);
+ bool FrameSkip(long long pts);
+ bool skipping;
+ void AdjustAudioPTS(long long pts);*/
+
+
+
+
+ static OMX_ERRORTYPE EventHandler_OMX(OMX_IN OMX_HANDLETYPE handle,OMX_IN OMX_PTR appdata,
+ OMX_IN OMX_EVENTTYPE event_type,OMX_IN OMX_U32 data1,
+ OMX_IN OMX_U32 data2,OMX_IN OMX_PTR event_data);
+ static OMX_ERRORTYPE EmptyBufferDone_OMX(OMX_IN OMX_HANDLETYPE hcomp,OMX_IN OMX_PTR appdata,OMX_IN OMX_BUFFERHEADERTYPE* bulibaver);
+ static OMX_ERRORTYPE FillBufferDone_OMX(OMX_IN OMX_HANDLETYPE hcomp, OMX_IN OMX_PTR appdata,OMX_IN OMX_BUFFERHEADERTYPE* bulibaver);
+
+ UINT DeliverMediaPacketOMX(MediaPacket packet,
+ const UCHAR* bulibaver,
+ UINT *samplepos);
+
+ bool detectIFrame(const UCHAR *buffer,unsigned int length);
+
+ int PrepareInputBufsOMX();
+ int DestroyInputBufsOMX();
+
+ void AddOmxEvent(VPE_OMX_EVENT new_event);
+ void ReturnEmptyOMXBuffer(OMX_BUFFERHEADERTYPE* bulibaver);
+
+ int ChangeComponentState(OMX_HANDLETYPE handle,OMX_STATETYPE type);
+ int CommandFinished(OMX_HANDLETYPE handle,OMX_U32 command,OMX_U32 data2);
+ int WaitForEvent(OMX_HANDLETYPE handle,OMX_U32 event);
+ int EnablePort(OMX_HANDLETYPE handle,OMX_U32 port,bool wait);
+ int DisablePort(OMX_HANDLETYPE handle,OMX_U32 port,bool wait=true);
+ int clearEvents();
+
+
+ int setClockExecutingandRunning();
+ int initClock();
+ void destroyClock();
+ int idleClock();
+ int getClockAudioandInit(OMX_HANDLETYPE *p_omx_clock,OMX_U32 *p_omx_clock_output_port);
+ int getClockVideoandInit();
+ void LockClock() {clock_mutex.Lock();};
+ void UnlockClock() {clock_mutex.Unlock();};
+ OMX_ERRORTYPE ProtOMXEmptyThisBuffer(OMX_HANDLETYPE handle, OMX_BUFFERHEADERTYPE* buffer);
+ void clockPause();
+ void clockUnpause();
+
+ void interlaceSwitch4Demux();
+
+ Mutex clock_mutex; //clock mutex is now responsible for all omx stuff
+ long long cur_clock_time;
+
+
+
+ OMX_HANDLETYPE omx_vid_dec;
+ OMX_HANDLETYPE omx_vid_sched;
+ OMX_HANDLETYPE omx_vid_deint;
+ OMX_HANDLETYPE omx_vid_rend;
+ OMX_HANDLETYPE omx_clock;
+ int clock_references;
+ bool dodeint; //deinterlacer was activated in omx filtergraph
+ bool deint_first_frame; //handle frame change
+ void DeinterlaceFix();
+
+
+ OMX_U32 omx_codec_input_port;
+ OMX_U32 omx_codec_output_port;
+ OMX_U32 omx_deint_input_port;
+ OMX_U32 omx_deint_output_port;
+ OMX_U32 omx_rend_input_port;
+ OMX_U32 omx_shed_input_port;
+ OMX_U32 omx_shed_output_port;
+ OMX_U32 omx_shed_clock_port;
+ OMX_U32 omx_clock_output_port;
+ // OMX_NALUFORMATSTYPE omx_nalu_format;
+
+
+
+ int AllocateCodecsOMX();
+ int DeAllocateCodecsOMX();
+ int FlushRenderingPipe();
+
+ vector<OMX_BUFFERHEADERTYPE*> input_bufs_omx_all;
+ list<OMX_BUFFERHEADERTYPE*> input_bufs_omx_free;
+ Mutex input_bufs_omx_mutex;
+ OMX_BUFFERHEADERTYPE* cur_input_buf_omx;
+
+ void PutBufferToPres(OMX_BUFFERHEADERTYPE* buffer);
+
+
+
+ bool omx_running;
+ bool omx_first_frame;
+
+ Mutex omx_event_mutex;
+
+ list<VPE_OMX_EVENT> omx_events;
+
+ bool omx_mpeg2;
+ bool omx_h264;
+ float xpos,ypos;
+ int deinterlace;
+ void updateMode();//called internally to adjust for different parameters
+ void selectVideoMode(int interlaced);
+ UCHAR tvsystem;
+ bool signalon;
+ bool pendingmodechange;
+ bool hdmi;
+ int outputinterlaced;
+
+
+
+ bool firstsynched;
+
+
+ vector<MediaPacket> mediapackets;
+};
+
+#endif
-/*\r
- Copyright 2004-2005 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-\r
-\r
-#include "videowin.h"\r
-#include "log.h"\r
-#include "dssourcefilter.h"\r
-#include "dsallocator.h"\r
-#include "vdr.h"\r
-#include "osdwin.h"\r
-#include "audiowin.h"\r
-#include "wwinvideofilter.h"\r
-#include "wwinvideoh264filter.h"\r
-#include "wtabbar.h"\r
-#include "woptionpane.h"\r
-#include "i18n.h"\r
-#include "demuxer.h"\r
-\r
-#include <Mfapi.h>\r
-#include <mferror.h>\r
-\r
-void AdjustWindow();\r
-\r
-\r
-\r
-VideoWin::VideoWin()\r
-{\r
- dsinited=false;\r
- dsgraphbuilder=NULL;\r
- dsmediacontrol=NULL;\r
- dsrenderer=NULL;\r
- dsrefclock=NULL;\r
- dsmediafilter=NULL;\r
- dsbasicaudio=NULL;\r
- sourcefilter=NULL;\r
- allocatorvmr=NULL;\r
- cr_time=0;\r
- lastaudiomode=MPTYPE_MPEG_AUDIO;\r
- //lastaudiomode=MPTYPE_AC3;\r
- dsvmrsurfnotify=NULL;\r
- filtermutex=CreateMutex(NULL,FALSE,NULL);\r
- offsetnotset=true;\r
- offsetvideonotset=true;\r
- offsetaudionotset=true;\r
- startoffset=0;\r
- lastrefaudiotime=0;\r
- lastrefvideotime=0;\r
- lastreftimeRT=0;\r
- lastreftimePTS=0;\r
- firstsynched=false;\r
- cur_audio_media_sample=NULL;\r
- cur_video_media_sample=NULL;\r
- videoon=true;\r
- audioon=true;\r
- audiovolume=0;\r
- pseudotvsize=0;\r
- videoposx=0;\r
- videoposy=0;\r
- aud_type=Audio::MPEG2_PES;\r
- iframemode=false;//We are not in Iframe mode at begining\r
- vmrdeinterlacing=2;//Best\r
- videofilterselected=-1;\r
- videoH264filterselected=-1;\r
- OSVERSIONINFO verinfo;\r
- verinfo.dwOSVersionInfoSize=sizeof(verinfo);\r
- GetVersionEx(&verinfo);\r
-\r
- if (verinfo.dwMajorVersion>=6) {\r
- currentpresenter=EVR;\r
- } else {\r
- currentpresenter=VMR9;\r
- }\r
- videoH264dtsfix=false;\r
- videompeg2dtsfix=false;\r
-\r
-\r
-\r
-\r
-}\r
-\r
-VideoWin::~VideoWin()\r
-{\r
- CleanupDS();\r
- CloseHandle(filtermutex);\r
- unsigned int i;\r
- for (i=0;i<videofilterlist.size();i++)\r
- {\r
- if (videofilterlist[i].displayname) delete [] videofilterlist[i].displayname;\r
- if (videofilterlist[i].friendlyname) delete [] videofilterlist[i].friendlyname;\r
- }\r
- videofilterlist.clear();\r
-\r
- for (i=0;i<videoH264filterlist.size();i++)\r
- {\r
- if (videoH264filterlist[i].displayname) delete [] videoH264filterlist[i].displayname;\r
- if (videoH264filterlist[i].friendlyname) delete [] videoH264filterlist[i].friendlyname;\r
- }\r
- videoH264filterlist.clear();\r
-\r
-\r
-\r
-\r
- instance = NULL;\r
-}\r
-\r
-int VideoWin::init(UCHAR tformat)\r
-{\r
- if (initted) return 0;\r
-\r
- initted = 1;\r
- tvsize=Video::ASPECT16X9; //Internally Vomp should think we are a 16:9 TV\r
- videoposx=0;\r
- videoposy=0;\r
- initFilterDatabase();\r
- initH264FilterDatabase();\r
-\r
- if (!setFormat(tformat)){ shutdown(); return 0; }\r
- return 1;\r
-}\r
-\r
-\r
-\r
-int VideoWin::setTVsize(UCHAR ttvsize)\r
-{\r
- pseudotvsize=ttvsize;\r
- return 1;\r
-}\r
-\r
-int VideoWin::setDefaultAspect()\r
-{\r
- return setAspectRatio(Video::ASPECT4X3);\r
-}\r
-\r
-int VideoWin::shutdown()\r
-{\r
- if (!initted) return 0;\r
- initted = 0;\r
- return 1;\r
-}\r
-\r
-int VideoWin::setFormat(UCHAR tformat)\r
-{\r
- if (!initted) return 0;\r
- if ((tformat != PAL) && (tformat != NTSC)) return 0;\r
- format = tformat;\r
- if (format == NTSC)\r
- {\r
- screenWidth = 720;\r
- screenHeight = 480;\r
- }\r
- if (format == PAL)\r
- {\r
- screenWidth = 720;\r
- screenHeight = 576;\r
- }\r
-\r
- return 1;\r
-}\r
-\r
-int VideoWin::setConnection(UCHAR tconnection)\r
-{\r
- if (!initted) return 0;\r
- if ((tconnection != COMPOSITERGB) && (tconnection != SVIDEO)) return 0;\r
- connection = tconnection;\r
-\r
- return 1;\r
-}\r
-\r
-int VideoWin::setAspectRatio(UCHAR taspectRatio)\r
-{\r
- if (!initted) return 0;\r
- if ((taspectRatio != ASPECT4X3) && (taspectRatio != ASPECT16X9)) return 0;\r
- aspectRatio = taspectRatio;\r
- AdjustWindow();\r
- return 1;\r
-}\r
-\r
-int VideoWin::setMode(UCHAR tmode)\r
-{\r
- if (!initted) return 0;\r
-\r
- //if ((tmode == LETTERBOX) && (tvsize == ASPECT16X9)) return 0; // invalid mode\r
-\r
- if ((tmode != NORMAL) && (tmode != LETTERBOX) && (tmode != UNKNOWN2) && (tmode != QUARTER) && (tmode != EIGHTH)\r
- && (tmode != ZOOM) && (tmode != UNKNOWN6)) return 0;\r
- mode = tmode;\r
- videoposx=0;\r
- videoposy=0;\r
- AdjustWindow();\r
-\r
- return 1;\r
-}\r
-\r
-int VideoWin::signalOff()\r
-{\r
- return 1;\r
-}\r
-\r
-int VideoWin::signalOn()\r
-{\r
- return 1;\r
-}\r
-\r
-int VideoWin::setSource()\r
-{\r
- if (!initted) return 0;\r
-\r
- return 1;\r
-}\r
-\r
-int VideoWin::setPosition(int x, int y)\r
-{\r
- if (!initted) return 0;\r
- if (mode==QUARTER || mode==EIGHTH) {\r
- videoposx=x;\r
- videoposy=y;\r
- }\r
- return 1;\r
-}\r
-\r
-int VideoWin::sync()\r
-{\r
- if (!initted) return 0;\r
-\r
- return 1;\r
-}\r
-void VideoWin::initFilterDatabase()\r
-{\r
- /* This method should determine all availiable DirectShow Filters */\r
- IFilterMapper2* filtmap=NULL;\r
- HRESULT result;\r
- result = CoCreateInstance(CLSID_FilterMapper2,NULL,CLSCTX_INPROC,\r
- IID_IFilterMapper2,(void**)&filtmap);\r
- if (result != S_OK)\r
- {\r
- Log::getInstance()->log("VideoWin", Log::ERR , "Unable to create FilterMapper!");\r
- return;\r
- }\r
- /* Wishlist, what Mediatypes do we want */\r
- GUID mtypesin[]={MEDIATYPE_Video,MEDIASUBTYPE_MPEG2_VIDEO};\r
- IEnumMoniker *myenum;\r
- result = filtmap->EnumMatchingFilters(&myenum,0,TRUE,MERIT_DO_NOT_USE+1,\r
- TRUE,1,mtypesin,NULL,NULL,FALSE,TRUE,0,NULL,NULL,NULL);\r
- if (result != S_OK)\r
- {\r
- filtmap->Release();\r
- Log::getInstance()->log("VideoWin", Log::ERR , "Unable to enum Filters!");\r
- return;\r
- }\r
- ULONG gethowmany;\r
- IMoniker * moni;\r
- while(myenum->Next(1,&moni,&gethowmany)==S_OK)\r
- {\r
- VideoFilterDesc desc;\r
- ZeroMemory(&desc,sizeof(desc));\r
- \r
- LPOLESTR string;\r
- moni->GetDisplayName(0,0,&string);\r
- desc.displayname=new char[wcslen(string)+1];\r
- wcstombs(desc.displayname,string,wcslen(string)+1);\r
- CoTaskMemFree(string);\r
- IPropertyBag *bag;\r
- if (moni->BindToStorage(0,0,IID_IPropertyBag,(void**)&bag) == S_OK)\r
- {\r
- VARIANT vari;\r
- VariantInit(&vari);\r
- result = bag->Read(L"FriendlyName",&vari,NULL);\r
- if (result == S_OK)\r
- {\r
- desc.friendlyname=new char[wcslen(vari.bstrVal)+1];\r
- wcstombs(desc.friendlyname,vari.bstrVal,wcslen(vari.bstrVal)+1);\r
- }\r
- VariantClear(&vari);\r
- bag->Release();\r
-\r
- }\r
- \r
- \r
- videofilterlist.push_back(desc);\r
- \r
-\r
- \r
- moni->Release();\r
- // bctx->Release();\r
- }\r
- int i;\r
- videofilterselected=-1;\r
- \r
- \r
- \r
- myenum->Release();\r
-\r
-\r
-\r
- filtmap->Release();\r
-}\r
-\r
-void VideoWin::initH264FilterDatabase()\r
-{\r
- /* This method should determine all availiable DirectShow Filters */\r
- IFilterMapper2* filtmap=NULL;\r
- HRESULT result;\r
- result = CoCreateInstance(CLSID_FilterMapper2,NULL,CLSCTX_INPROC,\r
- IID_IFilterMapper2,(void**)&filtmap);\r
- if (result != S_OK)\r
- {\r
- Log::getInstance()->log("VideoWin", Log::ERR , "Unable to create FilterMapper!");\r
- return;\r
- }\r
- /* Wishlist, what Mediatypes do we want */\r
- GUID mtypesin[]={MEDIATYPE_Video,MEDIASUBTYPE_H264};\r
- IEnumMoniker *myenum;\r
- result = filtmap->EnumMatchingFilters(&myenum,0,TRUE,MERIT_DO_NOT_USE+1,\r
- TRUE,1,mtypesin,NULL,NULL,FALSE,TRUE,0,NULL,NULL,NULL);\r
- if (result != S_OK)\r
- {\r
- filtmap->Release();\r
- Log::getInstance()->log("VideoWin", Log::ERR , "Unable to enum Filters!");\r
- return;\r
- }\r
- ULONG gethowmany;\r
- IMoniker * moni;\r
- while(myenum->Next(1,&moni,&gethowmany)==S_OK)\r
- {\r
- VideoFilterDesc desc;\r
- ZeroMemory(&desc,sizeof(desc));\r
- \r
- LPOLESTR string;\r
- moni->GetDisplayName(0,0,&string);\r
- desc.displayname=new char[wcslen(string)+1];\r
- wcstombs(desc.displayname,string,wcslen(string)+1);\r
- CoTaskMemFree(string);\r
- IPropertyBag *bag;\r
- if (moni->BindToStorage(0,0,IID_IPropertyBag,(void**)&bag) == S_OK)\r
- {\r
- VARIANT vari;\r
- VariantInit(&vari);\r
- result = bag->Read(L"FriendlyName",&vari,NULL);\r
- if (result == S_OK)\r
- {\r
- desc.friendlyname=new char[wcslen(vari.bstrVal)+1];\r
- wcstombs(desc.friendlyname,vari.bstrVal,wcslen(vari.bstrVal)+1);\r
- }\r
- VariantClear(&vari);\r
- bag->Release();\r
-\r
- }\r
- \r
- \r
- videoH264filterlist.push_back(desc);\r
- \r
-\r
- \r
- moni->Release();\r
- // bctx->Release();\r
- }\r
- int i;\r
- videoH264filterselected=-1;\r
- \r
- \r
- \r
- myenum->Release();\r
-\r
-\r
-\r
- filtmap->Release();\r
-}\r
-\r
-bool VideoWin::loadOptionsfromServer(VDR* vdr)\r
-{\r
- char *name=vdr->configLoad("DirectShow","VideoFilter");\r
- \r
- if (name != NULL) \r
- {\r
- for (int i = 0;i <videofilterlist.size();i++)\r
- {\r
- if (strcmp(name,videofilterlist[i].displayname)==0)\r
- {\r
- videofilterselected = i;\r
- break;\r
- }\r
- }\r
- }\r
- name=vdr->configLoad("DirectShow","VideoH264Filter");\r
- \r
- if (name != NULL) \r
- {\r
- for (int i = 0;i <videoH264filterlist.size();i++)\r
- {\r
- if (strcmp(name,videoH264filterlist[i].displayname)==0)\r
- {\r
- videoH264filterselected = i;\r
- break;\r
- }\r
- }\r
- }\r
- name=vdr->configLoad("DirectShow","VMR9DeinterlacingMode");\r
- if (name != NULL) \r
- {\r
- if (STRCASECMP(name,"NoMix")==0) {\r
- vmrdeinterlacing=0;\r
- } else if (STRCASECMP(name,"None")==0) {\r
- vmrdeinterlacing=1;\r
- } else if (STRCASECMP(name,"Best")==0) {\r
- vmrdeinterlacing=2;\r
- } else if (STRCASECMP(name,"Bob")==0) {\r
- vmrdeinterlacing=3;\r
- } else if (STRCASECMP(name,"Weave")==0) {\r
- vmrdeinterlacing=4;\r
- }\r
- }\r
-\r
- name=vdr->configLoad("DirectShow", "VideoPresenter");\r
- if (name!=NULL) {\r
- if (STRCASECMP(name,"VMR9")==0) {\r
- currentpresenter=VMR9;\r
- } else if (STRCASECMP(name,"EVR")==0) {\r
- currentpresenter=EVR;\r
- } \r
- }\r
- if (!((OsdWin*)Osd::getInstance())->IsEvrSupported()) {\r
- currentpresenter=VMR9;\r
- }\r
-\r
- name=vdr->configLoad("DirectShow","videoH264dtsfix");\r
- if (name!=NULL) {\r
- if (STRCASECMP(name,"YES")==0) {\r
- videoH264dtsfix=true;\r
- } else {\r
- videoH264dtsfix=false;\r
- }\r
- }\r
- name=vdr->configLoad("DirectShow","videompeg2dtsfix");\r
- if (name!=NULL) {\r
- if (STRCASECMP(name,"YES")==0) {\r
- videompeg2dtsfix=true;\r
- } else {\r
- videompeg2dtsfix=false;\r
- }\r
- }\r
-\r
- name=vdr->configLoad("DirectGraphics", "StretchFiltering");\r
- if (name!=NULL) {\r
- if (STRCASECMP(name,"None")==0) {\r
- ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_NONE);\r
- } else if (STRCASECMP(name,"Point")==0) {\r
- ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_POINT);\r
- } else if (STRCASECMP(name,"Linear")==0) {\r
- ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_LINEAR);\r
- }\r
- }\r
-\r
- \r
-\r
-\r
- return true;\r
-\r
-}\r
-\r
-bool VideoWin::handleOptionChanges(Option* option)\r
-{\r
- if( Video::handleOptionChanges(option)) return true;\r
- switch(option->id) {\r
- case 1: {\r
- if (STRCASECMP(option->options[option->userSetChoice],"None")==0) {\r
- ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_NONE);\r
- } else if (STRCASECMP(option->options[option->userSetChoice],"Point")==0) {\r
- ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_POINT);\r
- } else if (STRCASECMP(option->options[option->userSetChoice],"Linear")==0) {\r
- ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_LINEAR);\r
- }\r
- return true;\r
- } break;\r
- case 2: {\r
- if (STRCASECMP(option->options[option->userSetChoice],"NoMix")==0) {\r
- vmrdeinterlacing=0;\r
- } else if (STRCASECMP(option->options[option->userSetChoice],"None")==0) {\r
- vmrdeinterlacing=1;\r
- } else if (STRCASECMP(option->options[option->userSetChoice],"Best")==0) {\r
- vmrdeinterlacing=2;\r
- } else if (STRCASECMP(option->options[option->userSetChoice],"Bob")==0) {\r
- vmrdeinterlacing=3;\r
- } else if (STRCASECMP(option->options[option->userSetChoice],"Weave")==0) {\r
- vmrdeinterlacing=4;\r
- } \r
- }break;\r
- case 3: {\r
- if (STRCASECMP(option->options[option->userSetChoice],"VMR9")==0) {\r
- currentpresenter=VMR9;\r
- } else if (STRCASECMP(option->options[option->userSetChoice],"EVR")==0) {\r
- currentpresenter=EVR;\r
- } \r
- }break;\r
- case 4: {\r
- if (STRCASECMP(option->options[option->userSetChoice],"Yes")==0) {\r
- videoH264dtsfix=true;\r
- } else {\r
- videoH264dtsfix=false;\r
- }\r
- }break;\r
- case 5: {\r
- if (STRCASECMP(option->options[option->userSetChoice],"Yes")==0) {\r
- videompeg2dtsfix=true;\r
- } else {\r
- videompeg2dtsfix=false;\r
- }\r
- }break;\r
- };\r
- return false;\r
-\r
-}\r
-\r
-bool VideoWin::saveOptionstoServer()\r
-{\r
- if (videofilterselected!=-1) {\r
- VDR::getInstance()->configSave("DirectShow",\r
- "VideoFilter",videofilterlist[videofilterselected].displayname);\r
- VDR::getInstance()->configSave("DirectShow",\r
- "VideoH264Filter",videoH264filterlist[videoH264filterselected].displayname);\r
- }\r
- return true;\r
-}\r
-\r
-/*Option(UINT id, const char* displayText, const char* configSection, const char* configKey, UINT optionType, \r
- UINT numChoices, UINT defaultChoice, UINT startInt,\r
- const char * const * options, const char * const * optionkeys = NULL, AbstractOption* handler=NULL);*/\r
-\r
-bool VideoWin::addOptionsToPanes(int panenumber,Options *options,WOptionPane* pane)\r
-{\r
- if (!Video::addOptionsToPanes(panenumber,options,pane)) return false;\r
-\r
-\r
- Option* option;\r
- if (panenumber == 2) \r
- {\r
- DWORD scalingcaps=((OsdWin*)Osd::getInstance())->getFilterCaps();\r
- char **scalingopts=new char *[3];\r
- int i=0;\r
- scalingopts[i]=new char[strlen("None")+1];\r
- strcpy(scalingopts[i],"None");\r
- i++;\r
- if ((scalingcaps & D3DPTFILTERCAPS_MINFPOINT)!=0 \r
- && (scalingcaps & D3DPTFILTERCAPS_MAGFPOINT)!=0) {\r
- scalingopts[i]=new char[strlen("Point")+1];\r
- strcpy(scalingopts[i],"Point");\r
- i++;\r
- }\r
- if ((scalingcaps & D3DPTFILTERCAPS_MINFLINEAR)!=0 \r
- && (scalingcaps & D3DPTFILTERCAPS_MAGFLINEAR)!=0) {\r
- scalingopts[i]=new char[strlen("Linear")+1];\r
- strcpy(scalingopts[i],"Linear");\r
- i++;\r
- }\r
- option = new Option(1 ,tr("Video Stretching Filter"), "DirectGraphics", "StretchFiltering", Option::TYPE_TEXT, i, (i-1), 0, scalingopts,NULL,true, this);\r
- options->push_back(option);\r
- pane->addOptionLine(option);\r
- static const char* deintopts[]={"NoMix","None","Best","Bob","Weave"};\r
- option = new Option(2,tr("VMR9 Deinterlacing Mode"), "DirectShow","VMR9DeinterlacingMode",Option::TYPE_TEXT,5,2,0,deintopts,NULL,false,this);\r
- options->push_back(option);\r
- pane->addOptionLine(option);\r
-\r
- if (((OsdWin*)Osd::getInstance())->IsEvrSupported()) \r
- {\r
- static const char* presenteropts[]={"EVR","VMR9"};\r
- option = new Option(3,tr("Video Presenter Filter"),"DirectShow", "VideoPresenter",Option::TYPE_TEXT,2,\r
- (currentpresenter==EVR)?0:1,0,presenteropts,NULL,false,this);\r
- } else {\r
- static const char* presenteropts[]={"VMR9"};\r
- option = new Option(3,tr("Video Presenter Filter"),"DirectShow", "VideoPresenter",Option::TYPE_TEXT,1,0,0,presenteropts,NULL,false,this);\r
- }\r
- options->push_back(option);\r
- pane->addOptionLine(option);\r
-\r
- static const char* yesnoopts[]={"Yes","No"};\r
- option = new Option(4,tr("Video H264 fix dts time"), "DirectShow","videoH264dtsfix",Option::TYPE_TEXT,2,1,0,yesnoopts,NULL,false,this);\r
- options->push_back(option);\r
- pane->addOptionLine(option);\r
-\r
- option = new Option(5,tr("Video Mpeg2 fix dts time"), "DirectShow","videompeg2dtsfix",Option::TYPE_TEXT,2,1,0,yesnoopts,NULL,false,this);\r
- options->push_back(option);\r
- pane->addOptionLine(option);\r
-\r
-\r
- \r
- }\r
-\r
- return true;\r
-}\r
-\r
-IBaseFilter *VideoWin::getVideoFilter()\r
-{\r
- IBaseFilter *curfilter= NULL;\r
- if (videofilterselected == -1)\r
- {\r
- int i;\r
- for (i = 0;i <videofilterlist.size();i++)\r
- {\r
- \r
- if (videofilterlist[i].vmr9tested == true)\r
- {\r
- if (videofilterlist[i].vmr9 == true)\r
- {\r
- videofilterselected = i;\r
- break;\r
- } \r
- else\r
- {\r
- continue;\r
- }\r
- }\r
- else\r
- {\r
- IMoniker * moni=NULL;\r
- IBindCtx *bindctx=NULL;\r
- if (CreateBindCtx(0,&bindctx)!=S_OK) return NULL;\r
- LPCOLESTR name=(LPCOLESTR)new WCHAR[strlen(videofilterlist[i].displayname)+1];\r
- mbstowcs((wchar_t*)name,videofilterlist[i].displayname,strlen(videofilterlist[i].displayname)+1);\r
- ULONG eater=0;\r
- \r
-\r
- if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK)\r
- {\r
- if (moni->BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK)\r
- {\r
- IAMDecoderCaps* desccaps=NULL;\r
- if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK)\r
- {\r
- DWORD caps;\r
- desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps);\r
- if (caps == DECODER_CAP_SUPPORTED)\r
- {\r
- videofilterlist[i].vmr9tested = true;\r
- videofilterlist[i].vmr9 = true;\r
- videofilterselected = i;\r
- } \r
- else\r
- {\r
- videofilterlist[i].vmr9tested = true;\r
- videofilterlist[i].vmr9 = false;\r
- \r
- curfilter->Release();\r
- curfilter=NULL;\r
- }\r
- }\r
- desccaps->Release();\r
- }\r
- moni->Release();\r
- } \r
- delete [] name;\r
- bindctx->Release();\r
- }\r
- if (videofilterlist[i].vmr9) break;\r
- \r
- }\r
- if (curfilter != NULL)\r
- {\r
- VDR *vdr=VDR::getInstance();\r
- if (vdr != NULL)\r
- {\r
- vdr->configSave("DirectShow","VideoFilter",\r
- videofilterlist[videofilterselected].displayname);\r
- }\r
- return curfilter;\r
- }\r
- } \r
- else\r
- {\r
- IBindCtx *bindctx=NULL;\r
- if (CreateBindCtx(0,&bindctx)!=S_OK) return NULL;\r
- IMoniker * moni=NULL;\r
- LPCOLESTR name=new WCHAR[strlen(videofilterlist[videofilterselected].displayname)+1];\r
- mbstowcs((wchar_t*)name,videofilterlist[videofilterselected].displayname,\r
- strlen(videofilterlist[videofilterselected].displayname)+1);\r
- ULONG eater;\r
- if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK)\r
- {\r
- if (moni->BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK)\r
- {\r
- IAMDecoderCaps* desccaps=NULL;\r
- if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK)\r
- {\r
- DWORD caps;\r
- desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps);\r
- if (caps == DECODER_CAP_SUPPORTED)\r
- {\r
- videofilterlist[videofilterselected].vmr9tested = true;\r
- videofilterlist[videofilterselected].vmr9 = true;\r
- } \r
- else\r
- {\r
- videofilterlist[videofilterselected].vmr9tested = true;\r
- videofilterlist[videofilterselected].vmr9 = false;\r
- Log::getInstance()->log("VideoWin", Log::WARN ,"Filter does not support VMR9, but is selected, manual selection!");\r
- }\r
- }\r
- moni->Release();\r
- delete [] name;\r
- bindctx->Release();\r
- return curfilter;\r
- } \r
- moni->Release();\r
- }\r
- bindctx->Release();\r
- delete [] name;\r
- return NULL; \r
- }\r
- return NULL;\r
- \r
-}\r
-\r
-IBaseFilter *VideoWin::getVideoH264Filter()\r
-{\r
- IBaseFilter *curfilter= NULL;\r
- if (videoH264filterselected == -1)\r
- {\r
- int i;\r
- for (i = 0;i <videoH264filterlist.size();i++)\r
- {\r
- \r
- if (videoH264filterlist[i].vmr9tested == true)\r
- {\r
- if (videoH264filterlist[i].vmr9 == true)\r
- {\r
- videoH264filterselected = i;\r
- break;\r
- } \r
- else\r
- {\r
- continue;\r
- }\r
- }\r
- else\r
- {\r
- IMoniker * moni=NULL;\r
- IBindCtx *bindctx=NULL;\r
- if (CreateBindCtx(0,&bindctx)!=S_OK) return NULL;\r
- LPCOLESTR name=(LPCOLESTR)new WCHAR[strlen(videoH264filterlist[i].displayname)+1];\r
- mbstowcs((wchar_t*)name,videoH264filterlist[i].displayname,strlen(videoH264filterlist[i].displayname)+1);\r
- ULONG eater=0;\r
- Log::getInstance()->log("VideoWin", Log::DEBUG ,"Creating filter: %s",videoH264filterlist[i].friendlyname);\r
-\r
- \r
-\r
- if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK)\r
- {\r
- if (moni->BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK)\r
- {\r
- IAMDecoderCaps* desccaps=NULL;\r
- if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK)\r
- {\r
- DWORD caps;\r
- desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps);\r
- if (caps == DECODER_CAP_SUPPORTED)\r
- {\r
- videoH264filterlist[i].vmr9tested = true;\r
- videoH264filterlist[i].vmr9 = true;\r
- videoH264filterselected = i;\r
- } \r
- else\r
- {\r
- videoH264filterlist[i].vmr9tested = true;\r
- videoH264filterlist[i].vmr9 = false;\r
- \r
- curfilter->Release();\r
- curfilter=NULL;\r
- }\r
- }\r
- desccaps->Release();\r
- }\r
- moni->Release();\r
- } \r
- delete [] name;\r
- bindctx->Release();\r
- }\r
- if (videoH264filterlist[i].vmr9) break;\r
- \r
- }\r
- if (curfilter != NULL)\r
- {\r
- VDR *vdr=VDR::getInstance();\r
- if (vdr != NULL)\r
- {\r
- vdr->configSave("DirectShow","VideoH264Filter",\r
- videoH264filterlist[videoH264filterselected].displayname);\r
- }\r
- return curfilter;\r
- }\r
- } \r
- else\r
- {\r
- IBindCtx *bindctx=NULL;\r
- if (CreateBindCtx(0,&bindctx)!=S_OK) return NULL;\r
- IMoniker * moni=NULL;\r
- LPCOLESTR name=new WCHAR[strlen(videoH264filterlist[videoH264filterselected].displayname)+1];\r
- mbstowcs((wchar_t*)name,videoH264filterlist[videoH264filterselected].displayname,\r
- strlen(videoH264filterlist[videoH264filterselected].displayname)+1);\r
- ULONG eater;\r
- Log::getInstance()->log("VideoWin", Log::DEBUG ,"Creating filter: %s",videoH264filterlist[videoH264filterselected].friendlyname);\r
- if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK)\r
- {\r
- if (moni->BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK)\r
- {\r
- IAMDecoderCaps* desccaps=NULL;\r
- if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK)\r
- {\r
- DWORD caps;\r
- desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps);\r
- if (caps == DECODER_CAP_SUPPORTED)\r
- {\r
- videoH264filterlist[videoH264filterselected].vmr9tested = true;\r
- videoH264filterlist[videoH264filterselected].vmr9 = true;\r
- } \r
- else\r
- {\r
- videoH264filterlist[videoH264filterselected].vmr9tested = true;\r
- videoH264filterlist[videoH264filterselected].vmr9 = false;\r
- Log::getInstance()->log("VideoWin", Log::WARN ,"Filter does not support VMR9, but is selected, manual selection!");\r
- }\r
- }\r
- moni->Release();\r
- delete [] name;\r
- bindctx->Release();\r
- return curfilter;\r
- } \r
- moni->Release();\r
- }\r
- bindctx->Release();\r
- delete [] name;\r
- return NULL; \r
- }\r
- return NULL;\r
- \r
-}\r
-\r
-\r
-#ifdef DS_DEBUG // This stuff would not included in vomp due to lincemse restrcitions\r
-#include "dshelper.h"\r
-#endif\r
-\r
-#define DO_VIDEO\r
-\r
-int VideoWin::play()\r
-{\r
- if (!initted) return 0;\r
- return 1;\r
-}\r
-\r
-bool VideoWin::addOptionPagesToWTB(WTabBar *wtb)\r
-{\r
- Boxx *box=new WWinVideoFilter();\r
- wtb->addTab(tr("Video Filter"), box);\r
- box=new WWinVideoH264Filter();\r
- wtb->addTab(tr("H264 Filter"), box);\r
- return true;\r
-}\r
-\r
-const VideoFilterDescList *VideoWin::getVideoFilterList(int &selected)\r
-{\r
- selected=videofilterselected;\r
- return &videofilterlist;\r
-}\r
-\r
-const VideoFilterDescList *VideoWin::getVideoH264FilterList(int &selected)\r
-{\r
- selected=videoH264filterselected;\r
- return &videoH264filterlist;\r
-}\r
-\r
-bool VideoWin::selectVideoFilter(int filter)\r
-{\r
- IBindCtx *bindctx=NULL;\r
- if (CreateBindCtx(0,&bindctx)!=S_OK) return NULL;\r
- IMoniker * moni=NULL;\r
- LPCOLESTR name=new WCHAR[strlen(videofilterlist[filter].displayname)+1];\r
- mbstowcs((wchar_t*)name,videofilterlist[filter].displayname,\r
- strlen(videofilterlist[filter].displayname)+1);\r
- ULONG eater;\r
- bool success=false;\r
- if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK)\r
- {\r
- IBaseFilter* curfilter=NULL;\r
- if (moni->BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK)\r
- {\r
- IAMDecoderCaps* desccaps=NULL;\r
- if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK)\r
- {\r
- DWORD caps;\r
- HRESULT hres=desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps);\r
- if (caps == DECODER_CAP_SUPPORTED)\r
- {\r
- videofilterlist[filter].vmr9tested = true;\r
- videofilterlist[filter].vmr9 = true;\r
- success=true;\r
- } \r
- else\r
- {\r
- videofilterlist[filter].vmr9tested = true;\r
- videofilterlist[filter].vmr9 = false;\r
- success=false;\r
- }\r
- desccaps->Release();\r
- } else {\r
- videofilterlist[filter].vmr9tested = true;\r
- videofilterlist[filter].vmr9 = false;\r
- success=false;\r
- }\r
-\r
- curfilter->Release();\r
- \r
- } \r
- moni->Release();\r
- }\r
- bindctx->Release();\r
- delete [] name;\r
- if (success || true) \r
- {\r
- videofilterselected=filter;\r
- return true;\r
- } \r
- else\r
- {\r
- return false;\r
- }\r
-}\r
-\r
-bool VideoWin::selectVideoH264Filter(int filter)\r
-{\r
- IBindCtx *bindctx=NULL;\r
- if (CreateBindCtx(0,&bindctx)!=S_OK) return NULL;\r
- IMoniker * moni=NULL;\r
- LPCOLESTR name=new WCHAR[strlen(videoH264filterlist[filter].displayname)+1];\r
- mbstowcs((wchar_t*)name,videoH264filterlist[filter].displayname,\r
- strlen(videoH264filterlist[filter].displayname)+1);\r
- ULONG eater;\r
- bool success=false;\r
- if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK)\r
- {\r
- IBaseFilter* curfilter=NULL;\r
- if (moni->BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK)\r
- {\r
- IAMDecoderCaps* desccaps=NULL;\r
- if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK)\r
- {\r
- DWORD caps;\r
- HRESULT hres=desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps);\r
- if (caps == DECODER_CAP_SUPPORTED)\r
- {\r
- videoH264filterlist[filter].vmr9tested = true;\r
- videoH264filterlist[filter].vmr9 = true;\r
- success=true;\r
- } \r
- else\r
- {\r
- videoH264filterlist[filter].vmr9tested = true;\r
- videoH264filterlist[filter].vmr9 = false;\r
- success=false;\r
- }\r
- desccaps->Release();\r
- } else {\r
- videoH264filterlist[filter].vmr9tested = true;\r
- videoH264filterlist[filter].vmr9 = false;\r
- success=false;\r
- }\r
-\r
- curfilter->Release();\r
- \r
- } \r
- moni->Release();\r
- }\r
- bindctx->Release();\r
- delete [] name;\r
- if (success || true) \r
- {\r
- videoH264filterselected=filter;\r
- return true;\r
- } \r
- else\r
- {\r
- return false;\r
- }\r
-}\r
-\r
-int VideoWin::dsInitVideoFilter()\r
-{\r
- #ifdef DO_VIDEO\r
- HRESULT hres;\r
- if (videoon) {\r
- //We alloc the vmr9 as next step\r
- if (currentpresenter==VMR9) \r
- {\r
- Log::getInstance()->log("VideoWin", Log::INFO ,"VMR9 Videopresenter selected!");\r
- if (hres=CoCreateInstance(CLSID_VideoMixingRenderer9,0,\r
- CLSCTX_INPROC_SERVER,IID_IBaseFilter,(void**) &dsrenderer)!=S_OK) \r
- {\r
- Log::getInstance()->log("VideoWin", Log::WARN ,"Failed creating VMR9 renderer!");\r
- ReleaseMutex(filtermutex);\r
- CleanupDS();\r
- }\r
- /*VMR 9 stuff**/\r
- if (hres=dsgraphbuilder->AddFilter(dsrenderer,L"VMR9")!=S_OK)\r
- {\r
- ReleaseMutex(filtermutex);\r
- CleanupDS();\r
- Log::getInstance()->log("VideoWin", Log::WARN ,"Failed adding VMR9 renderer!");\r
- return 0;\r
- }\r
- IVMRFilterConfig9* vmrfilconfig;\r
- if (dsrenderer->QueryInterface(IID_IVMRFilterConfig9,(void**)&vmrfilconfig)!=S_OK)\r
- {\r
- ReleaseMutex(filtermutex);\r
- CleanupDS();\r
- Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting VMR9 Filterconfig interface!");\r
- return 0;\r
- }\r
- if (vmrdeinterlacing!=0) vmrfilconfig->SetNumberOfStreams(1);//Enter Mixing Mode\r
- vmrfilconfig->SetRenderingMode(VMR9Mode_Renderless);\r
- vmrfilconfig->Release();\r
- if (dsrenderer->QueryInterface(IID_IVMRSurfaceAllocatorNotify9,\r
- (void**)& dsvmrsurfnotify) != S_OK)\r
- {\r
- ReleaseMutex(filtermutex);\r
- CleanupDS();\r
- Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting VMR9 Surface Allocator interface!");\r
- return 0;\r
- }\r
- allocatorvmr=new DsAllocator();\r
- dsvmrsurfnotify->AdviseSurfaceAllocator(NULL,allocatorvmr);\r
- allocatorvmr->AdviseNotify(dsvmrsurfnotify);\r
- \r
- IVMRDeinterlaceControl9* deintctrl;\r
- if (dsrenderer->QueryInterface(IID_IVMRDeinterlaceControl9,(void**)&deintctrl)!=S_OK)\r
- {\r
- ReleaseMutex(filtermutex);\r
- CleanupDS();\r
- Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting VMR9 Deinterlace control!");\r
- return 0;\r
- }\r
- /*turnoff*/\r
- switch (vmrdeinterlacing)\r
- {\r
- case 1: //No Deinterlasing\r
- deintctrl->SetDeinterlaceMode(0xFFFFFFFF,(LPGUID)&GUID_NULL);//Turn Off\r
- break;\r
- case 2: //Best\r
- deintctrl->SetDeinterlacePrefs(DeinterlacePref_NextBest);//Choose Next Best\r
- break;\r
- case 3: //Bob\r
- deintctrl->SetDeinterlacePrefs(DeinterlacePref_BOB);//Choose NBob\r
- break;\r
- case 4: //Weave\r
- deintctrl->SetDeinterlacePrefs(DeinterlacePref_Weave);//Choose Weave\r
- break;\r
- };\r
- deintctrl->Release();\r
- /*VMR 9 stuff end */\r
- }\r
- else if (currentpresenter==EVR)\r
- {\r
- Log::getInstance()->log("VideoWin", Log::INFO ,"EVR Videopresenter selected!");\r
- if (hres=CoCreateInstance(CLSID_EnhancedVideoRenderer,0,\r
- CLSCTX_INPROC_SERVER,IID_IBaseFilter,(void**) &dsrenderer)!=S_OK) \r
- {\r
- Log::getInstance()->log("VideoWin", Log::WARN ,"Failed creating EVR renderer!");\r
- ReleaseMutex(filtermutex);\r
- CleanupDS();\r
- }\r
- /*EVR stuff**/\r
- if (hres=dsgraphbuilder->AddFilter(dsrenderer,L"EVR")!=S_OK)\r
- {\r
- ReleaseMutex(filtermutex);\r
- CleanupDS();\r
- Log::getInstance()->log("VideoWin", Log::WARN ,"Failed adding EVR renderer!");\r
- return 0;\r
- }\r
- \r
- \r
- IMFGetService *evr_services;\r
- if (dsrenderer->QueryInterface(IID_IMFGetService,(void**)&evr_services)!=S_OK)\r
- {\r
- ReleaseMutex(filtermutex);\r
- CleanupDS();\r
- Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting EVR IMFGetServices interface!");\r
- return 0;\r
- }\r
-\r
- IMFVideoDisplayControl* mfvideodisplaycontrol;\r
- if (evr_services->GetService(MR_VIDEO_RENDER_SERVICE,IID_IMFVideoDisplayControl,(void**)&mfvideodisplaycontrol)!=S_OK)\r
- {\r
- ReleaseMutex(filtermutex);\r
- CleanupDS();\r
- Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting EVR IMFVideoDisplayControl interface!");\r
- return 0;\r
- }\r
-\r
- evr_services->Release();\r
- mfvideodisplaycontrol->SetVideoWindow(((OsdWin*) Osd::getInstance())->getWindow());\r
- //RECT client;\r
- //GetClientRect(((OsdWin*) Osd::getInstance())->getWindow(), &client);\r
- //mfvideodisplaycontrol->SetVideoPosition(NULL,&client);\r
-\r
- mfvideodisplaycontrol->Release();\r
-\r
-\r
- /// if (vmrdeinterlacing!=0) vmrfilconfig->SetNumberOfStreams(1);//Enter Mixing Mode //always the case for evr!\r
-\r
- IMFVideoRenderer *mfvideorenderer;\r
- if (dsrenderer->QueryInterface(IID_IMFVideoRenderer,(void**)&mfvideorenderer)!=S_OK)\r
- {\r
- ReleaseMutex(filtermutex);\r
- CleanupDS();\r
- Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting EVR IMFVideoRenderer interface!");\r
- return 0;\r
- }\r
- \r
- allocatorvmr=new DsAllocator();\r
- HRESULT hres=mfvideorenderer->InitializeRenderer(NULL,allocatorvmr);\r
-\r
- mfvideorenderer->Release();\r
- //How should I do this in EVR?\r
- /* IVMRDeinterlaceControl9* deintctrl;\r
- if (dsrenderer->QueryInterface(IID_IVMRDeinterlaceControl9,(void**)&deintctrl)!=S_OK)\r
- {\r
- ReleaseMutex(filtermutex);\r
- CleanupDS();\r
- Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting VMR9 Deinterlace control!");\r
- return 0;\r
- }\r
- /*turnoff*\r
- switch (vmrdeinterlacing)\r
- {\r
- case 1: //No Deinterlasing\r
- deintctrl->SetDeinterlaceMode(0xFFFFFFFF,(LPGUID)&GUID_NULL);//Turn Off\r
- break;\r
- case 2: //Best\r
- deintctrl->SetDeinterlacePrefs(DeinterlacePref_NextBest);//Choose Next Best\r
- break;\r
- case 3: //Bob\r
- deintctrl->SetDeinterlacePrefs(DeinterlacePref_BOB);//Choose NBob\r
- break;\r
- case 4: //Weave\r
- deintctrl->SetDeinterlacePrefs(DeinterlacePref_Weave);//Choose Weave\r
- break;\r
- };\r
- deintctrl->Release();*/\r
- /*EVR stuff end */\r
- } else {\r
- Log::getInstance()->log("VideoWin", Log::ERR ,"No videopresenter selected! Please post on the forum!");\r
- return -1;\r
- }\r
- IFilterGraph2*fg2=NULL;\r
- if (dsgraphbuilder->QueryInterface(IID_IFilterGraph2,(void**)&fg2)!= S_OK)\r
- {\r
- Log::getInstance()->log("VideoWin", Log::WARN , "Failed querying for FilterGraph2 Interface!");\r
- ReleaseMutex(filtermutex);\r
- CleanupDS();\r
- return 0;\r
- }\r
-/*#ifndef NEW_DS_MECHANISMENS\r
- \r
- if (hres=fg2->RenderEx((IPin*)sourcefilter->GetVideoPin()/*video*,\r
- AM_RENDEREX_RENDERTOEXISTINGRENDERERS,NULL) != S_OK) \r
- {\r
- Log::getInstance()->log("VideoWin", Log::WARN , "Failed rendering Video!");\r
- fg2->Release();\r
- ReleaseMutex(filtermutex);\r
- CleanupDS();\r
- return 0;\r
- }\r
- \r
-#else*/\r
- IBaseFilter*videofilter;\r
- if (h264) \r
- {\r
- Log::getInstance()->log("VideoWin", Log::DEBUG ,"Entering h264 playback...");\r
- videofilter=getVideoH264Filter();\r
- } \r
- else\r
- {\r
- Log::getInstance()->log("VideoWin", Log::DEBUG ,"Entering MPEG2 playback...");\r
- videofilter=getVideoFilter();\r
- }\r
- if (hres=dsgraphbuilder->AddFilter(videofilter,NULL) != S_OK) \r
- {\r
- Log::getInstance()->log("VideoWin", Log::WARN , "Failed adding Video Filter!");\r
- ReleaseMutex(filtermutex);\r
- CleanupDS();\r
- return 0;\r
- }\r
- IEnumPins *pinenum=NULL;\r
- bool error=false;\r
-\r
- mptype_video_detail vid_details;\r
- Demuxer* demux=Demuxer::getInstance();\r
- vid_details.width=demux->getHorizontalSize();\r
- vid_details.height=demux->getVerticalSize();\r
-\r
- if (h264)\r
- {\r
- if (vid_details.width!=0 && vid_details.height!=0)\r
- {\r
- sourcefilter->GetVideoPin()->SetPinMode(MPTYPE_VIDEO_H264,&vid_details);\r
- }\r
- else\r
- {\r
- sourcefilter->GetVideoPin()->SetPinMode(MPTYPE_VIDEO_H264,NULL);\r
- }\r
-\r
- }\r
- else\r
- {\r
- if (vid_details.width!=0 && vid_details.height!=0)\r
- {\r
- sourcefilter->GetVideoPin()->SetPinMode(MPTYPE_VIDEO_MPEG2,&vid_details);\r
- }\r
- else\r
- {\r
- sourcefilter->GetVideoPin()->SetPinMode(MPTYPE_VIDEO_MPEG2,NULL);\r
- } \r
- }\r
- if (videofilter->EnumPins(&pinenum) == S_OK)\r
- {\r
- IPin *current=NULL;\r
- ULONG fetch=0;\r
- bool firststep=false;\r
-\r
- while (pinenum->Next(1,¤t,&fetch)==S_OK)\r
- {\r
- PIN_DIRECTION dir;\r
- if (current->QueryDirection(&dir)==S_OK)\r
- {\r
- if (dir == PINDIR_INPUT)\r
- {\r
- if (sourcefilter->GetVideoPin()->Connect(current,NULL)==S_OK)\r
- {\r
- current->Release();\r
- firststep=true;\r
- break;\r
- }\r
- }\r
- }\r
- current->Release();\r
- }\r
- if (firststep==false)\r
- {\r
- Log::getInstance()->log("VideoWin", Log::WARN , "Video Filter has no suitable input!");\r
- videofilter->Release();\r
- ReleaseMutex(filtermutex);\r
- CleanupDS();\r
- return 0;\r
- }\r
- bool secondstep=false;\r
- pinenum->Reset();\r
- while (pinenum->Next(1,¤t,&fetch)==S_OK)\r
- {\r
- PIN_DIRECTION dir;\r
- if (current->QueryDirection(&dir)==S_OK)\r
- {\r
- if (dir == PINDIR_OUTPUT)\r
- {\r
- \r
- if (fg2->RenderEx((IPin*)current/*video*/,\r
- AM_RENDEREX_RENDERTOEXISTINGRENDERERS,NULL) ==S_OK)\r
- {\r
- current->Release();\r
- secondstep=true;\r
- break;\r
- }\r
- }\r
- }\r
- current->Release();\r
- }\r
- if (secondstep==false)\r
- {\r
- Log::getInstance()->log("VideoWin", Log::WARN , "Video Filter has no suitable output!");\r
- videofilter->Release();\r
- ReleaseMutex(filtermutex);\r
- CleanupDS();\r
- return 0;\r
- }\r
- \r
- videofilter->Release();\r
- pinenum->Release();\r
-\r
- }\r
-\r
-\r
-\r
- fg2->Release();\r
- return 1;\r
- }\r
-#endif \r
- return 1;\r
-}\r
-\r
-int VideoWin::setAudioStreamType(UCHAR type)\r
-{\r
- aud_type=type;\r
- if (!initted) return 0;\r
- return 1;\r
-}\r
-\r
-int VideoWin::dsplay()\r
-{\r
- if (!initted) return 0;\r
- CleanupDS();\r
- \r
- //Build filter graph\r
- HRESULT hres;\r
- //So this is the real code, this prevents the feeder from calling noexisting objects!\r
- WaitForSingleObject(filtermutex,INFINITE);\r
- if (hres=CoCreateInstance(CLSID_FilterGraph,NULL,CLSCTX_INPROC_SERVER,\r
- IID_IGraphBuilder,(void**)&dsgraphbuilder) != S_OK) \r
- {\r
- ReleaseMutex(filtermutex);\r
- return 0;\r
- }\r
- #ifdef DS_DEBUG\r
- AddToRot(dsgraphbuilder,&graphidentifier);\r
- #endif\r
- \r
- firstsynched=false;\r
- if (aud_type==Audio::MP3) {\r
- lastaudiomode=MPTYPE_MPEG_AUDIO_LAYER3;\r
- } else {\r
- lastaudiomode=MPTYPE_MPEG_AUDIO;\r
- }\r
- //lastaudiomode=MPTYPE_AC3;\r
- sourcefilter=new DsSourceFilter(); //Creating our Source filter for pushing Data\r
- // to DirectShow\r
- if (hres=dsgraphbuilder->AddFilter(sourcefilter,L"Vomp Win Source Filter") != S_OK) \r
- {\r
- Log::getInstance()->log("VideoWin", Log::WARN , "Failed adding Vomp Source Filter!");\r
- ReleaseMutex(filtermutex);\r
- CleanupDS();\r
- return 0;\r
- }\r
- sourcefilter->GetAudioPin()->SetPinMode(lastaudiomode);\r
- /*if (hres=dsgraphbuilder->Render((IPin*)sourcefilter->GetAudioPin()/*audio*)!=S_OK) \r
- {\r
- Log::getInstance()->log("VideoWin", Log::WARN , "Failed rendering audio!");\r
- ReleaseMutex(filtermutex);\r
- CleanupDS();\r
- return 0;\r
- }*/\r
- if (((AudioWin*)Audio::getInstance())->dsInitAudioFilter(dsgraphbuilder)==0)\r
- {\r
- Log::getInstance()->log("VideoWin", Log::WARN , "Failed rendering audio!");\r
- ReleaseMutex(filtermutex);\r
- CleanupDS();\r
- return 0;\r
- }\r
-\r
-\r
- if (dsInitVideoFilter()==0)\r
- { \r
- return 0;\r
- }\r
-\r
- if (hres=CoCreateInstance(CLSID_SystemClock,NULL,CLSCTX_INPROC_SERVER,\r
- IID_IReferenceClock,(void**)&dsrefclock)!=S_OK) \r
- {\r
- return 0;\r
- }\r
- dsgraphbuilder->QueryInterface(IID_IMediaFilter,(void **) &dsmediafilter);\r
- HRESULT hresdeb = dsmediafilter->SetSyncSource(dsrefclock);\r
-\r
- dsgraphbuilder->QueryInterface(IID_IMediaControl,(void **) &dsmediacontrol);\r
- dsgraphbuilder->QueryInterface(IID_IBasicAudio,(void **) &dsbasicaudio); \r
- if (dsbasicaudio) \r
- dsbasicaudio->put_Volume(audiovolume);\r
- dsinited=true;\r
- //MILLISLEEP(100);\r
-\r
- hresdeb=dsmediacontrol->Run();\r
- iframemode=false;//exit iframe mode\r
- ReleaseMutex(filtermutex);\r
- return 1;\r
-}\r
-\r
-int VideoWin::EnterIframePlayback()\r
-{\r
- if (!initted) return 0;\r
- CleanupDS();\r
- //So this is the real code, this prevents the feeder from calling noexisting objects!\r
- WaitForSingleObject(filtermutex,INFINITE);\r
- iframemode=true;//enter iframe mode\r
- //Build filter graph\r
- HRESULT hres;\r
- if (hres=CoCreateInstance(CLSID_FilterGraph,NULL,CLSCTX_INPROC_SERVER,\r
- IID_IGraphBuilder,(void**)&dsgraphbuilder)!=S_OK) {\r
- ReleaseMutex(filtermutex);\r
- return 0;\r
- }\r
-#ifdef DS_DEBUG\r
- AddToRot(dsgraphbuilder,&graphidentifier);\r
-#endif\r
- \r
- //firstsynched=false;\r
- sourcefilter=new DsSourceFilter(); //Creating our Source filter for pushing Data\r
- // to DirectShow\r
- if (hres=dsgraphbuilder->AddFilter(sourcefilter,L"Vomp Win Source Filter")!=S_OK) {\r
- Log::getInstance()->log("VideoWin", Log::WARN , "Failed adding Vomp Source Filter!");\r
- ReleaseMutex(filtermutex); \r
- CleanupDS();\r
- return 0;\r
- }\r
-#ifdef DO_VIDEO\r
- if (videoon) {\r
- dsInitVideoFilter();\r
- }\r
-#endif\r
-/* if (hres=CoCreateInstance(CLSID_SystemClock,NULL,CLSCTX_INPROC_SERVER,\r
- IID_IReferenceClock,(void**)&dsrefclock)!=S_OK) {\r
- return 0;\r
- }*/\r
-\r
- dsgraphbuilder->QueryInterface(IID_IMediaFilter,(void **) &dsmediafilter);\r
- dsmediafilter->SetSyncSource(/*dsrefclock*/NULL); //Run as fast as you can!\r
-\r
- dsgraphbuilder->QueryInterface(IID_IMediaControl,(void **) &dsmediacontrol);\r
- dsgraphbuilder->QueryInterface(IID_IBasicAudio,(void **) &dsbasicaudio); \r
- dsinited=true;\r
- \r
-\r
- dsmediacontrol->Run();\r
- ReleaseMutex(filtermutex);\r
- return 1;\r
-\r
-}\r
-\r
-int VideoWin::dsstop()\r
-{\r
- if (!initted) return 0;\r
-\r
- CleanupDS();\r
-\r
-\r
- return 1;\r
-}\r
-\r
-int VideoWin::stop()\r
-{\r
- if (!initted) return 0;\r
-\r
-\r
- return 1;\r
-}\r
-\r
-int VideoWin::reset()\r
-{\r
- if (!initted) return 0;\r
- \r
-\r
- return 1;\r
-}\r
-\r
-int VideoWin::dsreset()\r
-{\r
- if (!initted) return 0;\r
- videoposx=0;\r
- videoposy=0;\r
- iframemode=false;//exit iframe mode\r
- CleanupDS();\r
-\r
- return 1;\r
-}\r
-\r
-int VideoWin::dspause()\r
-{\r
- if (!initted) return 0;\r
- WaitForSingleObject(filtermutex,INFINITE);\r
- if (dsmediacontrol) dsmediacontrol->Pause();\r
- ReleaseMutex(filtermutex);\r
- return 1;\r
-}\r
-\r
-int VideoWin::pause()\r
-{\r
- if (!initted) return 0;\r
- \r
- return 1;\r
-}\r
-\r
-int VideoWin::unPause() // FIXME get rid - same as play!!\r
-{//No on windows this is not the same, I don't get rid of!\r
- if (!initted) return 0;\r
- return 1;\r
-}\r
-\r
-int VideoWin::dsunPause() // FIXME get rid - same as play!!\r
-{//No on windows this is not the same, I don't get rid of!\r
- if (!initted) return 0;\r
- WaitForSingleObject(filtermutex,INFINITE);\r
- if (dsmediacontrol) dsmediacontrol->Run();\r
- ReleaseMutex(filtermutex);\r
-\r
- return 1;\r
-}\r
-\r
-int VideoWin::fastForward()\r
-{\r
- if (!initted) return 0;\r
-\r
- return 1;\r
-}\r
-\r
-int VideoWin::unFastForward()\r
-{\r
- if (!initted) return 0;\r
- \r
- return 1;\r
-}\r
-\r
-int VideoWin::attachFrameBuffer()\r
-{\r
- if (!initted) return 0;\r
- return 1;\r
-}\r
-\r
-int VideoWin::blank(void)\r
-{\r
- ((OsdWin*)Osd::getInstance())->Blank();\r
- return 1;\r
-}\r
-\r
-ULLONG VideoWin::getCurrentTimestamp()\r
-{\r
- REFERENCE_TIME startoffset;\r
- REFERENCE_TIME ncr_time;\r
- if (iframemode) return 0; //Not in iframe mode!\r
- if (!dsrefclock || !sourcefilter) return 0;\r
- FILTER_STATE state;\r
- sourcefilter->GetState(10,&state);\r
-\r
- if (state==State_Running) dsrefclock->GetTime(&cr_time);\r
- ncr_time=cr_time;\r
- startoffset=sourcefilter->getStartOffset();\r
- if (startoffset==0) return 0;\r
- ncr_time-=startoffset;\r
- ncr_time-=lastreftimeRT;\r
- /* ULLONG result=frameNumberToTimecode(\r
- VDR::getInstance()->frameNumberFromPosition(lastreftimeBYTE));*/\r
- long long result=lastreftimePTS;\r
- result+=(ULLONG)(ncr_time/10000LL*90LL);\r
- if (result<0) result=(1LL << 33)-result;\r
- return result;\r
-\r
-}\r
-\r
-\r
-/* //to beremoved\r
-ULONG VideoWin::timecodeToFrameNumber(ULLONG timecode)\r
-{\r
- if (format == PAL) return (ULONG)(((double)timecode / (double)90000) * (double)25);\r
- else return (ULONG)(((double)timecode / (double)90000) * (double)30);\r
-}\r
-\r
-ULLONG VideoWin::frameNumberToTimecode(ULONG framenumber)\r
-{\r
- if (format == PAL) return (ULLONG)(((double)framenumber * (double)90000) / (double)25);\r
- else return (ULLONG)(((double)framenumber * (double)90000) / (double)30);\r
-}\r
-*/\r
-void VideoWin::CleanupDS()\r
-{\r
- WaitForSingleObject(filtermutex,INFINITE);\r
- dsinited=false;\r
- if (dsmediacontrol)dsmediacontrol->Stop();\r
- if (cur_audio_media_sample) {\r
- cur_audio_media_sample->Release();\r
- cur_audio_media_sample=NULL;\r
- }\r
- if (cur_video_media_sample) {\r
- cur_video_media_sample->Release();\r
- cur_video_media_sample=NULL;\r
- }\r
- if (dsbasicaudio) {\r
- dsbasicaudio->Release();\r
- dsbasicaudio=NULL;\r
- }\r
- if (dsvmrsurfnotify) {\r
- dsvmrsurfnotify->Release();\r
- dsvmrsurfnotify=NULL;\r
- }\r
- if (dsrenderer) {\r
- dsrenderer->Release();\r
- dsrenderer=NULL;\r
- }\r
-\r
- if (allocatorvmr) {\r
- allocatorvmr->Release();\r
- allocatorvmr=NULL;\r
- }\r
-\r
- if (dsrefclock) {\r
- dsrefclock->Release();\r
- dsrefclock=NULL;\r
- }\r
- if (dsmediafilter) {\r
- dsmediafilter->Release();\r
- dsmediafilter=NULL;\r
- }\r
-\r
-\r
-\r
- if (dsmediacontrol) {\r
- dsmediacontrol->Stop();\r
- dsmediacontrol->Release();\r
- dsmediacontrol=NULL;\r
- }\r
- if (dsgraphbuilder){\r
-#ifdef DS_DEBUG\r
- RemoveFromRot(graphidentifier);\r
-#endif\r
- dsgraphbuilder->Release();\r
- dsgraphbuilder=NULL;\r
- \r
- sourcefilter=NULL; //The Graph Builder destroys our SourceFilter\r
- }\r
- ReleaseMutex(filtermutex);\r
-\r
-}\r
-\r
-void VideoWin::PrepareMediaSample(const MediaPacketList& mplist,\r
- UINT samplepos)\r
-{\r
- mediapacket = mplist.front();\r
-}\r
-\r
-UINT VideoWin::DeliverMediaSample(UCHAR* buffer, UINT *samplepos)\r
-{\r
- DeliverMediaPacket(mediapacket, buffer, samplepos);\r
- if (*samplepos == mediapacket.length) {\r
- *samplepos = 0;\r
- return 1;\r
- }\r
- else return 0;\r
-}\r
-\r
-UINT VideoWin::DeliverMediaPacket(MediaPacket packet,\r
- const UCHAR* buffer,\r
- UINT *samplepos)\r
-{\r
-\r
- /*First Check, if we have an audio sample*/\r
- if (!isdsinited()) return 0;\r
- if (packet.type == MPTYPE_VIDEO_H264)\r
- {\r
- h264=true;\r
- }\r
- else\r
- {\r
- h264=false;\r
- }\r
-\r
-#ifdef DO_VIDEO\r
- if (!videoon) {\r
- *samplepos+=packet.length;\r
- MILLISLEEP(0); //yet not implemented//bad idea\r
- return packet.length;\r
- }\r
- /*First Check, if we have an audio sample*/\r
- if (iframemode) {\r
- //samplepos=0;\r
- MILLISLEEP(10);\r
- return 0; //Not in iframe mode!\r
- }\r
- IMediaSample* ms=NULL;\r
- REFERENCE_TIME reftime1=0;\r
- REFERENCE_TIME reftime2=0;\r
-\r
- UINT headerstrip=0;\r
- if (packet.disconti) {\r
- firstsynched=false;\r
- DeliverVideoMediaSample();\r
- }\r
-\r
- \r
-\r
- /*Inspect PES-Header */\r
-\r
- if (*samplepos==0) {//stripheader\r
- headerstrip=buffer[packet.pos_buffer+8]+9/*is this right*/;\r
- *samplepos+=headerstrip;\r
- if ( packet.synched ) {\r
- DeliverVideoMediaSample();//write out old data\r
- /* if (packet.presentation_time<0) { //Preroll?\r
- *samplepos=packet.length;//if we have not processed at least one\r
- return packet.length;//synched packet ignore it!\r
- }*/\r
-\r
- reftime1=packet.presentation_time;\r
- reftime2=reftime1+1;\r
- firstsynched=true;\r
- } else {\r
- if (!firstsynched) {//\r
- *samplepos=packet.length;//if we have not processed at least one\r
- return packet.length;//synched packet ignore it!\r
- }\r
- }\r
- }\r
- BYTE *ms_buf;\r
- UINT ms_length;\r
- UINT ms_pos;\r
- UINT haveToCopy;\r
-\r
- if (!getCurrentVideoMediaSample(&ms) || ms==NULL) {// get the current sample\r
- //samplepos=0;\r
- //MessageBox(0,"da isser","hei",0);\r
- //MILLISLEEP(1);\r
- return 0;\r
- }\r
- ms_pos=ms->GetActualDataLength();\r
- ms_length=ms->GetSize();\r
- haveToCopy=min(ms_length-ms_pos,packet.length-*samplepos);\r
- if ((ms_length-ms_pos)<1 ) {\r
- DeliverVideoMediaSample(); //we are full!\r
- if (!getCurrentVideoMediaSample(&ms) || ms==NULL) {// get the current sample\r
- //samplepos=0;\r
- //MessageBox(0,"da isser","hei",0);\r
- //MILLISLEEP(10);\r
- return 0;\r
- }\r
- ms_pos=ms->GetActualDataLength();\r
- ms_length=ms->GetSize();\r
- haveToCopy=min(ms_length-ms_pos,packet.length-*samplepos);\r
- }\r
- ms->GetPointer(&ms_buf);\r
-\r
-\r
- if (ms_pos==0) {//will only be changed on first packet\r
- if (packet.disconti) {\r
- ms->SetDiscontinuity(TRUE);\r
- } else {\r
- ms->SetDiscontinuity(FALSE);\r
- }\r
- if (packet.synched) {\r
- ms->SetSyncPoint(TRUE);\r
- ms->SetTime(&reftime1,&reftime2);\r
- //Log::getInstance()->log("VideoWin", Log::DEBUG , "Setted videotime to %lld %lld",reftime1,reftime2);\r
- //Log::getInstance()->log("VideoWin", Log::DEBUG , "Packet pts %lld dts %lld",packet.pts,packet.dts);\r
- //ms->SetTime(NULL,NULL);\r
- ms->SetMediaTime(NULL, NULL);\r
- if (reftime1<0) ms->SetPreroll(TRUE);\r
- else ms->SetPreroll(FALSE);\r
- /*Timecode handling*/\r
- lastreftimeRT=reftime1;\r
- lastreftimePTS=packet.pts;\r
-\r
- }\r
- else\r
- {\r
- ms->SetSyncPoint(FALSE);\r
- ms->SetTime(NULL,NULL);\r
- ms->SetMediaTime(NULL, NULL);\r
- ms->SetPreroll(FALSE);\r
-\r
- // ms->SetSyncPoint(TRUE);\r
- }\r
- }\r
- \r
-\r
-\r
- memcpy(ms_buf+ms_pos,buffer+packet.pos_buffer+*samplepos,haveToCopy);\r
- ms->SetActualDataLength(haveToCopy+ms_pos);\r
-\r
- *samplepos+=haveToCopy;\r
-\r
- return haveToCopy+headerstrip;\r
-\r
-#else\r
-\r
- *samplepos+=packet.length;\r
- MILLISLEEP(0); //yet not implemented//bad idea\r
- return packet.length;\r
-#endif\r
-}\r
-\r
-int VideoWin::getCurrentAudioMediaSample(IMediaSample** ms)\r
-{\r
- //WaitForSingleObject(filtermutex,INFINITE);\r
- if (!sourcefilter){\r
- // ReleaseMutex(filtermutex);\r
- return 0;\r
- }\r
- if (cur_audio_media_sample) {\r
- *ms=cur_audio_media_sample;//already open\r
- return 1;\r
- }\r
- if (!sourcefilter->getCurrentAudioMediaSample(ms)) {\r
- // ReleaseMutex(filtermutex);\r
- }\r
- if (*ms) (*ms)->SetActualDataLength(0);\r
- cur_audio_media_sample=*ms;\r
- //Don't release the mutex before deliver\r
- return 1;\r
-}\r
-\r
-int VideoWin::getCurrentVideoMediaSample(IMediaSample** ms)\r
-{\r
- //WaitForSingleObject(filtermutex,INFINITE);\r
- if (!sourcefilter){\r
- // ReleaseMutex(filtermutex);\r
- return 0;\r
- }\r
- if (cur_video_media_sample) {\r
- *ms=cur_video_media_sample;//already open\r
- return 1;\r
- }\r
- if (!sourcefilter->getCurrentVideoMediaSample(ms)) {\r
- // ReleaseMutex(filtermutex);\r
- }\r
- if (*ms) (*ms)->SetActualDataLength(0);\r
-\r
- cur_video_media_sample=*ms;\r
- //Don't release the mutex before deliver\r
- return 1;\r
-}\r
-\r
-int VideoWin::DeliverAudioMediaSample(){\r
- if (cur_audio_media_sample) {\r
- sourcefilter->DeliverAudioMediaSample(cur_audio_media_sample);\r
- cur_audio_media_sample=NULL;\r
- }\r
- //ReleaseMutex(filtermutex);\r
- return 1;\r
-}\r
-\r
-int VideoWin::DeliverVideoMediaSample(){\r
- if (cur_video_media_sample) {\r
- sourcefilter->DeliverVideoMediaSample(cur_video_media_sample);\r
- cur_video_media_sample=NULL;\r
- }\r
- //ReleaseMutex(filtermutex);\r
- return 1;\r
-}\r
-\r
-long long VideoWin::SetStartOffset(long long curreftime, bool *rsync)\r
-{\r
- *rsync=false;\r
- if (offsetnotset) {\r
- startoffset=curreftime;//offset is set for audio\r
- offsetnotset=false;\r
- offsetvideonotset=false;\r
-\r
-\r
- } else {\r
- if (offsetvideonotset) {\r
- offsetvideonotset=false;\r
- *rsync=true;\r
- } else {\r
- if ( (curreftime-lastrefvideotime)>10000000LL\r
- || (curreftime-lastrefvideotime)<-10000000LL) {//if pts jumps to big resync\r
- startoffset+=curreftime-lastrefvideotime;\r
- lastrefaudiotime+=curreftime-lastrefvideotime;\r
- //*rsync=true;\r
- offsetaudionotset=true;\r
-\r
- }\r
- }\r
-\r
- }\r
-\r
- lastrefvideotime=curreftime;\r
- \r
- return startoffset;\r
-\r
-}\r
-\r
-long long VideoWin::SetStartAudioOffset(long long curreftime, bool *rsync)\r
-{\r
- *rsync=false;\r
- if (offsetnotset) {\r
- startoffset=curreftime;\r
- offsetnotset=false;\r
- offsetaudionotset=false;\r
- }else {\r
- if (offsetaudionotset) {\r
- offsetaudionotset=false;\r
- *rsync=true;\r
- } else {\r
- if ( (curreftime-lastrefaudiotime)>10000000LL\r
- || (curreftime-lastrefaudiotime)<-10000000LL) {//if pts jumps to big resync\r
- startoffset+=curreftime-lastrefaudiotime;\r
- lastrefvideotime+=curreftime-lastrefaudiotime;\r
- //*rsync=true;\r
- offsetvideonotset=true;\r
-\r
- }\r
- }\r
-\r
- }\r
- lastrefaudiotime=curreftime;\r
- return startoffset;\r
-\r
-}\r
-void VideoWin::ResetTimeOffsets() {\r
- offsetnotset=true; //called from demuxer\r
- offsetvideonotset=true;\r
- offsetaudionotset=true;\r
- startoffset=0;\r
- lastrefaudiotime=0;\r
- lastrefvideotime=0;\r
- lastreftimeRT=0;\r
- lastreftimePTS=0;\r
-\r
-\r
-}\r
-\r
-void VideoWin::SetAudioVolume(long volume)\r
-{\r
- audiovolume=volume;\r
- if (dsbasicaudio) dsbasicaudio->put_Volume(volume);\r
-}\r
-\r
-bool VideoWin::displayIFrame(const UCHAR* buffer, UINT length)\r
-{\r
- if (!iframemode) EnterIframePlayback();\r
- if (!isdsinited()) return false;\r
- \r
-#ifdef DO_VIDEO\r
- IMediaSample* ms=NULL;\r
- REFERENCE_TIME reftime1=0;\r
- REFERENCE_TIME reftime2=0;\r
- if (!videoon) return;\r
- if (!getCurrentVideoMediaSample(&ms) || ms==NULL) {// get the current sample\r
- MILLISLEEP(10);\r
- return ;\r
- }\r
- BYTE *ms_buf;\r
- DWORD ms_length;\r
- ms->GetPointer(&ms_buf);\r
- ms_length=ms->GetSize();\r
- \r
- /*First Check, if we have an video sample*/\r
- DWORD read_pos = 0, write_pos = 0;\r
- DWORD pattern, packet_length;\r
- DWORD headerstrip=0;\r
- bool first=true;\r
- if (length < 4) return ;\r
- //Now we strip the pes header\r
- pattern = (buffer[0] << 16) | (buffer[1] << 8) | (buffer[2]);\r
- while (read_pos + 7 <= length)\r
- {\r
- pattern = ((pattern << 8) & 0xFFFFFFFF) | buffer[read_pos+3];\r
- if (pattern < 0x000001E0 || pattern > 0x000001EF) {\r
- read_pos++;\r
- continue;\r
- }\r
- else\r
- {\r
- headerstrip=buffer[read_pos+8]+9/*is this right*/;\r
- packet_length = ((buffer[read_pos+4] << 8) | (buffer[read_pos+5])) + 6;\r
- if (read_pos + packet_length > length)\r
- read_pos = length;\r
- else\r
- {\r
- if ( (headerstrip < packet_length) &&\r
- (write_pos+packet_length-headerstrip)>ms_length) {\r
- if (first) {\r
- ms->SetSyncPoint(TRUE);\r
- ms->SetDiscontinuity(TRUE);\r
- first=false;\r
- } else ms->SetSyncPoint(FALSE);\r
- ms->SetTime(NULL,NULL);\r
- ms->SetMediaTime(NULL, NULL);\r
- ms->SetActualDataLength(write_pos);\r
- DeliverVideoMediaSample();\r
-\r
- if (!getCurrentVideoMediaSample(&ms) || ms==NULL) {// get the current sample\r
- MILLISLEEP(10);\r
- return ;\r
- }\r
- write_pos=0;\r
- ms_length=ms->GetSize();\r
- ms->GetPointer(&ms_buf);\r
- }\r
- if (packet_length>headerstrip) {\r
- memcpy(ms_buf+write_pos, buffer+read_pos+headerstrip, packet_length-headerstrip);\r
- write_pos += packet_length-headerstrip;\r
- }\r
- read_pos += packet_length;\r
- \r
- pattern = (buffer[read_pos] << 16) | (buffer[read_pos+1] << 8)\r
- | (buffer[read_pos+2]);\r
- }\r
- }\r
- }\r
- \r
- if (first) {ms->SetSyncPoint(TRUE);\r
- ms->SetDiscontinuity(TRUE);\r
- first=false;} \r
- else ms->SetSyncPoint(FALSE);\r
- ms->SetTime(NULL,NULL);\r
- ms->SetMediaTime(NULL, NULL);\r
- ms->SetActualDataLength(write_pos);\r
- DeliverVideoMediaSample();\r
-\r
- return true;\r
-#else\r
-\r
- // *samplepos+=packet.length;\r
- MILLISLEEP(0); //yet not implemented//bad idea\r
- return true;\r
-#endif\r
-}\r
-\r
-bool VideoWin::supportsAc3(){\r
- if (sourcefilter != NULL) {\r
- return sourcefilter->supportsAc3();\r
- } else {\r
- return false;\r
- }\r
-}\r
-\r
-bool VideoWin::supportsh264()\r
-{\r
- if (videoH264filterlist.size()>0) return true;\r
- else return false;\r
-}\r
-\r
-\r
-bool VideoWin::changeAType(int type,IMediaSample* ms){\r
- if (sourcefilter!= NULL) {\r
- lastaudiomode=type;\r
- return sourcefilter->changeAType(type,ms);\r
- }\r
- else \r
- {\r
- return false;\r
- }\r
-}\r
-\r
-#ifdef DEV\r
-int VideoWin::test()\r
-{\r
- return 0;\r
-}\r
-\r
-int VideoWin::test2()\r
-{\r
- return 0;\r
-}\r
-#endif\r
-\r
-\r
-\r
-\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 "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 <Mfapi.h>
+#include <mferror.h>
+
+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;i<videofilterlist.size();i++)
+ {
+ if (videofilterlist[i].displayname) delete [] videofilterlist[i].displayname;
+ if (videofilterlist[i].friendlyname) delete [] videofilterlist[i].friendlyname;
+ }
+ videofilterlist.clear();
+
+ for (i=0;i<videoH264filterlist.size();i++)
+ {
+ if (videoH264filterlist[i].displayname) delete [] videoH264filterlist[i].displayname;
+ if (videoH264filterlist[i].friendlyname) delete [] videoH264filterlist[i].friendlyname;
+ }
+ videoH264filterlist.clear();
+
+
+
+
+ instance = NULL;
+}
+
+int VideoWin::init(UCHAR tformat)
+{
+ if (initted) return 0;
+
+ initted = 1;
+ tvsize=Video::ASPECT16X9; //Internally Vomp should think we are a 16:9 TV
+ videoposx=0;
+ videoposy=0;
+ initFilterDatabase();
+ initH264FilterDatabase();
+
+ if (!setFormat(tformat)){ shutdown(); return 0; }
+ return 1;
+}
+
+
+
+int VideoWin::setTVsize(UCHAR ttvsize)
+{
+ pseudotvsize=ttvsize;
+ return 1;
+}
+
+int VideoWin::setDefaultAspect()
+{
+ return setAspectRatio(Video::ASPECT4X3);
+}
+
+int VideoWin::shutdown()
+{
+ if (!initted) return 0;
+ initted = 0;
+ return 1;
+}
+
+int VideoWin::setFormat(UCHAR tformat)
+{
+ if (!initted) return 0;
+ if ((tformat != PAL) && (tformat != NTSC)) return 0;
+ format = tformat;
+ if (format == NTSC)
+ {
+ screenWidth = 720;
+ screenHeight = 480;
+ }
+ if (format == PAL)
+ {
+ screenWidth = 720;
+ screenHeight = 576;
+ }
+
+ return 1;
+}
+
+int VideoWin::setConnection(UCHAR tconnection)
+{
+ if (!initted) return 0;
+ if ((tconnection != COMPOSITERGB) && (tconnection != SVIDEO)) return 0;
+ connection = tconnection;
+
+ return 1;
+}
+
+int VideoWin::setAspectRatio(UCHAR taspectRatio)
+{
+ if (!initted) return 0;
+ if ((taspectRatio != ASPECT4X3) && (taspectRatio != ASPECT16X9)) return 0;
+ aspectRatio = taspectRatio;
+ AdjustWindow();
+ return 1;
+}
+
+int VideoWin::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;
+ videoposx=0;
+ videoposy=0;
+ AdjustWindow();
+
+ return 1;
+}
+
+int VideoWin::signalOff()
+{
+ return 1;
+}
+
+int VideoWin::signalOn()
+{
+ return 1;
+}
+
+int VideoWin::setSource()
+{
+ if (!initted) return 0;
+
+ return 1;
+}
+
+int VideoWin::setPosition(int x, int y)
+{
+ if (!initted) return 0;
+ if (mode==QUARTER || mode==EIGHTH) {
+ videoposx=x;
+ videoposy=y;
+ }
+ return 1;
+}
+
+int VideoWin::sync()
+{
+ if (!initted) return 0;
+
+ return 1;
+}
+void VideoWin::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("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 <videofilterlist.size();i++)
+ {
+ if (strcmp(name,videofilterlist[i].displayname)==0)
+ {
+ videofilterselected = i;
+ break;
+ }
+ }
+ }
+ name=vdr->configLoad("DirectShow","VideoH264Filter");
+
+ if (name != NULL)
+ {
+ for (int i = 0;i <videoH264filterlist.size();i++)
+ {
+ if (strcmp(name,videoH264filterlist[i].displayname)==0)
+ {
+ videoH264filterselected = i;
+ break;
+ }
+ }
+ }
+ name=vdr->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 <videofilterlist.size();i++)
+ {
+
+ if (videofilterlist[i].vmr9tested == true)
+ {
+ if (videofilterlist[i].vmr9 == true)
+ {
+ videofilterselected = i;
+ break;
+ }
+ else
+ {
+ continue;
+ }
+ }
+ else
+ {
+ IMoniker * moni=NULL;
+ IBindCtx *bindctx=NULL;
+ if (CreateBindCtx(0,&bindctx)!=S_OK) return NULL;
+ LPCOLESTR name=(LPCOLESTR)new WCHAR[strlen(videofilterlist[i].displayname)+1];
+ mbstowcs((wchar_t*)name,videofilterlist[i].displayname,strlen(videofilterlist[i].displayname)+1);
+ ULONG eater=0;
+
+
+ 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[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 <videoH264filterlist.size();i++)
+ {
+
+ if (videoH264filterlist[i].vmr9tested == true)
+ {
+ if (videoH264filterlist[i].vmr9 == true)
+ {
+ videoH264filterselected = i;
+ break;
+ }
+ else
+ {
+ continue;
+ }
+ }
+ else
+ {
+ IMoniker * moni=NULL;
+ IBindCtx *bindctx=NULL;
+ if (CreateBindCtx(0,&bindctx)!=S_OK) return NULL;
+ LPCOLESTR name=(LPCOLESTR)new WCHAR[strlen(videoH264filterlist[i].displayname)+1];
+ mbstowcs((wchar_t*)name,videoH264filterlist[i].displayname,strlen(videoH264filterlist[i].displayname)+1);
+ ULONG eater=0;
+ Log::getInstance()->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
+
+
+
+
-/*\r
- Copyright 2004-2005 Chris Tallon\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#ifndef VIDEOWIN_H\r
-#define VIDEOWIN_H\r
-\r
-#include <stdio.h>\r
-#include <string.h>\r
-#include <winsock2.h>\r
-#include <dshow.h>\r
-#include <d3d9.h>\r
-#include <vmr9.h>\r
-#include <vector>\r
-\r
-#include "defines.h"\r
-#include "video.h"\r
-\r
-//#define DS_DEBUG\r
-#define NEW_DS_MECHANISMENS\r
-\r
-#ifdef NEW_DS_MECHANISMENS\r
-struct VideoFilterDesc {\r
- char * displayname;\r
- char * friendlyname;\r
- bool vmr9;\r
- bool vmr9tested;\r
-};\r
-using namespace std;\r
-typedef vector<VideoFilterDesc> VideoFilterDescList;\r
-#endif\r
-\r
-class DsSourceFilter;\r
-class DsAllocator;\r
-\r
-class VideoWin : public Video\r
-{\r
-public:\r
- VideoWin();\r
- virtual ~VideoWin();\r
-\r
- int init(UCHAR format);\r
- int shutdown();\r
-\r
- int setFormat(UCHAR format);\r
- UCHAR getSupportedFormats() { return 0;};\r
- UINT supportedTVsize() { return ASPECT4X3|ASPECT16X9;};\r
- UCHAR supportedTVFormats() { return 0;};\r
-\r
- int setConnection(UCHAR connection);\r
- int setAspectRatio(UCHAR aspectRatio); // This one does the pin 8 scart widescreen switching\r
- UCHAR getAspectRatio(){return aspectRatio;};\r
- UCHAR getMode(){return mode;};\r
- UCHAR getPseudoTVsize() {return pseudotvsize;};\r
- int setMode(UCHAR mode);\r
- int setTVsize(UCHAR size); // Is the TV a widescreen?\r
- int setDefaultAspect();\r
- int setSource();\r
- int setPosition(int x, int y);\r
- int sync();\r
- int play();\r
- int dsplay();\r
- bool InIframemode() {return iframemode;};\r
- int stop();\r
- int dsstop();\r
- int pause();\r
- int dspause();\r
- int unPause();\r
- int dsunPause();\r
- int fastForward();\r
- int unFastForward();\r
- int reset();\r
- int dsreset();\r
- int blank();\r
- int signalOn();\r
- int signalOff();\r
- int attachFrameBuffer(); // What does this do?\r
-// ULONG timecodeToFrameNumber(ULLONG timecode);\r
-// ULLONG frameNumberToTimecode(ULONG framenumber);\r
- ULLONG getCurrentTimestamp();\r
-\r
- bool loadOptionsfromServer(VDR* vdr);\r
- bool saveOptionstoServer();\r
- bool addOptionPagesToWTB(WTabBar *wtb);\r
- bool addOptionsToPanes(int panenumber,Options *options,WOptionPane* pane);\r
- bool handleOptionChanges(Option* option);\r
-\r
- //Writing Data to Videodevice\r
- virtual void PrepareMediaSample(const MediaPacketList&, UINT samplepos);\r
- virtual UINT DeliverMediaSample(UCHAR* buffer, UINT *samplepos);\r
- UINT DeliverMediaPacket(const MediaPacket packet, const UCHAR* buffer, UINT *samplepos);\r
- virtual bool dtsTimefix() {if (h264)return videoH264dtsfix; else return videompeg2dtsfix;}\r
-\r
- virtual bool supportsh264();\r
- virtual int getTeletextBufferFaktor(){return 4;};\r
-\r
-\r
- virtual bool supportsAc3();\r
-\r
- enum VideoPresenter {\r
- VMR9,\r
- EVR\r
- } ;\r
-\r
-\r
-\r
-private:\r
- MediaPacket mediapacket;\r
-public:\r
-\r
- int getCurrentAudioMediaSample(IMediaSample** ms);\r
- int DeliverAudioMediaSample();\r
-\r
- int getCurrentVideoMediaSample(IMediaSample** ms);\r
- int DeliverVideoMediaSample();\r
- int setAudioStreamType(UCHAR type);\r
-\r
- virtual long long SetStartOffset(long long curreftime, bool *rsync);\r
- long long SetStartAudioOffset(long long curreftime, bool *rsync);\r
- virtual void ResetTimeOffsets();\r
-\r
- void SetAudioState(bool state){audioon=state;};\r
- void SetAudioVolume(long volume);\r
-\r
- void turnVideoOn(){videoon=true;};\r
- void turnVideoOff(){videoon=false;};\r
-\r
- virtual bool displayIFrame(const UCHAR* buffer, UINT length);\r
-\r
- unsigned int getPosx() {return videoposx;};\r
- unsigned int getPosy() {return videoposy;};\r
- bool isVideoOn() {return videoon;};\r
- bool isdsinited() {return dsinited;};\r
- int lastAType() {return lastaudiomode;};\r
- bool changeAType(int type,IMediaSample* ms);\r
-\r
-\r
- const VideoFilterDescList *getVideoFilterList(int &selected);\r
- bool selectVideoFilter(int filter);\r
- DsSourceFilter* getSourceFilter() {return sourcefilter;};\r
-\r
- const VideoFilterDescList *getVideoH264FilterList(int &selected);\r
- bool selectVideoH264Filter(int filter);\r
-\r
-\r
-#ifdef DEV\r
- int test();\r
- int test2();\r
-#endif\r
-private:\r
- int EnterIframePlayback();\r
-#ifdef NEW_DS_MECHANISMENS\r
- void dstest(); \r
- void initFilterDatabase();\r
- IBaseFilter *getVideoFilter(); \r
- VideoFilterDescList videofilterlist;\r
- int videofilterselected;\r
-\r
- void initH264FilterDatabase();\r
- IBaseFilter *getVideoH264Filter(); \r
- VideoFilterDescList videoH264filterlist;\r
- int videoH264filterselected;\r
- bool videoH264dtsfix;\r
- bool videompeg2dtsfix;\r
-#endif\r
- int dsInitVideoFilter();\r
- IMediaControl* dsmediacontrol;\r
-\r
- IGraphBuilder* dsgraphbuilder;\r
- IMediaSample* cur_audio_media_sample;\r
- IMediaSample* cur_video_media_sample;\r
- IBaseFilter* dsrenderer;\r
- IVMRSurfaceAllocatorNotify9 *dsvmrsurfnotify;\r
- IReferenceClock *dsrefclock;\r
- IMediaFilter* dsmediafilter;\r
- IBasicAudio* dsbasicaudio;\r
- REFERENCE_TIME cr_time;\r
-\r
- DsSourceFilter* sourcefilter;\r
- DsAllocator* allocatorvmr;\r
- HANDLE filtermutex;\r
- void CleanupDS();\r
- bool offsetnotset;\r
- bool offsetvideonotset;\r
- bool offsetaudionotset;\r
- long long startoffset;\r
- long long lastrefvideotime;\r
- long long lastrefaudiotime;\r
- bool dsinited;\r
- bool firstsynched;\r
- bool audioon;\r
- bool videoon;\r
- bool iframemode;\r
- UCHAR pseudotvsize;\r
- REFERENCE_TIME lastreftimeRT;\r
- ULLONG lastreftimePTS;\r
- unsigned int videoposx;\r
- unsigned int videoposy;\r
- int lastaudiomode;\r
- int audiovolume;\r
- UCHAR aud_type;\r
- unsigned int vmrdeinterlacing; \r
- VideoPresenter currentpresenter;\r
-#ifdef DS_DEBUG\r
- DWORD graphidentifier;\r
-#endif\r
-};\r
-\r
-#endif\r
-\r
-\r
-\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.
+*/
+
+#ifndef VIDEOWIN_H
+#define VIDEOWIN_H
+
+#include <stdio.h>
+#include <string.h>
+#include <winsock2.h>
+#include <dshow.h>
+#include <d3d9.h>
+#include <vmr9.h>
+#include <vector>
+
+#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<VideoFilterDesc> VideoFilterDescList;
+#endif
+
+class DsSourceFilter;
+class DsAllocator;
+
+class VideoWin : public Video
+{
+public:
+ VideoWin();
+ virtual ~VideoWin();
+
+ int init(UCHAR format);
+ int shutdown();
+
+ int setFormat(UCHAR format);
+ UCHAR getSupportedFormats() { return 0;};
+ UINT supportedTVsize() { return ASPECT4X3|ASPECT16X9;};
+ UCHAR supportedTVFormats() { return 0;};
+
+ 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 int getTeletextBufferFaktor(){return 4;};
+
+
+ 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
+
+
+
-/*\r
- Copyright 2004-2005 Chris Tallon, Andreas Vogel\r
-\r
- This file is part of VOMP.\r
-\r
- VOMP is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- VOMP is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with VOMP; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-*/\r
-\r
-#include <time.h>\r
-\r
-#include "vmediaview.h"\r
-\r
-#include "vpicturebanner.h"\r
-#include "vcolourtuner.h"\r
-#include "audioplayer.h"\r
-#include "timers.h"\r
-#include "boxx.h"\r
-#include "wselectlist.h"\r
-#include "remote.h"\r
-#include "wsymbol.h"\r
-#include "boxstack.h"\r
-#include "vdr.h"\r
-#include "media.h"\r
-#include "video.h"\r
-#include "vinfo.h"\r
-#include "i18n.h"\r
-#include "message.h"\r
-#include "command.h"\r
-#include "mediaoptions.h"\r
-#include "mediaplayer.h"\r
-#include "log.h"\r
-\r
-const int VMediaView::EVENT_SLIDESHOW=100;\r
-const int VMediaView::EVENT_DRAWINGDONE=101;\r
-const int VMediaView::EVENT_DRAWINGERROR=102;\r
-const int VMediaView::EVENT_DIRECTORYDONE=103;\r
-\r
-\r
-/**\r
- * the combined user interface for pictures and audio\r
- * has 2 surfaces to enable drawing in a separate thread\r
- * the info display can either show a picture info or and audio info\r
- * if the audio player comes on top it disables the picture info display\r
- * basically we have 3 modes:\r
- * - HIDDEN viewer is hidden (pictureEnabled=false, audioEnabled=false)\r
- * if justPlaying=true the audioplayer is still running\r
- * - PICTURE picture viewer on top ("slide show") - pictureEnabled=true, audioEnabled=false\r
- * no AudioBanner visible\r
- * if justPlaying=true the audio player runs in bg\r
- * - AUDIO audioPlayer on top ("playlist mode") - pictureEnabled=true, audioEnabled=true\r
- * the picture viewer is currently halted (should be able to continue if no AudioInfo)\r
- * no picture banner (we should have an audio banner to indicate the audio player on top!)\r
- * state transitions (via setPictureMode, setAudioMode)\r
- * - show Picture -> pictureEnabled=true, \r
- * only called from command handler if audioEnabled=false\r
- * call from mediaList only possible if audioEnabled=false\r
- * *** TODO: would be better to have separate function for external call/internal call\r
- * - play [Audio] -> if activate is set -> audioEnabled=true (-> Mode AUDIO)\r
- * used for calls from medialist\r
- * internal calls do not set activate!\r
- * - YELLOW key - toggle\r
- * if audioActive==true -> audioActive=false (new mode depends on pictureEnabled - either HIDDEN or PICTURE)\r
- * if audioActive==false -> audioActive=true (new mode AUDIO)\r
- * *** open handling if no audio file there (ignore key???) - currently empty player\r
- * - BACK if mode AUDIO -> audioEnabled=false, justPlaying=false\r
- * - BACK if mode PICTURE -> pictureEnabled=false;\r
- * - player StreamEnd -> audioEnabled=false (new mode depends on pciture - either HIDDEN or PICTURE)\r
- * info/banner handling:\r
- * - AudioInfo - alternativ to pictureInfo\r
- * off when disabling audio or after timer or with OK in AUDIO mode\r
- * on currently any command or player event (end/new song) when audioEnabled==true and retriggerAudioInfo=true\r
- * on on OK in AUDIO mode\r
- * - Picture Info\r
- * off when disabling Picture or after timer, OK, green\r
- * on only on green when pictureEnabled==true\r
- * - PictureBanner\r
- * 2 modes: loading/normal\r
- * normal:\r
- * off when disabling picture, OK, after timer\r
- * on when enabling picture, with OK\r
- * loading:\r
- * off when disabling picture and when loading done\r
- * on on start loading\r
- * *** open: do not show when audio enabled and slide show running (currently slideshow is paused)\r
- * - AudioBanner\r
- * always shown when audioEnabled=true\r
- * on when enabling Audio\r
- * off when disabling audio\r
- * update in play\r
- * timers: 1 - slide show\r
- * 2 - pictureBanner\r
- * 3 - info showtime\r
- * 4 - audio short update\r
- */\r
-\r
-#define DRAWING_THREAD\r
-\r
-DrawStyle VMediaView::pictureBack=DrawStyle(140,140,140);\r
-DrawStyle VMediaView::infoBack=DrawStyle(110,110,110);\r
-DrawStyle VMediaView::audioBannerBack=DrawStyle(110,110,110,20);\r
-//the jpeg reader\r
-\r
-//how long do we wait for a single picture chunk\r
-//the unit is 100ms (the timeout on the server side)\r
-#define MAXTRY 100 \r
-class VPreader : public JpegReader {\r
- private:\r
- ImageReader *reader;\r
- VMediaView * parent;\r
- ULLONG size;\r
- bool dobreak;;\r
- public:\r
- VPreader(VMediaView *p,ImageReader *r){ \r
- parent=p;\r
- size=0;\r
- reader=r;\r
- dobreak=false;\r
- };\r
- virtual ULONG readChunk(ULONG offset,ULONG len,char ** buf) {\r
- Log::getInstance()->log("VMediaView::jpegReader", Log::DEBUG, "read chunk o=%d,len=%d,buf=%p",\r
- offset,len,*buf);\r
- UINT numrec=0;\r
- int rt=0;\r
- for (int trycount=0;trycount < MAXTRY && ! dobreak && numrec == 0 && rt == 0;trycount++) {\r
- if (dobreak) {\r
- *buf=NULL;\r
- rt=-1;\r
- }\r
- else {\r
- rt=reader->getImageChunk((ULLONG)offset,(UINT)len,&numrec,(UCHAR **)buf);\r
- }\r
- }\r
- Log::getInstance()->log("VMediaView::jpegReader", Log::DEBUG, "got n=%d,buf=%p,rt=%d",\r
- numrec,*buf,rt);\r
- return numrec;\r
- }\r
- virtual int initRead(const MediaURI *uri,ULLONG *sz,ULONG factor=100) {\r
- Log::getInstance()->log("VMediaView::jpegReader", Log::DEBUG, "load image %s",uri->getDisplayName());\r
- Video* video = Video::getInstance();\r
- dobreak=false;\r
- int rt=MediaPlayer::getInstance()->openMedium(1,uri,sz,video->getScreenWidth()*factor/100, video->getScreenHeight()*factor/100);\r
- if (rt < 0) *sz=0;\r
- size=*sz;\r
- Log::getInstance()->log("VMediaView::jpegReader", Log::DEBUG, "load image %s returned %llu",uri->getDisplayName(),size);\r
- return rt;\r
- }\r
- virtual ULONG getSize() {return size;}\r
- //seems to be thread safe (more or less...)\r
- void setBreak() {\r
- dobreak=true;\r
- }\r
-};\r
-\r
-/**\r
- * a separate thread for drawing pictures\r
- * will be started with the sfc and the draw control\r
- * will send a player event when done\r
- */\r
-class DrawingThread : public Thread_TYPE {\r
- private:\r
- VMediaView *_parent;\r
- VPreader * _reader;\r
- WJpegComplex::JpegControl *_ctl;\r
- Surface *_sfc;\r
- DrawStyle _colour;\r
- bool _interrupted;\r
- public:\r
- DrawingThread(VMediaView *parent) {\r
- _parent=parent;\r
- _reader=NULL;\r
- _ctl=NULL;\r
- _sfc=NULL;\r
- _interrupted=false;\r
- }\r
- virtual ~DrawingThread(){}\r
- bool isRunning() {\r
- return (threadIsActive()!=0);\r
- }\r
- bool start(WJpegComplex::JpegControl *ctl,Surface *sfc,VPreader *reader,DrawStyle &c) {\r
- if (isRunning()) {\r
- return false;\r
- }\r
- _interrupted=false;\r
- _ctl=ctl;\r
- _sfc=sfc;\r
- _reader=reader;\r
- _colour=c;\r
- return (threadStart() == 1);\r
- }\r
- bool stop() {\r
- Log::getInstance()->log("DrawingThread",Log::DEBUG,"stop initiated");\r
- if (threadIsActive()) {\r
- _interrupted=true;\r
- if (_reader) _reader->setBreak();\r
- threadStop();\r
- }\r
- Log::getInstance()->log("DrawingThread",Log::DEBUG,"stop done");\r
- _reader=NULL;\r
- return true;\r
- }\r
- protected:\r
- virtual void threadMethod() {\r
- Log::getInstance()->log("DrawingThread",Log::DEBUG,"started");\r
- bool rt=WJpegComplex::drawJpeg(_ctl,_sfc,_reader,_colour);\r
- _reader=NULL;\r
- if (! _interrupted) {\r
- Message* m = new Message(); \r
- //we misuse PLAYER_EVENT here\r
- m->message = Message::PLAYER_EVENT;\r
- m->to = _parent;\r
- m->from = _parent;\r
- m->parameter= rt?VMediaView::EVENT_DRAWINGDONE:VMediaView::EVENT_DRAWINGERROR;\r
- Command::getInstance()->postMessageFromOuterSpace(m);\r
- }\r
- Log::getInstance()->log("DrawingThread",Log::DEBUG,"finishing interrupt=%d",(int)_interrupted);\r
- }\r
- virtual void threadPostStopCleanup() {}\r
-\r
-};\r
-\r
-\r
-VMediaView::VMediaView(VMediaList *p)\r
-{\r
- Log::getInstance()->log("VMediaView::VMediaView", Log::DEBUG, "p=%p", this);\r
- audioEnabled=false;\r
- pictureEnabled=false;\r
- parent=p;\r
- ireader=new ImageReader(1,MediaPlayer::getInstance());\r
- reader=new VPreader(this,ireader);\r
- //the surface handling\r
- Video* video = Video::getInstance();\r
- setSize(video->getScreenWidth(), video->getScreenHeight());\r
- createBuffer();\r
- sfc2=getSurface();\r
- surface=NULL;\r
- //create the second surface\r
- createBuffer();\r
- sfc1=getSurface();\r
- originalh=area.h;\r
- originalw=area.w;\r
- //disable the surface\r
- area.h=1;\r
- area.w=1;\r
- //banners and infos\r
- pictureBanner=NULL;\r
- slideshow=false;\r
- pictureError=NULL;\r
- currentPicture=NULL;\r
- info=NULL;\r
- pictureLoading=false;\r
-\r
- //picture settings\r
- showtime=INITIAL_SHOWTIME;\r
- rotate=WJpegComplex::ROT_0;\r
- currentScale=1;\r
- options=MediaOptions::getInstance();\r
- VColourTuner::initFactors();\r
- int st=options->getIntOption("SlideShowInterval");\r
- if (st > 0) showtime=st;\r
- ctl.area.w=originalw;\r
- ctl.area.h=originalh;\r
- ctl.enlarge=false;\r
- ctl.scaleafter=options->getIntOption("ScaleFactor");\r
- const char * mode=options->getStringOption("PictureMode");\r
- if (strcmp(mode,"clip") == 0) ctl.mode=WJpegComplex::CROP;\r
- else if (strcmp(mode,"letter") == 0) ctl.mode=WJpegComplex::LETTER;\r
- else if (strcmp(mode,"clipfactor") == 0) ctl.mode=WJpegComplex::CROPPERCENT;\r
- ctl.scaleAmount=options->getIntOption("PictureSize");\r
- if (ctl.scaleAmount < 10) ctl.scaleAmount=10;\r
- if (ctl.scaleAmount > 200) ctl.scaleAmount=200;\r
- ctl2=ctl;\r
- cropmode=ctl.mode;\r
- //current control is the one used for DISPLAY (not the one for drawing - this is the other one)\r
- //this is closely coupled to the surface - i.e. ctl is for sfc1, ctl2 for sfc2\r
- currentControl=&ctl;\r
- bannerEnabled=true;\r
- pictureShowing=false;\r
-\r
- //audio player\r
-\r
- barBlue.set(0, 0, 150, 150);\r
- audioError=NULL;\r
- currentAudio=NULL;\r
- justPlaying=false;\r
- playall=false;\r
- retriggerAudioInfo=false;\r
- audioBanner=NULL;\r
- drawingThread=new DrawingThread(this);\r
-}\r
-\r
-VMediaView::~VMediaView()\r
-{\r
- Log::getInstance()->log("VMediaView::~VMediaView", Log::DEBUG, "p=%p,secondSfc=%s", this,(secondSurface()?"true":"false"));\r
- destroyPictureBanner();\r
- if (currentPicture) delete currentPicture;\r
- Timers::getInstance()->cancelTimer(this,1);\r
- Timers::getInstance()->cancelTimer(this,2);\r
- Timers::getInstance()->cancelTimer(this,3);\r
- Timers::getInstance()->cancelTimer(this,4);\r
- Timers::getInstance()->cancelTimer(this,5);\r
- destroyInfo();\r
- destroyAudioBanner();\r
- if (getPlayer(false)) {\r
- AudioPlayer::getInstance(NULL,false)->shutdown();\r
- }\r
- if (currentAudio) delete currentAudio;\r
- drawingThread->stop();\r
- ireader->shutdown();\r
- delete ireader;\r
- delete reader;\r
- if (secondSurface()) {\r
- delete sfc1;\r
- sfc1=NULL;\r
- }\r
- else {\r
- delete sfc2;\r
- sfc2=NULL;\r
- }\r
- MediaPlayer::getInstance()->closeMediaChannel(1);\r
- MediaPlayer::getInstance()->closeMediaChannel(2);\r
- Log::getInstance()->log("VMediaView::~VMediaView", Log::DEBUG, "done p=%p", this);\r
-}\r
-\r
-void VMediaView::setPictureMode(bool act) {\r
- Log::getInstance()->log("VMediaView", Log::DEBUG, "set pictureMode %d p=%d, a=%d", (int)act,(int)pictureEnabled,(int)audioEnabled);\r
- if ( act) {\r
- if ( ! pictureEnabled && ! audioEnabled) {\r
- area.h=originalh;\r
- area.w=originalw;\r
- showPictureBanner();\r
- parent->updateAll();\r
- }\r
- if (! pictureEnabled) {\r
- //we newly enable the picture - so clear the screen\r
- draw();\r
- BoxStack::getInstance()->update(this);\r
- if (slideshow) startSlideshow();\r
- }\r
- }\r
- else \r
- {\r
- if ( pictureEnabled) {\r
- destroyPictureBanner();\r
- stopSlideshow(false);\r
-#ifdef DRAWING_THREAD\r
- drawingThread->stop();\r
-#endif\r
- if (! audioEnabled) {\r
- destroyInfo();\r
- area.w=1;\r
- area.h=1;\r
- draw();\r
- parent->updateAll();\r
- }\r
- }\r
- pictureShowing=false;\r
- }\r
- pictureEnabled=act;\r
-}\r
-\r
-//we can disable audio without automatically hiding us\r
-//this will become strange - we are visible but do not handle\r
-//keys - so if you call setAudioMode(false,false) be sure\r
-//to call setAudioMode(false,true) afterwards\r
-//it is only here to give the list a chance to start a new play\r
-//when we call directoryDone\r
-void VMediaView::setAudioMode(bool act,bool doHiding) {\r
- Log::getInstance()->log("VMediaView", Log::DEBUG, "setAudioMode %d p=%d, a=%d", (int)act,(int)pictureEnabled,(int)audioEnabled);\r
- if ( act) {\r
- if (! audioEnabled) {\r
- if (! pictureEnabled) {\r
- area.w=originalw;\r
- area.h=originalh;\r
- draw(); //empty screen if no picture\r
- parent->updateAll();\r
- }\r
- destroyPictureBanner();\r
-\r
- }\r
- audioEnabled=true;\r
- showAudioBanner();\r
- showAudioInfo();\r
- }\r
- else \r
- {\r
- if ( audioEnabled) {\r
- audioEnabled=false;\r
- destroyInfo();\r
- destroyAudioBanner();\r
- }\r
- if (! pictureEnabled && doHiding) {\r
- area.w=1;\r
- area.h=1;\r
- draw();\r
- parent->updateAll();\r
- }\r
- else {\r
- //picture now on top\r
- if (havePictureBanner && ! pictureBanner) showPictureBanner();\r
- }\r
- }\r
-}\r
-\r
-\r
-\r
-void VMediaView::draw()\r
-{\r
- Log::getInstance()->log("VMediaView::draw", Log::DEBUG, "pictureError=%s,p=%p", pictureError,this);\r
- \r
- if (pictureShowing ) fillColour(pictureBack);\r
- else fillColour(DrawStyle::BLACK);\r
- if (pictureError) {\r
- drawText(pictureError,100,area.h/2,DrawStyle::LIGHTTEXT);\r
- return;\r
- }\r
-}\r
-\r
-\r
-int VMediaView::handleCommand(int command)\r
-{\r
- Log::getInstance()->log("VMediaView::handleCommand", Log::DEBUG, "cmd=%d,p=%p", command,this);\r
- if ( !audioEnabled && ! pictureEnabled ) {\r
- //only handle YELLOW\r
- if (command == Remote::YELLOW) {\r
- setAudioMode(true);\r
- return 1;\r
- }\r
- return 0;\r
- }\r
- if ( ! audioEnabled) {\r
- //------------------------- command in mode PICTURE (i.e. picture is on top) ----------------\r
- //picture on top\r
- int rt=1;\r
- switch(command)\r
- {\r
- case Remote::DF_UP:\r
- case Remote::UP:\r
- case Remote::SKIPBACK:\r
- rotate=WJpegComplex::ROT_0;\r
- showPicture(VMediaList::MV_PREV,slideshow,true);\r
- rt= 2;\r
- break;\r
- case Remote::FORWARD:\r
- if (showtime > 1) showtime--;\r
- updatePictureBanner(true);\r
- break;\r
- case Remote::DF_DOWN:\r
- case Remote::DOWN:\r
- case Remote::SKIPFORWARD:\r
- rotate=WJpegComplex::ROT_0;\r
- showPicture(VMediaList::MV_NEXT,slideshow,true);\r
- rt= 2;\r
- break;\r
- case Remote::REVERSE:\r
- if (showtime < 50 ) showtime++;\r
- updatePictureBanner(true);\r
- break;\r
- case Remote::OK:\r
- {\r
- if (pictureBanner) {\r
- destroyPictureBanner();\r
- destroyInfo();\r
- havePictureBanner=false;\r
- }\r
- else {\r
- havePictureBanner=true;\r
- showPictureBanner(pictureLoading);\r
- }\r
- rt= 2;\r
- }\r
- break;\r
- case Remote::PLAY:\r
- {\r
- slideshow=true;\r
- rotate=WJpegComplex::ROT_0;\r
- showPicture(VMediaList::MV_NEXT,slideshow,true);\r
- rt= 2;\r
- }\r
- break;\r
- case Remote::PAUSE:\r
- if (slideshow) {\r
- stopSlideshow(true);\r
- updatePictureBanner();\r
- }\r
- else {\r
- slideshow=true;\r
- rotate=WJpegComplex::ROT_0;\r
- showPicture(VMediaList::MV_NEXT,slideshow,true);\r
- }\r
- rt= 2;\r
- break;\r
- case Remote::STOP:\r
- stopSlideshow(true);\r
- showtime=INITIAL_SHOWTIME;\r
- updatePictureBanner();\r
- rt= 2;\r
- break;\r
- case Remote::RED:\r
- switch(rotate) {\r
- case WJpegComplex::ROT_0:\r
- rotate=WJpegComplex::ROT_90;\r
- break;\r
- case WJpegComplex::ROT_90:\r
- rotate=WJpegComplex::ROT_180;\r
- break;\r
- case WJpegComplex::ROT_180:\r
- rotate=WJpegComplex::ROT_270;\r
- break;\r
- case WJpegComplex::ROT_270:\r
- rotate=WJpegComplex::ROT_0;\r
- break;\r
- }\r
- showPicture(VMediaList::MV_NONE,slideshow,true);\r
- rt=2;\r
- break;\r
- case Remote::GREEN:\r
- if (info) destroyInfo();\r
- else showPictureInfo();\r
- rt=2;\r
- break;\r
- case Remote::BLUE:\r
- switch (cropmode) {\r
- case WJpegComplex::CROP:\r
- cropmode=WJpegComplex::LETTER;\r
- break;\r
- case WJpegComplex::LETTER:\r
- cropmode=WJpegComplex::CROPPERCENT;\r
- break;\r
- default:\r
- cropmode=WJpegComplex::CROP;\r
- break;\r
- }\r
- showPicture(VMediaList::MV_NONE,slideshow,true);\r
- rt=2;\r
- break;\r
- case Remote::MENU: {\r
- stopSlideshow(true);\r
- destroyPictureBanner();\r
- destroyInfo();\r
- VColourTuner *ct=new VColourTuner();\r
- BoxStack::getInstance()->add(ct);\r
- ct->draw();\r
- BoxStack::getInstance()->update(ct);\r
- rt=2;\r
- \r
- } break;\r
- case Remote::BACK:\r
- {\r
- setPictureMode(false);\r
- rt= 2;\r
- }\r
- break;\r
- case Remote::YELLOW:\r
- {\r
- setAudioMode(true);\r
- }\r
- }\r
- return rt;\r
- } \r
- else \r
- {\r
- int rt=1;\r
- bool updateInfo=false;\r
- //------------------------- command in mode AUDIO (i.e. audio is on top) ----------------\r
- switch(command)\r
- {\r
- case Remote::YELLOW:\r
- setAudioMode(false);\r
- rt=2;\r
- break;\r
- case Remote::DF_UP:\r
- case Remote::UP:\r
- play(playall,false,VMediaList::MV_PREV);\r
- rt= 2;\r
- break;\r
- case Remote::FORWARD:\r
- if (! audioError) getPlayer()->fastForward();\r
- updateInfo=true;\r
- rt=2;\r
- break;\r
- case Remote::DF_DOWN:\r
- case Remote::DOWN:\r
- play(playall,false,VMediaList::MV_NEXT);\r
- rt= 2;\r
- break;\r
- case Remote::SKIPFORWARD:\r
- if (! audioError) getPlayer()->skipForward(10);\r
- rt=2;\r
- break;\r
- case Remote::SKIPBACK:\r
- if (! audioError) getPlayer()->skipBackward(10);\r
- rt=2;\r
- break;\r
- case Remote::REVERSE:\r
- rt=2;\r
- break;\r
- case Remote::ZERO:\r
- if (! audioError) getPlayer()->jumpToPercent(0);\r
- rt=2;\r
- break;\r
- case Remote::ONE:\r
- if (! audioError) getPlayer()->jumpToPercent(10);\r
- rt=2;\r
- break;\r
- case Remote::TWO:\r
- if (! audioError) getPlayer()->jumpToPercent(20);\r
- rt=2;\r
- break;\r
- case Remote::THREE:\r
- if (! audioError) getPlayer()->jumpToPercent(30);\r
- rt=2;\r
- break;\r
- case Remote::FOUR:\r
- if (! audioError) getPlayer()->jumpToPercent(40);\r
- rt=2;\r
- break;\r
- case Remote::FIVE:\r
- if (! audioError) getPlayer()->jumpToPercent(50);\r
- rt=2;\r
- break;\r
- case Remote::SIX:\r
- if (! audioError) getPlayer()->jumpToPercent(60);\r
- rt=2;\r
- break;\r
- case Remote::SEVEN:\r
- if (! audioError) getPlayer()->jumpToPercent(70);\r
- rt=2;\r
- break;\r
- case Remote::EIGHT:\r
- if (! audioError) getPlayer()->jumpToPercent(80);\r
- rt=2;\r
- break;\r
- case Remote::NINE:\r
- if (! audioError) getPlayer()->jumpToPercent(90);\r
- rt=2;\r
- break;\r
- case Remote::OK:\r
- case Remote::GREEN:\r
- {\r
- if (info) {\r
- destroyInfo();\r
- retriggerAudioInfo=false;\r
- }\r
- else {\r
- retriggerAudioInfo=true;\r
- showAudioInfo();\r
- }\r
- if (getPlayer()->getState() == AudioPlayer::S_ERROR) {\r
- if (playall) play(playall,false,VMediaList::MV_NEXT);\r
- }\r
- rt= 2;\r
- }\r
- break;\r
- case Remote::PLAY:\r
- {\r
- if (! audioError) getPlayer()->unpause();\r
- updateInfo=true;\r
- if (getPlayer()->getState() != AudioPlayer::S_ERROR) ;\r
- else if (playall) play(playall,false,VMediaList::MV_NEXT);\r
- rt= 2;\r
- }\r
- break;\r
- case Remote::PAUSE:\r
- if (! audioError) getPlayer()->pause();\r
- updateInfo=true;\r
- rt= 2;\r
- break;\r
- case Remote::STOP:\r
- getPlayer()->stop();\r
- justPlaying=false;\r
- updateInfo=true;\r
- rt= 2;\r
- break;\r
- case Remote::BACK:\r
- {\r
- getPlayer()->stop();\r
- playall=false;\r
- retriggerAudioInfo=false;\r
- justPlaying=false;\r
- setAudioMode(false);\r
- if (! pictureShowing) setPictureMode(false); //could have been delayed\r
- rt= 2;\r
- }\r
- break;\r
- }\r
- if (audioEnabled && updateInfo) updateAudioInfo();\r
- return rt;\r
- }\r
-}\r
-\r
-void VMediaView::processMessage(Message* m)\r
-{\r
- Log::getInstance()->log("VMediaView::processMessage", Log::DEBUG, "cmd=%lu,p=%lu", m->message,m->parameter);\r
- if (m->message == Message::MOUSE_MOVE)\r
- {\r
- ;\r
- }\r
- else if (m->message == Message::MOUSE_LBDOWN)\r
- {\r
- \r
- //check if press is outside this view! then simulate cancel\r
- int x=(m->parameter>>16)-getScreenX();\r
- int y=(m->parameter&0xFFFF)-getScreenY();\r
- if (x<0 || y <0 || x>(int)getWidth() || y>(int)getHeight())\r
- {\r
- BoxStack::getInstance()->handleCommand(Remote::BACK); //simulate cancel press\r
- }\r
- }\r
- else if (m->message = Message::PLAYER_EVENT) {\r
- switch (m->parameter) {\r
- case EVENT_SLIDESHOW:\r
- if (! pictureEnabled) break; //old timer msg...\r
- //if (! audioEnabled) {\r
- if (true) {\r
- if (slideshow) {\r
- rotate=WJpegComplex::ROT_0;\r
- showPicture(VMediaList::MV_NEXT,true,false);\r
- startSlideshow();\r
- }\r
- }\r
- break;\r
- case EVENT_DRAWINGERROR:\r
- drawingDone(true);\r
- break;\r
- case EVENT_DRAWINGDONE:\r
- drawingDone(false);\r
- break;\r
- case EVENT_DIRECTORYDONE:\r
- //just disable audio without (poetntially) hiding us\r
- //this gives the list a chance to decide whether audio\r
- //should be on top afterwards without showing the list\r
- //immediately\r
- setAudioMode(false,false);\r
- parent->directoryDone();\r
- if (! pictureShowing) setPictureMode(false);\r
- if (! justPlaying) setAudioMode(false);\r
- break;\r
- case AudioPlayer::STREAM_ERR:\r
- case AudioPlayer::STREAM_END:\r
- if (playall) play(playall,false,VMediaList::MV_NEXT);\r
- else {\r
- setAudioMode(false);\r
- justPlaying=false;\r
- }\r
- break;\r
- case AudioPlayer::SHORT_UPDATE:\r
- if (info && audioEnabled ) {\r
- drawAudioClocks();\r
- BoxStack::getInstance()->update(info,&clocksRegion);\r
- BoxStack::getInstance()->update(info,&barRegion);\r
- Timers::getInstance()->setTimerD(this, 4, 1);\r
- }\r
- break;\r
- case AudioPlayer::NEW_SONG:\r
- if (audioEnabled) updateAudioInfo();\r
- break;\r
- case AudioPlayer::CONNECTION_LOST:\r
- if (audioEnabled) destroyInfo();\r
- if (AudioPlayer *player=getPlayer(false)) {\r
- player->shutdown();\r
- player=NULL;\r
- }\r
- Command::getInstance()->connectionLost();\r
- break;\r
- }\r
- }\r
-}\r
-\r
-\r
-VMediaView * VMediaView::createViewer(VMediaList * mparent) {\r
- Log::getInstance()->log("VMediaView::createViewer", Log::DEBUG, "p=%p",\r
- mparent);\r
- VMediaView *vmn=new VMediaView(mparent);\r
- BoxStack::getInstance()->add(vmn);\r
- vmn->draw();\r
- BoxStack::getInstance()->update(vmn);\r
- return vmn;\r
-}\r
-\r
-void VMediaView::startSlideshow() {\r
- slideshow=true;\r
- if (! pictureLoading) Timers::getInstance()->setTimerD(this,1,showtime);\r
-}\r
-\r
-void VMediaView::stopSlideshow(bool hard) {\r
- if (hard) slideshow=false;\r
- Timers::getInstance()->cancelTimer(this,1);\r
-}\r
-\r
-\r
-void VMediaView::showPicture(ULONG move,bool bslideshow,bool activateBanner) {\r
- pictureShowing=true;\r
- setPictureMode(true);\r
- stopSlideshow(true);\r
-#ifdef DRAWING_THREAD\r
- drawingThread->stop();\r
-#endif\r
- slideshow=bslideshow;\r
- Media *newPicture=parent->getMedia(MEDIA_TYPE_PICTURE,move);\r
- if ( ! newPicture) {\r
- pictureShowing=false;\r
- if (!audioEnabled) {\r
- //within the event handler first directoryDone is called\r
- //and afterwards everything is stopped if nothing new\r
- sendCommandMsg(EVENT_DIRECTORYDONE);\r
- }\r
- slideshow=false;\r
- }\r
- else \r
- {\r
- pictureShowing=true;\r
- if (currentPicture) {\r
- delete currentPicture;\r
- currentPicture=NULL;\r
- }\r
- currentPicture=newPicture;\r
- loadPicture(currentPicture,activateBanner);\r
- }\r
-}\r
-\r
-//the real picture drawing method\r
-//will start drawing on a new surface and will switch it wehn done\r
-\r
-int VMediaView::loadPicture(Media *md,bool activateBanner) {\r
- pictureError=NULL;\r
- if (! md) return 1;\r
- if (! md->getURI()) {\r
- pictureError=tr("No media found");\r
- Log::getInstance()->log("VMediaView::load",Log::ERR,"no URI in media");\r
- return 1;\r
- }\r
- Log::getInstance()->log("VMediaView::load", Log::DEBUG, "filename=%s,p=%p",\r
- md->getURI()->getName(),this);\r
- //do we have a pictureBanner?\r
- havePictureBanner=pictureBanner!=NULL || activateBanner;\r
- pictureLoading=true;\r
-#ifdef DRAWING_THREAD\r
- if (!audioEnabled && havePictureBanner ) showPictureBanner(true);\r
- drawingThread->stop();\r
-#else\r
- showPictureBanner(true);\r
-#endif\r
- ireader->stop();\r
- ULLONG size=0;\r
- int rtok=reader->initRead(md->getURI(),&size,ctl.scaleAmount); //scaleAmount is the same for both...\r
- if (rtok == 0) {\r
- //now we can really draw\r
- //get the surface for drawing\r
- Surface * drawSurface=NULL;\r
- WJpegComplex::JpegControl *drawCtl=NULL;\r
- getDrawingParam(drawSurface,drawCtl);\r
- drawCtl->error[0]=0;\r
- drawCtl->rotation=rotate;\r
- drawCtl->mode=cropmode;\r
- drawCtl->compressedSize=size;\r
-#ifdef DRAWING_THREAD\r
- bool ok=drawingThread->start(drawCtl,drawSurface,reader,pictureBack);\r
- if (! ok) {\r
- Log::getInstance()->log("VMediaView::load", Log::ERR, "unable to start drawing thread");\r
- pictureError=tr("JpegError");\r
- pictureLoading=false;\r
- destroyPictureBanner();\r
- return 1;\r
- }\r
- return 0;\r
-#else\r
- //here we could hand this over to the drawing thread\r
- bool ok=WJpegComplex::drawJpeg(drawCtl,drawSurface,reader,pictureBack);\r
- drawingDone(!ok);\r
- return ok?0:1;\r
-#endif\r
- }\r
- pictureLoading=false;\r
- return 1;\r
-}\r
-\r
-void VMediaView::drawingDone(bool hasError) {\r
- pictureLoading=false;\r
- destroyPictureBanner(); //disable loading indication (or real banner)\r
- if (! hasError) {\r
- switchSurface();\r
- Log::getInstance()->log("VMediaView::drawingDone", Log::DEBUG, "success: sfc now=%p",surface);\r
- pictureError=NULL;\r
- //only show the pictureBanner if it was there before\r
- if (havePictureBanner && ! audioEnabled) showPictureBanner();\r
- Log::getInstance()->log("VMediaView::load", Log::DEBUG, "success" );\r
- updatePictureInfo(); //will only do somethng if pictureEnabled\r
- }\r
- else \r
- {\r
- pictureError=tr("JpegError");\r
- if (pictureEnabled) {\r
- draw();\r
- destroyInfo();\r
- }\r
- }\r
- MediaPlayer::getInstance()->closeMediaChannel(1);\r
-#ifndef DRAWING_THREAD\r
- if (audioEnabled) showAudioBanner();\r
-#endif\r
- if (slideshow) startSlideshow();\r
- BoxStack::getInstance()->update(this);\r
-}\r
-\r
-void VMediaView::showPictureBanner(bool loading) {\r
- //we are in the main thread - so we can (and must) safely hard destroy/create the banner\r
- Timers::getInstance()->cancelTimer(this,2);\r
- if (! currentPicture) {\r
- //hmm...\r
- destroyPictureBanner(false);\r
- return;\r
- }\r
- if (pictureBanner) destroyPictureBanner(false);\r
- if (! pictureEnabled || ! bannerEnabled) return;\r
- pictureBanner= new VPictureBanner(loading, slideshow);\r
- pictureBanner->fillColour(infoBack);\r
- if (! loading) {\r
- int len=strlen(currentPicture->getFileName())+Media::TIMEBUFLEN+20;\r
- char *buf=new char[len];\r
- char tbuf[Media::TIMEBUFLEN];\r
- SNPRINTF(buf,len,"%c%02ds%c %s %s " ,\r
- slideshow?' ':'[',\r
- showtime,\r
- slideshow?' ':']',\r
- currentPicture->getTimeString(tbuf),\r
- currentPicture->getFileName()\r
- );\r
- pictureBanner->setText(buf);\r
- delete [] buf;\r
- }\r
- else {\r
- char *buf=new char[strlen(currentPicture->getDisplayName())+50];\r
- SNPRINTF(buf,50,"%s %s",tr("Loading"), currentPicture->getDisplayName());\r
- pictureBanner->setText(buf);\r
- delete buf;\r
- }\r
- pictureBanner->draw();\r
- if (! loading ) Timers::getInstance()->setTimerD(this,2,8);\r
- BoxStack::getInstance()->add(pictureBanner);\r
- BoxStack::getInstance()->update(pictureBanner);\r
- }\r
-\r
-void VMediaView::destroyPictureBanner(bool fromTimer) {\r
- if (pictureBanner) {\r
- if (fromTimer) sendViewMsg(pictureBanner); \r
- else BoxStack::getInstance()->remove(pictureBanner);\r
- pictureBanner=NULL;\r
- if (! fromTimer) Timers::getInstance()->cancelTimer(this,2);\r
- }\r
-}\r
-void VMediaView::updatePictureBanner(bool loading) {\r
- if (pictureBanner ) {\r
- showPictureBanner(loading);\r
- }\r
-}\r
-void VMediaView::timercall(int clientref) {\r
- Log::getInstance()->log("VMediaView::timercall", Log::DEBUG, "id=%d",clientref);\r
- switch(clientref)\r
- {\r
- case 4:\r
- {\r
- sendCommandMsg(AudioPlayer::SHORT_UPDATE);\r
- break;\r
- }\r
- case 3:\r
- {\r
- Log::getInstance()->log("VMediaView::timercall", Log::DEBUG, "infoEnd");\r
- destroyInfo(true);\r
- if (audioEnabled) {\r
- //we only did show the audio error info if audio is enabled\r
- bool stillError=false;\r
- if (AudioPlayer * player=getPlayer(false)) {\r
- stillError=player->getState()==AudioPlayer::S_ERROR;\r
- }\r
- if (stillError) {\r
- sendCommandMsg(AudioPlayer::STREAM_END);\r
- }\r
- }\r
- break;\r
- }\r
- case 1:\r
- if (! slideshow) return;\r
- Log::getInstance()->log("VMediaView::timercall", Log::DEBUG, "slideshow");\r
- sendCommandMsg(EVENT_SLIDESHOW);\r
- break;\r
- case 2:\r
- Log::getInstance()->log("VMediaView::timercall", Log::DEBUG, "pictureBannerEnd");\r
- destroyPictureBanner(true);\r
- break;\r
- }\r
- \r
- }\r
-\r
-#define INFOBUF 2000\r
-void VMediaView::showPictureInfo(){\r
- if (! pictureEnabled || audioEnabled) return;\r
- if (info) destroyInfo();\r
- if (! currentPicture) return;\r
-\r
- info=new VInfo();\r
- info->setTitleText(currentPicture->getFileName());\r
- info->setDropThrough();\r
- info->setSize(500, 300);\r
- info->createBuffer();\r
- info->setBorderOn(1);\r
- info->setTitleBarOn(1);\r
-\r
- if (Video::getInstance()->getFormat() == Video::PAL)\r
- info->setPosition(100, 180);\r
- else\r
- info->setPosition(100, 150);\r
- char buf[INFOBUF];\r
- char tbuf[Media::TIMEBUFLEN];\r
- //modes should come from mediaoptions...\r
- const char *mode=NULL;\r
- switch (currentControl->mode) {\r
- case WJpegComplex::CROPPERCENT:\r
- mode="clipfactor";\r
- break;\r
- case WJpegComplex::LETTER:\r
- mode="letter";\r
- break;\r
- default:\r
- mode="clip";\r
- break;\r
- }\r
- const char *dirname=parent->getDirname(MEDIA_TYPE_PICTURE);\r
- int ldir=0;\r
- const char *prfx="";\r
- if (dirname) ldir=strlen(dirname);\r
- if (ldir > 35){\r
- dirname=dirname+ldir-35;\r
- prfx="...";\r
- }\r
- 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",\r
- tr("Directory"), prfx,dirname,\r
- tr("Format(px)"),currentControl->picw,currentControl->pich,\r
- tr("Filesize"),currentControl->compressedSize/1000,\r
- tr("Time"),currentPicture->getTimeString(tbuf),\r
- tr("Rotation"),90*currentControl->finalRotation,\r
- tr("Scale"),currentControl->scale,\r
- tr("Picture Mode"),mode );\r
- info->setMainText(buf);\r
- info->draw();\r
- BoxStack::getInstance()->add(info);\r
- BoxStack::getInstance()->update(info);\r
- Timers::getInstance()->setTimerD(this,3,8);\r
-}\r
-void VMediaView::updatePictureInfo(){\r
- if (info) {\r
- showPictureInfo();\r
- }\r
-}\r
-void VMediaView::destroyInfo(bool fromTimer){\r
- if (info) {\r
- if (! fromTimer) {\r
- Log::getInstance()->log("VMediaView",Log::DEBUG,"remove info %p",info);\r
- BoxStack::getInstance()->remove(info);\r
- }\r
- else {\r
- Log::getInstance()->log("VMediaView",Log::DEBUG,"trigger remove info %p",info);\r
- sendViewMsg(info);\r
- }\r
- info=NULL;\r
- }\r
- if (! fromTimer) Timers::getInstance()->cancelTimer(this,3);\r
- if (! fromTimer) Timers::getInstance()->cancelTimer(this,4);\r
-}\r
-\r
-void VMediaView::sendViewMsg(Boxx *v) {\r
- Message* m = new Message(); \r
- m->message = Message::CLOSE_ME;\r
- m->to = BoxStack::getInstance();\r
- m->from = v;\r
- m->parameter=(ULONG)v;\r
- Command::getInstance()->postMessageFromOuterSpace(m);\r
-}\r
-void VMediaView::sendCommandMsg(int command) {\r
- Message* m = new Message(); \r
- //we misuse PLAYER_EVENT here\r
- m->message = Message::PLAYER_EVENT;\r
- m->to = this;\r
- m->from = this;\r
- m->parameter= command;\r
- Command::getInstance()->postMessageFromOuterSpace(m);\r
-}\r
-\r
-void VMediaView::enableBanner(bool enable) {\r
- bannerEnabled=false;\r
- updatePictureBanner();\r
-}\r
-\r
-void VMediaView::getDrawingParam(Surface *&sfc,WJpegComplex::JpegControl *&c){\r
- if (secondSurface()) {\r
- //we currently display on sfc2\r
- sfc=sfc1;\r
- c=&ctl;\r
- }\r
- else {\r
- sfc=sfc2;\r
- c=&ctl2;\r
- }\r
-}\r
-void VMediaView::switchSurface(){\r
- if (secondSurface()) {\r
- //now we switch to sfc1\r
- surface=sfc1;\r
- currentControl=&ctl;\r
- }\r
- else {\r
- surface=sfc2;\r
- currentControl=&ctl2;\r
- }\r
-}\r
-\r
-AudioPlayer * VMediaView::getPlayer(bool createIfNeeded)\r
-{\r
- AudioPlayer* rt=AudioPlayer::getInstance(this,false);\r
- if (! createIfNeeded && rt == NULL) return NULL;\r
- if (rt == NULL) {\r
- rt=AudioPlayer::getInstance(this);\r
- rt->run();\r
- }\r
- return rt;\r
-}\r
-\r
-bool VMediaView::isAudioPlaying() {\r
- Log::getInstance()->log("VMediaView::isPlaying", Log::DEBUG, "rt=%s", justPlaying?"true":"false");\r
- return justPlaying;\r
-}\r
-\r
-\r
-\r
-\r
-\r
-\r
-int VMediaView::play(bool all,bool activate,ULONG move,bool showInfo) {\r
- int rt=0;\r
- if (getPlayer(false)) getPlayer(false)->stop();\r
- justPlaying=false;\r
- if (currentAudio) delete currentAudio;\r
- currentAudio=NULL;\r
- playall=all;\r
- currentAudio=parent->getMedia(MEDIA_TYPE_AUDIO,move);\r
- audioError=NULL;\r
- if ( ! currentAudio || ! currentAudio->getURI()) {\r
- Log::getInstance()->log("VMediaView::load", Log::ERR, "no URI in media");\r
- audioError=tr("no audio file");\r
- if (audioEnabled) sendCommandMsg(EVENT_DIRECTORYDONE);\r
- rt= -1;\r
- }\r
- else {\r
- Log::getInstance()->log("VMediaView::load", Log::DEBUG, "filename=%s,p=%p",\r
- currentAudio->getURI()->getName(),this);\r
- int wseq=getPlayer()->play(currentAudio->getURI());\r
- if (getPlayer()->waitForSequence(5,wseq)<0) {\r
- audioError=tr("unable to open audio file");\r
- rt= -1;\r
- }\r
- else {\r
- justPlaying=true;\r
- rt=0;\r
- }\r
- Log::getInstance()->log("VMediaView", Log::DEBUG, "player started for %s",currentAudio->getURI()->getName());\r
- }\r
- if (activate && ! audioEnabled){\r
- if (showInfo) retriggerAudioInfo=true;\r
- setAudioMode(true);\r
- }\r
- else {\r
- //avoid duplicate creation of banner and info\r
- showAudioBanner();\r
- showAudioInfo();\r
- }\r
- if (activate && (! currentPicture || ! pictureShowing)) draw();\r
- BoxStack::getInstance()->update(this);\r
- return rt;\r
-}\r
-\r
-void VMediaView::showAudioInfo() {\r
- if (! audioEnabled) return;\r
- Timers::getInstance()->cancelTimer(this,4);\r
- Timers::getInstance()->cancelTimer(this,3);\r
- if (info) destroyInfo();\r
- if (! retriggerAudioInfo) return;\r
- drawAudioInfo();\r
- bool playerError=getPlayer()->getState()==AudioPlayer::S_ERROR || audioError;\r
- if (! playerError) Timers::getInstance()->setTimerD(this,3,AUDIOBANNER_TIME);\r
- else Timers::getInstance()->setTimerD(this,3,AUDIOERROR_TIME);\r
- if (! playerError) Timers::getInstance()->setTimerD(this,4, 1);\r
- BoxStack::getInstance()->update(info);\r
- }\r
-\r
-void VMediaView::updateAudioInfo() {\r
- if (info) {\r
- showAudioInfo();\r
- }\r
-}\r
-\r
-void VMediaView::drawAudioInfo(){\r
- Log::getInstance()->log("VMediaView",Log::DEBUG, "draw banner for %p",info);\r
- const char *title=NULL;\r
- char *playerTitle=NULL;\r
- bool playerError=false;\r
- int numlines=0;\r
- int len=0;\r
- int num=0;\r
- const char *pl=tr("Playlist");\r
- const char *first=NULL;\r
- char *playerInfo=NULL;\r
- if (audioError) {\r
- if (getPlayer()->getState() == AudioPlayer::S_PLAY) audioError=NULL;\r
- }\r
- if (! currentAudio && ! audioError) audioError=tr("no audio file");\r
- if (audioError ) {\r
- title=tr("MediaError");\r
- }\r
- else {\r
- playerTitle=getPlayer()->getTitle();\r
- if (playerTitle) title=playerTitle;\r
- else title=currentAudio->getDisplayName();\r
- num=parent->getNumEntries(MEDIA_TYPE_AUDIO,currentAudio->index);\r
- playerError=getPlayer()->getState() == AudioPlayer::S_ERROR;\r
- //1more line for long dirs\r
- numlines=playall?5:4;\r
- }\r
- if (playerError) {\r
- numlines=3;\r
- first=tr("Unable to play audio file");\r
- len=strlen(first)+3;\r
- }\r
- else if (audioError) {\r
- numlines=3;\r
- first=audioError;\r
- len=strlen(first)+3;\r
- }\r
- else {\r
- playerInfo=getPlayer()->getID3Info();drawText(tr("Loading"),5,3,DrawStyle::LIGHTTEXT);\r
- len=strlen(currentAudio->getDisplayName())+strlen(pl)+30+strlen(parent->getDirname(MEDIA_TYPE_AUDIO))+Media::TIMEBUFLEN+110;\r
- if (playerInfo) {\r
- for (UINT i=0;i<strlen(playerInfo);i++) \r
- if (playerInfo[i] == '\n') numlines++;\r
- len+=strlen(playerInfo);\r
- first=playerInfo;\r
- }\r
- else {\r
- first="";\r
- }\r
- }\r
- destroyInfo();\r
- info=new VInfo();\r
- UINT height=numlines*30+60;\r
- UINT vheight=Video::getInstance()->getScreenHeight();\r
- UINT vwidth=Video::getInstance()->getScreenWidth();\r
- if (height > vheight-2*AUDIOBANNER_BOTTOM_MARGIN)\r
- height=vheight-2*AUDIOBANNER_BOTTOM_MARGIN;\r
- info->setSize(vwidth -2*AUDIOBANNER_X_MARGIN, height);\r
- info->createBuffer();\r
- if (Video::getInstance()->getFormat() == Video::PAL)\r
- {\r
- info->setPosition(AUDIOBANNER_X_MARGIN, vheight-height-AUDIOBANNER_BOTTOM_MARGIN);\r
- }\r
- else\r
- {\r
- info->setPosition(AUDIOBANNER_X_MARGIN, vheight-height-AUDIOBANNER_BOTTOM_MARGIN);\r
- }\r
-\r
- info->setTitleBarOn(0);\r
- info->setDropThrough();\r
- //from vvideorec \r
- //set the regions for the closcks and bars on banner\r
- barRegion.x = info->getWidth()-AUDIOBARLEN-20;\r
- barRegion.y = info->getHeight() - 30; // FIXME, need to be - 1? and below?\r
- barRegion.w = info->getWidth()-AUDIOBARLEN+10;\r
- barRegion.h = 30;\r
-\r
- clocksRegion.x = 130;\r
- clocksRegion.y = barRegion.y + 3;\r
- clocksRegion.w = 190;\r
- clocksRegion.h = getFontHeight();\r
- Log::getInstance()->log("VMediaView",Log::DEBUG,"created AudioInfo %p",info);\r
- BoxStack::getInstance()->add(info);\r
- char *buf=new char [len];\r
- if (playerError || audioError) {\r
- SNPRINTF(buf,len,"%s",first);\r
- }\r
- else {\r
- char tbuf[Media::TIMEBUFLEN];\r
- if (playall) {\r
- SNPRINTF(buf,len,"%s\n"\r
- "%s:%s\n"\r
- "%s: %d/%d\n"\r
- "%s:%s\n"\r
- "%s:%s\n",\r
- first,\r
- tr("FileName"),currentAudio->getDisplayName(),\r
- pl,num,parent->getNumEntries(MEDIA_TYPE_AUDIO),\r
- tr("Directory"),parent->getDirname(MEDIA_TYPE_AUDIO),\r
- tr("Time"),currentAudio->getTimeString(tbuf));\r
- }\r
- else {\r
- SNPRINTF(buf,len,"%s\n"\r
- "%s:%s\n"\r
- "%s:%s\n"\r
- "%s:%s\n",\r
- first,\r
- tr("FileName"),currentAudio->getDisplayName(),\r
- tr("Directory"),parent->getDirname(MEDIA_TYPE_AUDIO),\r
- tr("Time"),currentAudio->getTimeString(tbuf));\r
- }\r
- }\r
- Log::getInstance()->log("VMediaView",Log::DEBUG,"info: (%d)%s",strlen(buf),buf);\r
- //now the real drawing functions\r
- info->draw();\r
- //title\r
- info->rectangle(0, 0, info->getWidth(), 30, DrawStyle::TITLEBARBACKGROUND);\r
- info->drawText(title, 5, 5, DrawStyle::LIGHTTEXT);\r
- info->drawPara(buf,5,32,DrawStyle::LIGHTTEXT);\r
- delete [] buf;\r
- if (! audioError) {\r
- WSymbol w;\r
- info->TEMPADD(&w);\r
- int x=10;\r
- int ybottom=info->getHeight();\r
- info->rectangle(0, ybottom - barRegion.h, info->getWidth(), barRegion.h, DrawStyle::TITLEBARBACKGROUND);\r
- bool drawSymbol=true;\r
- switch(getPlayer()->getState()) {\r
- case AudioPlayer::S_PAUSE:\r
- w.nextSymbol = WSymbol::PAUSE;\r
- break;\r
- case AudioPlayer::S_PLAY:\r
- w.nextSymbol = WSymbol::PLAY;\r
- break;\r
- case AudioPlayer::S_DONE:\r
- if (playall) \r
- w.nextSymbol = WSymbol::PLAY;\r
- else\r
- drawSymbol=false;\r
- break;\r
- case AudioPlayer::S_BACK:\r
- w.nextSymbol = WSymbol::FBWD ;\r
- break;\r
- case AudioPlayer::S_FF:\r
- w.nextSymbol = WSymbol::FFWD ;\r
- break;\r
- default:\r
- drawSymbol=false;\r
- break;\r
- }\r
- if (drawSymbol) {\r
- w.setPosition(x, ybottom-24);\r
- w.draw();\r
- }\r
- else {\r
- //stop symbol\r
- info->rectangle(x, ybottom - 23, 18, 16, DrawStyle::LIGHTTEXT);\r
- }\r
- x+=30;\r
- drawAudioClocks();\r
- }\r
- if (playerInfo) delete playerInfo;\r
- if (playerTitle) delete playerTitle;\r
-}\r
-\r
-void VMediaView::drawAudioClocks() {\r
- if (! info || ! audioEnabled) return;\r
- Log::getInstance()->log("VMediaView::drawAudioClocks", Log::DEBUG, "");\r
- //draw clocks and bar\r
- info->rectangle(clocksRegion, DrawStyle::TITLEBARBACKGROUND);\r
-\r
- time_t currentSec = (time_t)(getPlayer()->getCurrentTimes());\r
- time_t lengthSec=(time_t)(getPlayer()->getSonglen());\r
- struct tm cpos;\r
- struct tm slen;\r
-/* gmtime_r(¤tSec,&cpos);\r
- gmtime_r(&lengthSec,&slen);*/\r
- cpos.tm_hour=currentSec/3600;\r
- cpos.tm_min=(currentSec-cpos.tm_hour*3600)/60;\r
- cpos.tm_sec=(currentSec-cpos.tm_hour*3600-cpos.tm_min*60);\r
- slen.tm_hour=lengthSec/3600;;\r
- slen.tm_min=(lengthSec-slen.tm_hour*3600)/60;\r
- slen.tm_sec=(lengthSec-slen.tm_hour*3600-slen.tm_min*60);\r
-\r
- char buffer[100];\r
- if (currentSec >= lengthSec)\r
- {\r
- SNPRINTF(buffer,99, "-:--:-- / -:--:-- %03dk",getPlayer()->getCurrentBitrate()/1000);\r
- }\r
- else\r
- {\r
- 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,\r
- getPlayer()->getCurrentBitrate()/1000);\r
- //Log::getInstance()->log("VMediaView", Log::DEBUG, buffer);\r
- }\r
-\r
- info->drawText(buffer, clocksRegion.x, clocksRegion.y, DrawStyle::LIGHTTEXT);\r
- \r
- // Draw progress bar\r
- int progBarXbase = 0;\r
- int barlen=250;\r
-\r
- info->rectangle(barRegion.x + progBarXbase, barRegion.y + 3, barlen+10, 24, DrawStyle::LIGHTTEXT);\r
- info->rectangle(barRegion.x + progBarXbase + 2, barRegion.y + 5, barlen+6, 20, barBlue);\r
-\r
- if (currentSec > lengthSec) currentSec=lengthSec;\r
- if (lengthSec == 0) return;\r
-\r
- // Draw yellow portion\r
- int progressWidth = (barlen+2) * currentSec / lengthSec;\r
- info->rectangle(barRegion.x + progBarXbase + 4, barRegion.y + 7, progressWidth, 16, DrawStyle::SELECTHIGHLIGHT);\r
-\r
-}\r
-\r
-void VMediaView::showAudioBanner() {\r
- destroyAudioBanner();\r
- if (! audioEnabled) return;\r
- audioBanner=new VInfo();\r
- Log::getInstance()->log("VMediaView",Log::DEBUG,"creating AudioBanner %p", audioBanner);\r
- Video *v=Video::getInstance();\r
- audioBanner->setSize(v->getScreenWidth()-100, 36);\r
- audioBanner->createBuffer();\r
- audioBanner->setPosition(50, v->getScreenHeight()-50);\r
- audioBanner->fillColour(audioBannerBack);\r
- audioBanner->setTitleBarOn(0);\r
- audioBanner->setDropThrough();\r
- if ( ! currentAudio || ! currentAudio->getDisplayName() || audioError) {\r
- audioBanner->drawText(tr("AudioPlayer - not playing"),5,3,DrawStyle::LIGHTTEXT);\r
- }\r
- else {\r
- char * buf=new char[strlen(currentAudio->getDisplayName())+50];\r
- SNPRINTF(buf,50,"%s %s",tr("AudioPlayer"),currentAudio->getDisplayName());\r
- audioBanner->drawText(buf,5,3,DrawStyle::LIGHTTEXT);\r
- delete buf;\r
- }\r
- BoxStack::getInstance()->add(audioBanner);\r
- BoxStack::getInstance()->update(audioBanner);\r
-}\r
-\r
-void VMediaView::destroyAudioBanner() {\r
- if (audioBanner) {\r
- Log::getInstance()->log("VMediaView",Log::DEBUG,"deleting AudioBanner %p",audioBanner);\r
- BoxStack::getInstance()->remove(audioBanner);\r
- audioBanner=NULL;\r
- }\r
-}\r
+/*
+ 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 <time.h>
+
+#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
+
+DrawStyle VMediaView::pictureBack=DrawStyle(140,140,140);
+DrawStyle VMediaView::infoBack=DrawStyle(110,110,110);
+DrawStyle VMediaView::audioBannerBack=DrawStyle(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;
+ WJpegComplex::JpegControl *_ctl;
+ Surface *_sfc;
+ DrawStyle _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(WJpegComplex::JpegControl *ctl,Surface *sfc,VPreader *reader,DrawStyle &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=WJpegComplex::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=WJpegComplex::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=WJpegComplex::CROP;
+ else if (strcmp(mode,"letter") == 0) ctl.mode=WJpegComplex::LETTER;
+ else if (strcmp(mode,"clipfactor") == 0) ctl.mode=WJpegComplex::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(DrawStyle::BLACK);
+ if (pictureError) {
+ drawText(pictureError,100,area.h/2,DrawStyle::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=WJpegComplex::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=WJpegComplex::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=WJpegComplex::ROT_0;
+ showPicture(VMediaList::MV_NEXT,slideshow,true);
+ rt= 2;
+ }
+ break;
+ case Remote::PAUSE:
+ if (slideshow) {
+ stopSlideshow(true);
+ updatePictureBanner();
+ }
+ else {
+ slideshow=true;
+ rotate=WJpegComplex::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 WJpegComplex::ROT_0:
+ rotate=WJpegComplex::ROT_90;
+ break;
+ case WJpegComplex::ROT_90:
+ rotate=WJpegComplex::ROT_180;
+ break;
+ case WJpegComplex::ROT_180:
+ rotate=WJpegComplex::ROT_270;
+ break;
+ case WJpegComplex::ROT_270:
+ rotate=WJpegComplex::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 WJpegComplex::CROP:
+ cropmode=WJpegComplex::LETTER;
+ break;
+ case WJpegComplex::LETTER:
+ cropmode=WJpegComplex::CROPPERCENT;
+ break;
+ default:
+ cropmode=WJpegComplex::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=WJpegComplex::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;
+ WJpegComplex::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=WJpegComplex::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 WJpegComplex::CROPPERCENT:
+ mode="clipfactor";
+ break;
+ case WJpegComplex::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,WJpegComplex::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,DrawStyle::LIGHTTEXT);
+ len=strlen(currentAudio->getDisplayName())+strlen(pl)+30+strlen(parent->getDirname(MEDIA_TYPE_AUDIO))+Media::TIMEBUFLEN+110;
+ if (playerInfo) {
+ for (UINT i=0;i<strlen(playerInfo);i++)
+ if (playerInfo[i] == '\n') numlines++;
+ len+=strlen(playerInfo);
+ first=playerInfo;
+ }
+ else {
+ first="";
+ }
+ }
+ destroyInfo();
+ info=new VInfo();
+ UINT height=numlines*30+60;
+ UINT vheight=Video::getInstance()->getScreenHeight();
+ 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, DrawStyle::TITLEBARBACKGROUND);
+ info->drawText(title, 5, 5, DrawStyle::LIGHTTEXT);
+ info->drawPara(buf,5,32,DrawStyle::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, DrawStyle::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, DrawStyle::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, DrawStyle::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, DrawStyle::LIGHTTEXT);
+
+ // Draw progress bar
+ int progBarXbase = 0;
+ int barlen=250;
+
+ info->rectangle(barRegion.x + progBarXbase, barRegion.y + 3, barlen+10, 24, DrawStyle::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, DrawStyle::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,DrawStyle::LIGHTTEXT);
+ }
+ else {
+ char * buf=new char[strlen(currentAudio->getDisplayName())+50];
+ SNPRINTF(buf,50,"%s %s",tr("AudioPlayer"),currentAudio->getDisplayName());
+ audioBanner->drawText(buf,5,3,DrawStyle::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;
+ }
+}