From f33106d3f97a59bb5646980ee8427dc38bb60cf3 Mon Sep 17 00:00:00 2001 From: Marten Richter Date: Mon, 5 Nov 2012 08:40:58 +0100 Subject: [PATCH] End of line normalization --- GNUmakefile | 236 +- afeed.cc | 204 +- audio.h | 204 +- audioomx.h | 394 ++-- audiowin.cc | 1454 ++++++------ boxstack.cc | 1298 +++++------ boxx.cc | 912 ++++---- boxx.h | 382 ++-- command.cc | 2048 ++++++++--------- defines.h | 352 +-- demuxer.cc | 2184 +++++++++--------- demuxer.h | 482 ++-- demuxeraudio.cc | 2496 ++++++++++----------- demuxerts.h | 192 +- demuxervdr.cc | 792 +++---- draintarget.h | 234 +- feed.h | 58 +- fonts/licensesourcesans.txt | 186 +- glosdshader.cc | 170 +- glosdshader.h | 88 +- glshader.cc | 214 +- glshader.h | 110 +- imagereader.h | 232 +- log.cc | 456 ++-- log.h | 230 +- main.cc | 1230 +++++------ message.h | 164 +- objects.mk | 56 +- osd.h | 112 +- osdopengl.cc | 900 ++++---- osdopengl.h | 294 +-- osdopenvg.cc | 2122 +++++++++--------- osdopenvg.h | 324 +-- osdvector.cc | 756 +++---- osdvector.h | 414 ++-- osdwin.cc | 1206 +++++----- osdwin.h | 248 +-- player.cc | 2842 ++++++++++++------------ player.h | 474 ++-- playerliveradio.cc | 1008 ++++----- playerlivetv.cc | 1630 +++++++------- playerradio.cc | 1188 +++++----- playerradio.h | 314 +-- stream.cc | 412 ++-- surface.cc | 650 +++--- surface.h | 218 +- surfacedirectfb.cc | 608 +++--- surfacedirectfb.h | 150 +- surfacemvp.h | 500 ++--- surfaceopengl.cc | 728 +++--- surfaceopengl.h | 144 +- surfacevector.cc | 602 ++--- surfacevector.h | 186 +- surfacewin.cc | 808 +++---- surfacewin.h | 132 +- teletextdecodervbiebu.cc | 1674 +++++++------- tfeed.cc | 124 +- tfeed.h | 42 +- threadsystem.h | 58 +- vcolourtuner.cc | 578 ++--- vdr.cc | 3062 +++++++++++++------------- vdr.h | 584 ++--- vdrcommand.h | 328 +-- vdrresponsepacket.cc | 300 +-- vepg.cc | 1656 +++++++------- vfeed.cc | 168 +- video.cc | 196 +- video.h | 358 +-- videomvp.cc | 812 +++---- videomvp.h | 318 +-- videoomx.h | 578 ++--- videowin.cc | 4132 +++++++++++++++++------------------ videowin.h | 456 ++-- vmediaview.cc | 2914 ++++++++++++------------ 74 files changed, 27183 insertions(+), 27183 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index 6221114..54ea355 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -1,118 +1,118 @@ -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 - +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 + diff --git a/afeed.cc b/afeed.cc index ee0b55b..c841f45 100644 --- a/afeed.cc +++ b/afeed.cc @@ -1,102 +1,102 @@ -/* - 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 - } - } -} - +/* + 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 + } + } +} + diff --git a/audio.h b/audio.h index 601c2e7..4a3342b 100644 --- a/audio.h +++ b/audio.h @@ -1,102 +1,102 @@ -/* - Copyright 2004-2005 Chris Tallon - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#ifndef AUDIO_H -#define AUDIO_H - -#include -#include "defines.h" -#include "draintarget.h" -#include "abstractoption.h" - -typedef struct -{ - unsigned char frontleft; - unsigned char frontright; - unsigned char rearleft; - unsigned char rearright; - unsigned char center; - unsigned char lfe; -} audio_volume; - -class Audio : public DrainTarget, public AbstractOption -{ - public: - Audio(); - virtual ~Audio(); - static Audio* getInstance(); - // static void setInstance(Audio* ); - - virtual int init(UCHAR streamType)=0; - virtual int shutdown()=0; - virtual int setStreamType(UCHAR streamType)=0; - virtual int setChannel()=0; - virtual int setSource()=0; - virtual int sync()=0; - virtual int play()=0; - virtual int stop()=0; - virtual int pause()=0; - virtual int unPause()=0; - virtual int reset()=0; - virtual int setVolume(int volume)=0; - virtual int mute()=0; - virtual int unMute()=0; - virtual bool supportsAc3()=0; - virtual bool maysupportAc3(){return false;} - 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 +/* + Copyright 2004-2005 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef AUDIO_H +#define AUDIO_H + +#include +#include "defines.h" +#include "draintarget.h" +#include "abstractoption.h" + +typedef struct +{ + unsigned char frontleft; + unsigned char frontright; + unsigned char rearleft; + unsigned char rearright; + unsigned char center; + unsigned char lfe; +} audio_volume; + +class Audio : public DrainTarget, public AbstractOption +{ + public: + Audio(); + virtual ~Audio(); + static Audio* getInstance(); + // static void setInstance(Audio* ); + + virtual int init(UCHAR streamType)=0; + virtual int shutdown()=0; + virtual int setStreamType(UCHAR streamType)=0; + virtual int setChannel()=0; + virtual int setSource()=0; + virtual int sync()=0; + virtual int play()=0; + virtual int stop()=0; + virtual int pause()=0; + virtual int unPause()=0; + virtual int reset()=0; + virtual int setVolume(int volume)=0; + virtual int mute()=0; + virtual int unMute()=0; + virtual bool supportsAc3()=0; + virtual bool maysupportAc3(){return false;} + 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 diff --git a/audioomx.h b/audioomx.h index 30032e4..b166e0d 100644 --- a/audioomx.h +++ b/audioomx.h @@ -1,197 +1,197 @@ -/* - 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 -#include -#include -#include - -#include "defines.h" -#include "audio.h" -#include "videoomx.h" - -extern "C" { -#include -#include -} - - - - -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 input_bufs_omx_all; - list 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 +/* + 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 +#include +#include +#include + +#include "defines.h" +#include "audio.h" +#include "videoomx.h" + +extern "C" { +#include +#include +} + + + + +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 input_bufs_omx_all; + list 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 diff --git a/audiowin.cc b/audiowin.cc index 2079b1d..9903890 100644 --- a/audiowin.cc +++ b/audiowin.cc @@ -1,727 +1,727 @@ -/* - Copyright 2004-2005 Chris Tallon - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#include "audiowin.h" -#include "videowin.h" -#include "vdr.h" -#include "wtabbar.h" -#include "wwinaudiofilter.h" -#include "wwinmp3audiofilter.h" -#include "i18n.h" - - - - -AudioWin::AudioWin() -{ - initted = 0; - firstsynched=false; - winvolume=0; - volume=20; -audiofilterselected=-1; - mp3audiofilterselected=-1; - aud_type=Audio::MPEG2_PES; - -} - -AudioWin::~AudioWin() -{ - - int i; - for (i=0;isetAudioStreamType(type); - aud_type=type; - if (!initted) return 0; - return 1; -} - -int AudioWin::setChannel() -{ - if (!initted) return 0; - return 1; -} - -int AudioWin::setSource() -{ - if (!initted) return 0; - return 1; -} - -int AudioWin::sync() -{ - if (!initted) return 0; - return 1; -} - -int AudioWin::play() -{ - if (!initted) return 0; - firstsynched=false; - return ((VideoWin*)Video::getInstance())->dsplay(); - -} - -int AudioWin::stop() -{ - if (!initted) return 0; - return ((VideoWin*)Video::getInstance())->dsstop(); -} - -int AudioWin::pause() -{ - if (!initted) return 0; - return ((VideoWin*)Video::getInstance())->dspause(); -} - -int AudioWin::unPause() -{ - if (!initted) return 0; - return ((VideoWin*)Video::getInstance())->dsunPause(); -} - -int AudioWin::reset() -{ - - if (!initted){return 0;} - return ((VideoWin*)Video::getInstance())->dsreset(); -} - -int AudioWin::setVolume(int tvolume) -{ - // parameter: 0 for silence, 20 for full - if ((tvolume < 0) || (tvolume > 20)) return 0; - winvolume=((tvolume-20)*100*30)/20; - if (tvolume==0) winvolume=-10000; - ((VideoWin*)Video::getInstance())->SetAudioVolume(winvolume); - - - return 1; -} - -int AudioWin::mute() -{ - if (!initted) return 0; - ((VideoWin*)Video::getInstance())->SetAudioState(false); - ((VideoWin*)Video::getInstance())->SetAudioVolume(-10000); - return 1; -} - -int AudioWin::unMute() -{ - if (!initted) return 0; - ((VideoWin*)Video::getInstance())->SetAudioState(true); - ((VideoWin*)Video::getInstance())->SetAudioVolume(winvolume); - return 1; -} - -void AudioWin::PrepareMediaSample(const MediaPacketList& mplist,UINT samplepos) -{ - mediapacket = mplist.front(); -} - -void AudioWin::initFilterDatabase() -{ - /* This method should determine all availiable DirectShow Filters */ - IFilterMapper2* filtmap=NULL; - HRESULT result; - result = CoCreateInstance(CLSID_FilterMapper2,NULL,CLSCTX_INPROC, - IID_IFilterMapper2,(void**)&filtmap); - if (result != S_OK) - { - Log::getInstance()->log("AudioWin", Log::ERR , "Unable to create FilterMapper!"); - return; - } - /* Wishlist, what Mediatypes do we want */ - GUID mtypesin[]={MEDIATYPE_Audio,MEDIASUBTYPE_MPEG2_AUDIO, - /*MEDIATYPE_Audio,MEDIASUBTYPE_MPEG1Payload,*/ - MEDIATYPE_Audio,MEDIASUBTYPE_DOLBY_AC3, - MEDIATYPE_Audio, MEDIASUBTYPE_DOLBY_AC3_SPDIF}; - IEnumMoniker *myenum; - result = filtmap->EnumMatchingFilters(&myenum,0,TRUE,MERIT_DO_NOT_USE+1, - TRUE,3,mtypesin,NULL,NULL,FALSE,TRUE,0,NULL,NULL,NULL); - if (result != S_OK) - { - filtmap->Release(); - Log::getInstance()->log("AudioWin", Log::ERR , "Unable to enum Filters!"); - return; - } - ULONG gethowmany; - IMoniker * moni; - while(myenum->Next(1,&moni,&gethowmany)==S_OK) - { - AudioFilterDesc desc; - ZeroMemory(&desc,sizeof(desc)); - - LPOLESTR string; - moni->GetDisplayName(0,0,&string); - desc.displayname=new char[wcslen(string)+1]; - wcstombs(desc.displayname,string,wcslen(string)+1); - CoTaskMemFree(string); - IPropertyBag *bag; - if (moni->BindToStorage(0,0,IID_IPropertyBag,(void**)&bag) == S_OK) - { - VARIANT vari; - VariantInit(&vari); - result = bag->Read(L"FriendlyName",&vari,NULL); - if (result == S_OK) - { - desc.friendlyname=new char[wcslen(vari.bstrVal)+1]; - wcstombs(desc.friendlyname,vari.bstrVal,wcslen(vari.bstrVal)+1); - } - VariantClear(&vari); - bag->Release(); - - } - - - audiofilterlist.push_back(desc); - - - - moni->Release(); - // bctx->Release(); - } - int i; - audiofilterselected=-1; - myenum->Release(); - filtmap->Release(); -} - -void AudioWin::initMp3FilterDatabase() -{ - /* This method should determine all availiable DirectShow Filters */ - IFilterMapper2* filtmap=NULL; - HRESULT result; - result = CoCreateInstance(CLSID_FilterMapper2,NULL,CLSCTX_INPROC, - IID_IFilterMapper2,(void**)&filtmap); - if (result != S_OK) - { - Log::getInstance()->log("AudioWin", Log::ERR , "Unable to create FilterMapper!"); - return; - } - /* Wishlist, what Mediatypes do we want */ - GUID mtypesin[]={MEDIATYPE_Audio,MEDIATYPE_WaveFmt_Mpeg1Layer3, - MEDIATYPE_Audio,MEDIASUBTYPE_MPEG2_AUDIO}; - IEnumMoniker *myenum; - result = filtmap->EnumMatchingFilters(&myenum,0,TRUE,MERIT_DO_NOT_USE+1, - TRUE,3,mtypesin,NULL,NULL,FALSE,TRUE,0,NULL,NULL,NULL); - if (result != S_OK) - { - filtmap->Release(); - Log::getInstance()->log("AudioWin", Log::ERR , "Unable to enum Filters!"); - return; - } - ULONG gethowmany; - IMoniker * moni; - while(myenum->Next(1,&moni,&gethowmany)==S_OK) - { - AudioFilterDesc desc; - ZeroMemory(&desc,sizeof(desc)); - - LPOLESTR string; - moni->GetDisplayName(0,0,&string); - desc.displayname=new char[wcslen(string)+1]; - wcstombs(desc.displayname,string,wcslen(string)+1); - CoTaskMemFree(string); - IPropertyBag *bag; - if (moni->BindToStorage(0,0,IID_IPropertyBag,(void**)&bag) == S_OK) - { - VARIANT vari; - VariantInit(&vari); - result = bag->Read(L"FriendlyName",&vari,NULL); - if (result == S_OK) - { - desc.friendlyname=new char[wcslen(vari.bstrVal)+1]; - wcstombs(desc.friendlyname,vari.bstrVal,wcslen(vari.bstrVal)+1); - } - VariantClear(&vari); - bag->Release(); - - } - - - mp3audiofilterlist.push_back(desc); - - - - moni->Release(); - // bctx->Release(); - } - int i; - mp3audiofilterselected=-1; - myenum->Release(); - filtmap->Release(); -} - -bool AudioWin::loadOptionsfromServer(VDR* vdr) -{ - char *name=vdr->configLoad("DirectShow","AudioFilter"); - - if (name != NULL) - { - for (int i = 0;i configLoad("DirectShow","Mp3AudioFilter"); - - if (name != NULL) - { - for (int i = 0;i configSave("DirectShow", - "AudioFilter",audiofilterlist[audiofilterselected].displayname); - } - if (mp3audiofilterselected!=-1) { - VDR::getInstance()->configSave("DirectShow", - "Mp3AudioFilter",mp3audiofilterlist[mp3audiofilterselected].displayname); - } - return true; -} - -UINT AudioWin::DeliverMediaSample(UCHAR* buffer, UINT *samplepos) -{ - DeliverMediaPacket(mediapacket, buffer, samplepos); - if (*samplepos == mediapacket.length) { - *samplepos = 0; - return 1; - } - else return 0; -} - -UINT AudioWin::DeliverMediaPacket(const MediaPacket packet, - UCHAR* buffer, - UINT *samplepos) -{ - - /*First Check, if we have an audio sample*/ - VideoWin *vw=(VideoWin*)Video::getInstance(); - if (!vw->isdsinited()) return 0; - if (vw->InIframemode()) { - samplepos=0; - MILLISLEEP(10); - return 0; //Not in iframe mode! - } - IMediaSample* ms=NULL; - REFERENCE_TIME reftime1=0; - REFERENCE_TIME reftime2=0; - - UINT headerstrip=0; - if (packet.disconti) { - firstsynched=false; - vw->DeliverAudioMediaSample(); - } - - if (packet.type!=vw->lastAType()){//Format Change //Push data out ! - firstsynched=false; - vw->DeliverAudioMediaSample(); - } - - - - /*Inspect PES-Header */ -/* UINT header_length=buffer[(packet.pos_buffer+8)%bufferlength]+8/*is this right*; -*/ - if (*samplepos==0 && packet.type!=MPTYPE_MPEG_AUDIO_LAYER3) {//stripheader - headerstrip=buffer[packet.pos_buffer+8]+9; - if (packet.type == MPTYPE_AC3) headerstrip+=4; //skip ac3 bytes - *samplepos+=headerstrip; - if ( packet.synched ) { - vw->DeliverAudioMediaSample();//write out old data - reftime1=packet.presentation_time; - reftime2=reftime1+1; - firstsynched=true; - } else { - if (!firstsynched) {// - *samplepos=packet.length;//if we have not processed at least one - return packet.length;//synched packet ignore it! - } - } - } - BYTE *ms_buf; - UINT ms_length; - UINT ms_pos; - UINT haveToCopy; - if (!vw->getCurrentAudioMediaSample(&ms) || ms==NULL) {// get the current sample - //samplepos=0; - //MILLISLEEP(10); - return *samplepos; - } - ms_pos=ms->GetActualDataLength(); - ms_length=ms->GetSize(); - haveToCopy=min(ms_length-ms_pos,packet.length-*samplepos); - if ((ms_length-ms_pos)<1) { - vw->DeliverAudioMediaSample(); //we are full! - if (!vw->getCurrentAudioMediaSample(&ms) || ms==NULL) {// get the current sample - //samplepos=0; - //MILLISLEEP(10); - return *samplepos; - } - ms_pos=ms->GetActualDataLength(); - ms_length=ms->GetSize(); - haveToCopy=min(ms_length-ms_pos,packet.length-*samplepos); - } - ms->GetPointer(&ms_buf); - - - if (ms_pos==0) {//will only be changed on first packet - if (packet.disconti) { - ms->SetDiscontinuity(TRUE); - } else { - ms->SetDiscontinuity(FALSE); - } - if (packet.synched) { - ms->SetSyncPoint(TRUE); - ms->SetTime(&reftime1,&reftime2); - - //ms->SetTime(NULL,NULL); - ms->SetMediaTime(NULL, NULL); - if (reftime1<0) ms->SetPreroll(TRUE); - else ms->SetPreroll(FALSE); - }else { - ms->SetSyncPoint(FALSE); - ms->SetTime(NULL,NULL); - ms->SetMediaTime(NULL, NULL); - ms->SetPreroll(FALSE); - // ms->SetSyncPoint(TRUE); - } - } - if (packet.type!=vw->lastAType()) { - vw->changeAType(packet.type,ms); - ms->SetDiscontinuity(TRUE); - } - - - memcpy(ms_buf+ms_pos,buffer+packet.pos_buffer+*samplepos,haveToCopy); - - ms->SetActualDataLength(haveToCopy+ms_pos); - - *samplepos+=haveToCopy; - - return haveToCopy+headerstrip; - -} - -int AudioWin::dsInitAudioFilter(IGraphBuilder* dsgraphbuilder) -{ - HRESULT hres; - IFilterGraph2*fg2=NULL; - VideoWin *vw=(VideoWin*)Video::getInstance(); - if (dsgraphbuilder->QueryInterface(IID_IFilterGraph2,(void**)&fg2)!= S_OK) - { - Log::getInstance()->log("AudiooWin", Log::WARN , "Failed querying for FilterGraph2 Interface!"); - return 0; - } - IBaseFilter*audiofilter; - if (aud_type!=Audio::MP3) { - audiofilter = getAudioFilter(); - } else { - audiofilter = getMp3AudioFilter(); - } - if (dsgraphbuilder->AddFilter(audiofilter,NULL) != S_OK) - { - Log::getInstance()->log("AudioWin", Log::WARN , "Failed adding Video Filter!"); - return 0; - } - IEnumPins *pinenum=NULL; - bool error=false; - if (audiofilter->EnumPins(&pinenum) == S_OK) - { - IPin *current=NULL; - ULONG fetch=0; - bool firststep=false; - while (pinenum->Next(1,¤t,&fetch)==S_OK) - { - PIN_DIRECTION dir; - if (current->QueryDirection(&dir)==S_OK) - { - if (dir == PINDIR_INPUT) - { - if (vw->getSourceFilter()->GetAudioPin()->Connect(current,NULL)==S_OK) - { - current->Release(); - firststep=true; - break; - } - } - } - current->Release(); - } - if (firststep==false) - { - Log::getInstance()->log("AudioWin", Log::WARN , "Audio Filter has no suitable input!"); - audiofilter->Release(); - return 0; - } - bool secondstep=false; - pinenum->Reset(); - while (pinenum->Next(1,¤t,&fetch)==S_OK) - { - PIN_DIRECTION dir; - if (current->QueryDirection(&dir)==S_OK) - { - if (dir == PINDIR_OUTPUT) - { - - if (fg2->RenderEx((IPin*)current/*video*/, - 0,NULL) ==S_OK) - { - current->Release(); - secondstep=true; - break; - } - } - } - current->Release(); - } - if (secondstep==false) - { - Log::getInstance()->log("AudioWin", Log::WARN , "Audio Filter has no suitable output!"); - audiofilter->Release(); - - return 0; - } - - audiofilter->Release(); - pinenum->Release(); - - } - - - - fg2->Release(); - return 1; -} - - -IBaseFilter *AudioWin::getAudioFilter() -{ - IBaseFilter *curfilter= NULL; - bool notset=false; - if (audiofilterselected == -1) - { - int i; - for (i = 0;i BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) - { - if (curfilter != NULL && notset) - { - VDR *vdr=VDR::getInstance(); - if (vdr != NULL) - { - vdr->configSave("DirectShow","AudioFilter", - audiofilterlist[audiofilterselected].displayname); - } - } - - moni->Release(); - delete [] name; - bindctx->Release(); - return curfilter; - } - bindctx->Release(); - delete [] name; - return NULL; - } - return NULL; -} - -IBaseFilter *AudioWin::getMp3AudioFilter() -{ - IBaseFilter *curfilter= NULL; - bool notset=false; - if (mp3audiofilterselected == -1) - { - int i; - for (i = 0;i BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) - { - if (curfilter != NULL && notset) - { - VDR *vdr=VDR::getInstance(); - if (vdr != NULL) - { - vdr->configSave("DirectShow","Mp3AudioFilter", - mp3audiofilterlist[mp3audiofilterselected].displayname); - } - } - - moni->Release(); - delete [] name; - bindctx->Release(); - return curfilter; - } - bindctx->Release(); - delete [] name; - return NULL; - } - return NULL; -} - - -bool AudioWin::addOptionPagesToWTB(WTabBar *wtb) -{ - Boxx *box=new WWinAudioFilter(); - wtb->addTab(tr("Audio Filter"), box); - - - box=new WWinMp3AudioFilter(); - wtb->addTab(tr("Mp3 Audio Filter"), box); - - - return true; -} - -const AudioFilterDescList *AudioWin::getAudioFilterList(int &selected) -{ - selected=audiofilterselected; - return &audiofilterlist; -} - -const AudioFilterDescList *AudioWin::getMp3AudioFilterList(int &selected) -{ - selected=mp3audiofilterselected; - return &mp3audiofilterlist; -} -bool AudioWin::selectMp3AudioFilter(int filter) -{ - mp3audiofilterselected=filter; - return true; - -} - -bool AudioWin::selectAudioFilter(int filter) -{ - audiofilterselected=filter; - return true; - -} - -long long AudioWin::SetStartOffset(long long curreftime, bool *rsync){ - VideoWin *vw=(VideoWin*)Video::getInstance(); - return vw->SetStartAudioOffset(curreftime,rsync); -} - -void AudioWin::ResetTimeOffsets() { - VideoWin *vw=(VideoWin*)Video::getInstance(); - vw->ResetTimeOffsets(); -} - -bool AudioWin::supportsAc3(){ - VideoWin *vw=(VideoWin*)Video::getInstance(); - return vw->supportsAc3(); -} - -#ifdef DEV -int AudioWin::test() -{ - return 0; -} -#endif - - - +/* + Copyright 2004-2005 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "audiowin.h" +#include "videowin.h" +#include "vdr.h" +#include "wtabbar.h" +#include "wwinaudiofilter.h" +#include "wwinmp3audiofilter.h" +#include "i18n.h" + + + + +AudioWin::AudioWin() +{ + initted = 0; + firstsynched=false; + winvolume=0; + volume=20; +audiofilterselected=-1; + mp3audiofilterselected=-1; + aud_type=Audio::MPEG2_PES; + +} + +AudioWin::~AudioWin() +{ + + int i; + for (i=0;isetAudioStreamType(type); + aud_type=type; + if (!initted) return 0; + return 1; +} + +int AudioWin::setChannel() +{ + if (!initted) return 0; + return 1; +} + +int AudioWin::setSource() +{ + if (!initted) return 0; + return 1; +} + +int AudioWin::sync() +{ + if (!initted) return 0; + return 1; +} + +int AudioWin::play() +{ + if (!initted) return 0; + firstsynched=false; + return ((VideoWin*)Video::getInstance())->dsplay(); + +} + +int AudioWin::stop() +{ + if (!initted) return 0; + return ((VideoWin*)Video::getInstance())->dsstop(); +} + +int AudioWin::pause() +{ + if (!initted) return 0; + return ((VideoWin*)Video::getInstance())->dspause(); +} + +int AudioWin::unPause() +{ + if (!initted) return 0; + return ((VideoWin*)Video::getInstance())->dsunPause(); +} + +int AudioWin::reset() +{ + + if (!initted){return 0;} + return ((VideoWin*)Video::getInstance())->dsreset(); +} + +int AudioWin::setVolume(int tvolume) +{ + // parameter: 0 for silence, 20 for full + if ((tvolume < 0) || (tvolume > 20)) return 0; + winvolume=((tvolume-20)*100*30)/20; + if (tvolume==0) winvolume=-10000; + ((VideoWin*)Video::getInstance())->SetAudioVolume(winvolume); + + + return 1; +} + +int AudioWin::mute() +{ + if (!initted) return 0; + ((VideoWin*)Video::getInstance())->SetAudioState(false); + ((VideoWin*)Video::getInstance())->SetAudioVolume(-10000); + return 1; +} + +int AudioWin::unMute() +{ + if (!initted) return 0; + ((VideoWin*)Video::getInstance())->SetAudioState(true); + ((VideoWin*)Video::getInstance())->SetAudioVolume(winvolume); + return 1; +} + +void AudioWin::PrepareMediaSample(const MediaPacketList& mplist,UINT samplepos) +{ + mediapacket = mplist.front(); +} + +void AudioWin::initFilterDatabase() +{ + /* This method should determine all availiable DirectShow Filters */ + IFilterMapper2* filtmap=NULL; + HRESULT result; + result = CoCreateInstance(CLSID_FilterMapper2,NULL,CLSCTX_INPROC, + IID_IFilterMapper2,(void**)&filtmap); + if (result != S_OK) + { + Log::getInstance()->log("AudioWin", Log::ERR , "Unable to create FilterMapper!"); + return; + } + /* Wishlist, what Mediatypes do we want */ + GUID mtypesin[]={MEDIATYPE_Audio,MEDIASUBTYPE_MPEG2_AUDIO, + /*MEDIATYPE_Audio,MEDIASUBTYPE_MPEG1Payload,*/ + MEDIATYPE_Audio,MEDIASUBTYPE_DOLBY_AC3, + MEDIATYPE_Audio, MEDIASUBTYPE_DOLBY_AC3_SPDIF}; + IEnumMoniker *myenum; + result = filtmap->EnumMatchingFilters(&myenum,0,TRUE,MERIT_DO_NOT_USE+1, + TRUE,3,mtypesin,NULL,NULL,FALSE,TRUE,0,NULL,NULL,NULL); + if (result != S_OK) + { + filtmap->Release(); + Log::getInstance()->log("AudioWin", Log::ERR , "Unable to enum Filters!"); + return; + } + ULONG gethowmany; + IMoniker * moni; + while(myenum->Next(1,&moni,&gethowmany)==S_OK) + { + AudioFilterDesc desc; + ZeroMemory(&desc,sizeof(desc)); + + LPOLESTR string; + moni->GetDisplayName(0,0,&string); + desc.displayname=new char[wcslen(string)+1]; + wcstombs(desc.displayname,string,wcslen(string)+1); + CoTaskMemFree(string); + IPropertyBag *bag; + if (moni->BindToStorage(0,0,IID_IPropertyBag,(void**)&bag) == S_OK) + { + VARIANT vari; + VariantInit(&vari); + result = bag->Read(L"FriendlyName",&vari,NULL); + if (result == S_OK) + { + desc.friendlyname=new char[wcslen(vari.bstrVal)+1]; + wcstombs(desc.friendlyname,vari.bstrVal,wcslen(vari.bstrVal)+1); + } + VariantClear(&vari); + bag->Release(); + + } + + + audiofilterlist.push_back(desc); + + + + moni->Release(); + // bctx->Release(); + } + int i; + audiofilterselected=-1; + myenum->Release(); + filtmap->Release(); +} + +void AudioWin::initMp3FilterDatabase() +{ + /* This method should determine all availiable DirectShow Filters */ + IFilterMapper2* filtmap=NULL; + HRESULT result; + result = CoCreateInstance(CLSID_FilterMapper2,NULL,CLSCTX_INPROC, + IID_IFilterMapper2,(void**)&filtmap); + if (result != S_OK) + { + Log::getInstance()->log("AudioWin", Log::ERR , "Unable to create FilterMapper!"); + return; + } + /* Wishlist, what Mediatypes do we want */ + GUID mtypesin[]={MEDIATYPE_Audio,MEDIATYPE_WaveFmt_Mpeg1Layer3, + MEDIATYPE_Audio,MEDIASUBTYPE_MPEG2_AUDIO}; + IEnumMoniker *myenum; + result = filtmap->EnumMatchingFilters(&myenum,0,TRUE,MERIT_DO_NOT_USE+1, + TRUE,3,mtypesin,NULL,NULL,FALSE,TRUE,0,NULL,NULL,NULL); + if (result != S_OK) + { + filtmap->Release(); + Log::getInstance()->log("AudioWin", Log::ERR , "Unable to enum Filters!"); + return; + } + ULONG gethowmany; + IMoniker * moni; + while(myenum->Next(1,&moni,&gethowmany)==S_OK) + { + AudioFilterDesc desc; + ZeroMemory(&desc,sizeof(desc)); + + LPOLESTR string; + moni->GetDisplayName(0,0,&string); + desc.displayname=new char[wcslen(string)+1]; + wcstombs(desc.displayname,string,wcslen(string)+1); + CoTaskMemFree(string); + IPropertyBag *bag; + if (moni->BindToStorage(0,0,IID_IPropertyBag,(void**)&bag) == S_OK) + { + VARIANT vari; + VariantInit(&vari); + result = bag->Read(L"FriendlyName",&vari,NULL); + if (result == S_OK) + { + desc.friendlyname=new char[wcslen(vari.bstrVal)+1]; + wcstombs(desc.friendlyname,vari.bstrVal,wcslen(vari.bstrVal)+1); + } + VariantClear(&vari); + bag->Release(); + + } + + + mp3audiofilterlist.push_back(desc); + + + + moni->Release(); + // bctx->Release(); + } + int i; + mp3audiofilterselected=-1; + myenum->Release(); + filtmap->Release(); +} + +bool AudioWin::loadOptionsfromServer(VDR* vdr) +{ + char *name=vdr->configLoad("DirectShow","AudioFilter"); + + if (name != NULL) + { + for (int i = 0;i configLoad("DirectShow","Mp3AudioFilter"); + + if (name != NULL) + { + for (int i = 0;i configSave("DirectShow", + "AudioFilter",audiofilterlist[audiofilterselected].displayname); + } + if (mp3audiofilterselected!=-1) { + VDR::getInstance()->configSave("DirectShow", + "Mp3AudioFilter",mp3audiofilterlist[mp3audiofilterselected].displayname); + } + return true; +} + +UINT AudioWin::DeliverMediaSample(UCHAR* buffer, UINT *samplepos) +{ + DeliverMediaPacket(mediapacket, buffer, samplepos); + if (*samplepos == mediapacket.length) { + *samplepos = 0; + return 1; + } + else return 0; +} + +UINT AudioWin::DeliverMediaPacket(const MediaPacket packet, + UCHAR* buffer, + UINT *samplepos) +{ + + /*First Check, if we have an audio sample*/ + VideoWin *vw=(VideoWin*)Video::getInstance(); + if (!vw->isdsinited()) return 0; + if (vw->InIframemode()) { + samplepos=0; + MILLISLEEP(10); + return 0; //Not in iframe mode! + } + IMediaSample* ms=NULL; + REFERENCE_TIME reftime1=0; + REFERENCE_TIME reftime2=0; + + UINT headerstrip=0; + if (packet.disconti) { + firstsynched=false; + vw->DeliverAudioMediaSample(); + } + + if (packet.type!=vw->lastAType()){//Format Change //Push data out ! + firstsynched=false; + vw->DeliverAudioMediaSample(); + } + + + + /*Inspect PES-Header */ +/* UINT header_length=buffer[(packet.pos_buffer+8)%bufferlength]+8/*is this right*; +*/ + if (*samplepos==0 && packet.type!=MPTYPE_MPEG_AUDIO_LAYER3) {//stripheader + headerstrip=buffer[packet.pos_buffer+8]+9; + if (packet.type == MPTYPE_AC3) headerstrip+=4; //skip ac3 bytes + *samplepos+=headerstrip; + if ( packet.synched ) { + vw->DeliverAudioMediaSample();//write out old data + reftime1=packet.presentation_time; + reftime2=reftime1+1; + firstsynched=true; + } else { + if (!firstsynched) {// + *samplepos=packet.length;//if we have not processed at least one + return packet.length;//synched packet ignore it! + } + } + } + BYTE *ms_buf; + UINT ms_length; + UINT ms_pos; + UINT haveToCopy; + if (!vw->getCurrentAudioMediaSample(&ms) || ms==NULL) {// get the current sample + //samplepos=0; + //MILLISLEEP(10); + return *samplepos; + } + ms_pos=ms->GetActualDataLength(); + ms_length=ms->GetSize(); + haveToCopy=min(ms_length-ms_pos,packet.length-*samplepos); + if ((ms_length-ms_pos)<1) { + vw->DeliverAudioMediaSample(); //we are full! + if (!vw->getCurrentAudioMediaSample(&ms) || ms==NULL) {// get the current sample + //samplepos=0; + //MILLISLEEP(10); + return *samplepos; + } + ms_pos=ms->GetActualDataLength(); + ms_length=ms->GetSize(); + haveToCopy=min(ms_length-ms_pos,packet.length-*samplepos); + } + ms->GetPointer(&ms_buf); + + + if (ms_pos==0) {//will only be changed on first packet + if (packet.disconti) { + ms->SetDiscontinuity(TRUE); + } else { + ms->SetDiscontinuity(FALSE); + } + if (packet.synched) { + ms->SetSyncPoint(TRUE); + ms->SetTime(&reftime1,&reftime2); + + //ms->SetTime(NULL,NULL); + ms->SetMediaTime(NULL, NULL); + if (reftime1<0) ms->SetPreroll(TRUE); + else ms->SetPreroll(FALSE); + }else { + ms->SetSyncPoint(FALSE); + ms->SetTime(NULL,NULL); + ms->SetMediaTime(NULL, NULL); + ms->SetPreroll(FALSE); + // ms->SetSyncPoint(TRUE); + } + } + if (packet.type!=vw->lastAType()) { + vw->changeAType(packet.type,ms); + ms->SetDiscontinuity(TRUE); + } + + + memcpy(ms_buf+ms_pos,buffer+packet.pos_buffer+*samplepos,haveToCopy); + + ms->SetActualDataLength(haveToCopy+ms_pos); + + *samplepos+=haveToCopy; + + return haveToCopy+headerstrip; + +} + +int AudioWin::dsInitAudioFilter(IGraphBuilder* dsgraphbuilder) +{ + HRESULT hres; + IFilterGraph2*fg2=NULL; + VideoWin *vw=(VideoWin*)Video::getInstance(); + if (dsgraphbuilder->QueryInterface(IID_IFilterGraph2,(void**)&fg2)!= S_OK) + { + Log::getInstance()->log("AudiooWin", Log::WARN , "Failed querying for FilterGraph2 Interface!"); + return 0; + } + IBaseFilter*audiofilter; + if (aud_type!=Audio::MP3) { + audiofilter = getAudioFilter(); + } else { + audiofilter = getMp3AudioFilter(); + } + if (dsgraphbuilder->AddFilter(audiofilter,NULL) != S_OK) + { + Log::getInstance()->log("AudioWin", Log::WARN , "Failed adding Video Filter!"); + return 0; + } + IEnumPins *pinenum=NULL; + bool error=false; + if (audiofilter->EnumPins(&pinenum) == S_OK) + { + IPin *current=NULL; + ULONG fetch=0; + bool firststep=false; + while (pinenum->Next(1,¤t,&fetch)==S_OK) + { + PIN_DIRECTION dir; + if (current->QueryDirection(&dir)==S_OK) + { + if (dir == PINDIR_INPUT) + { + if (vw->getSourceFilter()->GetAudioPin()->Connect(current,NULL)==S_OK) + { + current->Release(); + firststep=true; + break; + } + } + } + current->Release(); + } + if (firststep==false) + { + Log::getInstance()->log("AudioWin", Log::WARN , "Audio Filter has no suitable input!"); + audiofilter->Release(); + return 0; + } + bool secondstep=false; + pinenum->Reset(); + while (pinenum->Next(1,¤t,&fetch)==S_OK) + { + PIN_DIRECTION dir; + if (current->QueryDirection(&dir)==S_OK) + { + if (dir == PINDIR_OUTPUT) + { + + if (fg2->RenderEx((IPin*)current/*video*/, + 0,NULL) ==S_OK) + { + current->Release(); + secondstep=true; + break; + } + } + } + current->Release(); + } + if (secondstep==false) + { + Log::getInstance()->log("AudioWin", Log::WARN , "Audio Filter has no suitable output!"); + audiofilter->Release(); + + return 0; + } + + audiofilter->Release(); + pinenum->Release(); + + } + + + + fg2->Release(); + return 1; +} + + +IBaseFilter *AudioWin::getAudioFilter() +{ + IBaseFilter *curfilter= NULL; + bool notset=false; + if (audiofilterselected == -1) + { + int i; + for (i = 0;i BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) + { + if (curfilter != NULL && notset) + { + VDR *vdr=VDR::getInstance(); + if (vdr != NULL) + { + vdr->configSave("DirectShow","AudioFilter", + audiofilterlist[audiofilterselected].displayname); + } + } + + moni->Release(); + delete [] name; + bindctx->Release(); + return curfilter; + } + bindctx->Release(); + delete [] name; + return NULL; + } + return NULL; +} + +IBaseFilter *AudioWin::getMp3AudioFilter() +{ + IBaseFilter *curfilter= NULL; + bool notset=false; + if (mp3audiofilterselected == -1) + { + int i; + for (i = 0;i BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) + { + if (curfilter != NULL && notset) + { + VDR *vdr=VDR::getInstance(); + if (vdr != NULL) + { + vdr->configSave("DirectShow","Mp3AudioFilter", + mp3audiofilterlist[mp3audiofilterselected].displayname); + } + } + + moni->Release(); + delete [] name; + bindctx->Release(); + return curfilter; + } + bindctx->Release(); + delete [] name; + return NULL; + } + return NULL; +} + + +bool AudioWin::addOptionPagesToWTB(WTabBar *wtb) +{ + Boxx *box=new WWinAudioFilter(); + wtb->addTab(tr("Audio Filter"), box); + + + box=new WWinMp3AudioFilter(); + wtb->addTab(tr("Mp3 Audio Filter"), box); + + + return true; +} + +const AudioFilterDescList *AudioWin::getAudioFilterList(int &selected) +{ + selected=audiofilterselected; + return &audiofilterlist; +} + +const AudioFilterDescList *AudioWin::getMp3AudioFilterList(int &selected) +{ + selected=mp3audiofilterselected; + return &mp3audiofilterlist; +} +bool AudioWin::selectMp3AudioFilter(int filter) +{ + mp3audiofilterselected=filter; + return true; + +} + +bool AudioWin::selectAudioFilter(int filter) +{ + audiofilterselected=filter; + return true; + +} + +long long AudioWin::SetStartOffset(long long curreftime, bool *rsync){ + VideoWin *vw=(VideoWin*)Video::getInstance(); + return vw->SetStartAudioOffset(curreftime,rsync); +} + +void AudioWin::ResetTimeOffsets() { + VideoWin *vw=(VideoWin*)Video::getInstance(); + vw->ResetTimeOffsets(); +} + +bool AudioWin::supportsAc3(){ + VideoWin *vw=(VideoWin*)Video::getInstance(); + return vw->supportsAc3(); +} + +#ifdef DEV +int AudioWin::test() +{ + return 0; +} +#endif + + + diff --git a/boxstack.cc b/boxstack.cc index 5330003..4c821d2 100644 --- a/boxstack.cc +++ b/boxstack.cc @@ -1,649 +1,649 @@ -/* - 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; - } - } -} +/* + 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; + } + } +} diff --git a/boxx.cc b/boxx.cc index af2464d..a1b31b1 100644 --- a/boxx.cc +++ b/boxx.cc @@ -1,456 +1,456 @@ -/* - Copyright 2007 Chris Tallon - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#include "boxx.h" -#include "bitmap.h" -#include "log.h" -#include "osd.h" -#include - -char Boxx::numBoxxes = 0; - -Boxx::Boxx() -{ - // I want a parent box or a surface. - parent = NULL; - surface = NULL; - - area.x = 0; - area.y = 0; - area.w = 0; - area.h = 0; - - paraVSpace = 6; // default gap for drawPara - - backgroundColourSet = false; - visible = true; - - numBoxxes++; - Log::getInstance()->log("Boxx", Log::DEBUG, "Construct, now %u", numBoxxes); -} - -Boxx::~Boxx() -{ - if (surface) delete surface; - numBoxxes--; - Log::getInstance()->log("Boxx", Log::DEBUG, "Destruct, now %u", numBoxxes); -} - -void Boxx::draw() -{ - //Log::getInstance()->log("Boxx", Log::DEBUG, "Draw this %p surface %p", this, surface); - if (backgroundColourSet) fillColour(backgroundColour); - - Boxx* currentBoxx; - vector::iterator j; - //int count=0; - for (j = children.begin(); j != children.end(); j++) - { - currentBoxx = *j; - // Log::getInstance()->log("Boxx", Log::DEBUG, "Draw child %d %d", count,currentBoxx); - if (currentBoxx->getVisible()) currentBoxx->draw(); - // count++; - } - // Log::getInstance()->log("Boxx", Log::DEBUG, "Draw this %p surface %p End", this, surface); -} - -void Boxx::setSize(UINT w, UINT h) -{ - area.w = w; - area.h = h; -} - -void Boxx::setPosition(UINT x, UINT y) -{ - area.x = x; - area.y = y; -} - -void Boxx::createBuffer() -{ - surface = Osd::getInstance()->createNewSurface(); - surface->create(area.w, area.h); -} - -void Boxx::add(Boxx* newChild) -{ - newChild->setParent(this); - children.push_back(newChild); -} - -void Boxx::remove(Boxx* oldChild) -{ - for(vector::iterator i = children.begin(); i != children.end(); i++) - { - if (*i == oldChild) - { - children.erase(i); - return; - } - } - Log::getInstance()->log("Boxx", Log::ERR, "Remove child box called, child %p not found", oldChild); -} - -void Boxx::setParent(Boxx* newParent) -{ - parent = newParent; -} - -void Boxx::setBackgroundColour(const 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=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; -} - +/* + Copyright 2007 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "boxx.h" +#include "bitmap.h" +#include "log.h" +#include "osd.h" +#include + +char Boxx::numBoxxes = 0; + +Boxx::Boxx() +{ + // I want a parent box or a surface. + parent = NULL; + surface = NULL; + + area.x = 0; + area.y = 0; + area.w = 0; + area.h = 0; + + paraVSpace = 6; // default gap for drawPara + + backgroundColourSet = false; + visible = true; + + numBoxxes++; + Log::getInstance()->log("Boxx", Log::DEBUG, "Construct, now %u", numBoxxes); +} + +Boxx::~Boxx() +{ + if (surface) delete surface; + numBoxxes--; + Log::getInstance()->log("Boxx", Log::DEBUG, "Destruct, now %u", numBoxxes); +} + +void Boxx::draw() +{ + //Log::getInstance()->log("Boxx", Log::DEBUG, "Draw this %p surface %p", this, surface); + if (backgroundColourSet) fillColour(backgroundColour); + + Boxx* currentBoxx; + vector::iterator j; + //int count=0; + for (j = children.begin(); j != children.end(); j++) + { + currentBoxx = *j; + // Log::getInstance()->log("Boxx", Log::DEBUG, "Draw child %d %d", count,currentBoxx); + if (currentBoxx->getVisible()) currentBoxx->draw(); + // count++; + } + // Log::getInstance()->log("Boxx", Log::DEBUG, "Draw this %p surface %p End", this, surface); +} + +void Boxx::setSize(UINT w, UINT h) +{ + area.w = w; + area.h = h; +} + +void Boxx::setPosition(UINT x, UINT y) +{ + area.x = x; + area.y = y; +} + +void Boxx::createBuffer() +{ + surface = Osd::getInstance()->createNewSurface(); + surface->create(area.w, area.h); +} + +void Boxx::add(Boxx* newChild) +{ + newChild->setParent(this); + children.push_back(newChild); +} + +void Boxx::remove(Boxx* oldChild) +{ + for(vector::iterator i = children.begin(); i != children.end(); i++) + { + if (*i == oldChild) + { + children.erase(i); + return; + } + } + Log::getInstance()->log("Boxx", Log::ERR, "Remove child box called, child %p not found", oldChild); +} + +void Boxx::setParent(Boxx* newParent) +{ + parent = newParent; +} + +void Boxx::setBackgroundColour(const 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=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; +} + diff --git a/boxx.h b/boxx.h index 6755cb7..06b3c92 100644 --- a/boxx.h +++ b/boxx.h @@ -1,191 +1,191 @@ -/* - Copyright 2004-2005 Chris Tallon - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#ifndef BOXX_H -#define BOXX_H - -#include -#include - -using namespace std; - -#include "colour.h" -#include "region.h" -#include "message.h" - - -#include "surface.h" - -class Bitmap; - -class Boxx -{ - 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 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. - -*/ - +/* + Copyright 2004-2005 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef BOXX_H +#define BOXX_H + +#include +#include + +using namespace std; + +#include "colour.h" +#include "region.h" +#include "message.h" + + +#include "surface.h" + +class Bitmap; + +class Boxx +{ + 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 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. + +*/ + diff --git a/command.cc b/command.cc index 814f053..463d2ec 100644 --- a/command.cc +++ b/command.cc @@ -1,1024 +1,1024 @@ -/* - Copyright 2004-2005 Chris Tallon - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#ifndef WIN32 -#include -#endif - -#include "command.h" - -#ifdef WIN32 -#include "remotewin.h" -#endif - -#ifdef __ANDROID__ -#include "remoteandroid.h" -#endif - -#include "led.h" -#include "video.h" -#include "audio.h" -#include "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 , 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); - } -} +/* + Copyright 2004-2005 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef WIN32 +#include +#endif + +#include "command.h" + +#ifdef WIN32 +#include "remotewin.h" +#endif + +#ifdef __ANDROID__ +#include "remoteandroid.h" +#endif + +#include "led.h" +#include "video.h" +#include "audio.h" +#include "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 , 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); + } +} diff --git a/defines.h b/defines.h index aec5c86..ae606a6 100644 --- a/defines.h +++ b/defines.h @@ -1,176 +1,176 @@ -/* - 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 - #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 +/* + 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 + #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 diff --git a/demuxer.cc b/demuxer.cc index a3700bc..db37c9d 100644 --- a/demuxer.cc +++ b/demuxer.cc @@ -1,1092 +1,1092 @@ -/* - Copyright 2005-2008 Mark Calderbank - Copyright 2007 Marten Richter (AC3 support) - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#include "demuxer.h" - -#include "callback.h" -#include "dvbsubtitles.h" -#include "log.h" - -#include - -#include - -#define DEMUXER_SEQ_HEAD 0x000001B3 -#define DEMUXER_PIC_HEAD 0x00000101 -#define DEMUXER_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>bit_pos) & (0xFF>>(8-fetch_bits))); - work|=(working_byte &(0xFF>>(bit_pos)))>>(8-fetch_bits-bit_pos); - remain_bits-=fetch_bits; - bit_pos=(bit_pos+fetch_bits)%8; - } - return work; -} - -UINT NALUUnit::getUe() -{ - int leadbits=-1; - bool bit; - for( bit = 0; !bit && !eonalu; leadbits++ ) - bit = getBits(1); - if (eonalu) return true; - return ((1 << leadbits)-1)+getBits(leadbits); -} - -int NALUUnit::getSe() -{ - UINT input=getUe(); - if (input==0) return 0; - int output=((input+1)>>1); - if (input & 0x1) output*=-1; - return output; -} - - - -static const int PESPacket_initial_size = 2000; - -// PESPacket methods -PESPacket::PESPacket() -{ - data_size = PESPacket_initial_size; - data = (UCHAR*)malloc(data_size); - data[0] = 0x00; - data[1] = 0x00; - data[2] = 0x01; - init(0); -} - -PESPacket::PESPacket(const PESPacket& packet) -{ - copyFrom(packet); -} - -PESPacket& PESPacket::operator=(const PESPacket& packet) -{ - if (this != &packet) - { - if (data) free(data); - copyFrom(packet); - } - return *this; -} - -PESPacket::~PESPacket() -{ - if (data) free(data); -} - -void PESPacket::copyFrom(const PESPacket& packet) -{ - length = packet.length; - size = packet.size; - packetType = packet.packetType; - substream = packet.substream; - seq_header = packet.seq_header; - data_size = size; - data = (UCHAR*)malloc(data_size); - memcpy(data, packet.data, data_size); -} - -void PESPacket::init(UCHAR type, UCHAR sub) -{ - length = 0; - size = 6; - data[4] = data[5] = 0; - data[3] = type; - packetType = type; - substream = sub; - seq_header = 1; // Unknown seq_header status - -} - -void PESPacket::truncate() -{ - init(packetType,substream); -} - - - -int PESPacket::write(const UCHAR *buf, int len) -{ - - - if (size + len > 0x10000) return 0; - if (size + len > data_size) - { // Reallocate - UINT new_data_size = max(data_size + data_size / 2, data_size + len); - if (new_data_size > 0x10000) new_data_size = 0x10000; - data_size = new_data_size; - data = (UCHAR*)realloc(data, data_size); - } - memcpy(data + size, buf, len); - length += len; - size += len; - data[4] = (length >> 8); - data[5] = (length & 0xFF); - // We have added data - reset seq_header indicator if necessary - if (seq_header == 0) seq_header = 1; // Reset to 'unknown' - return 1; -} - -ULLONG PESPacket::getPTS() const -{ - if ( ( (packetType >= Demuxer::PESTYPE_AUD0 && - packetType <= Demuxer::PESTYPE_AUDMAX) - || - (packetType >= Demuxer::PESTYPE_VID0 && - packetType <= Demuxer::PESTYPE_VIDMAX) - || - packetType == Demuxer::PESTYPE_PRIVATE_1 - ) - && size >= 14 && data[7] & 0x80) - { - return ( (ULLONG)(data[ 9] & 0x0E) << 29) | - ( (ULLONG)(data[10]) << 22 ) | - ( (ULLONG)(data[11] & 0xFE) << 14 ) | - ( (ULLONG)(data[12]) << 7 ) | - ( (ULLONG)(data[13] & 0xFE) >> 1 ); - } - else return PTS_INVALID; -} - -UCHAR PESPacket::operator[] (UINT index) const -{ - if (index >= size) - return 0; - else - return data[index]; -} - -UINT PESPacket::findPictureHeader(bool h264) const -{ - if (size < 12) return 0; - UINT pattern = ( ((UINT)data[ 8] << 24) | - ((UINT)data[ 9] << 16) | - ((UINT)data[10] << 8) | - (UINT)data[11] ); - UINT pos = 11; - if (h264) { - - while (pattern != DEMUXER_H264_ACCESS_UNIT) - { - if (++pos >= size) return 0; - pattern = (pattern << 8) | data[pos]; - } - return pos-3; - } else { - while (pattern != DEMUXER_PIC_HEAD) - { - if (++pos >= size) return 0; - pattern = (pattern << 8) | data[pos]; - } - return pos-3; - } -} - -UINT PESPacket::countPictureHeaders(bool h264) const -{ - if (size < 12) return 0; - UINT pattern = ( ((UINT)data[ 8] << 24) | - ((UINT)data[ 9] << 16) | - ((UINT)data[10] << 8) | - (UINT)data[11] ); - UINT pos = 11; - UINT count=0; - if (h264) { - - while (pos= size) - { - seq_header = 0; - return 0; - } - pattern = (pattern << 8) | data[pos]; - } - seq_header = pos - 3; - } - else - { - while (pattern != DEMUXER_SEQ_HEAD) - { - if (++pos >= size) - { - seq_header = 0; - return 0; - } - pattern = (pattern << 8) | data[pos]; - } - seq_header = pos - 3; - } - return seq_header; -} - -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;i1) { - 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; -} - - +/* + Copyright 2005-2008 Mark Calderbank + Copyright 2007 Marten Richter (AC3 support) + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "demuxer.h" + +#include "callback.h" +#include "dvbsubtitles.h" +#include "log.h" + +#include + +#include + +#define DEMUXER_SEQ_HEAD 0x000001B3 +#define DEMUXER_PIC_HEAD 0x00000101 +#define DEMUXER_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>bit_pos) & (0xFF>>(8-fetch_bits))); + work|=(working_byte &(0xFF>>(bit_pos)))>>(8-fetch_bits-bit_pos); + remain_bits-=fetch_bits; + bit_pos=(bit_pos+fetch_bits)%8; + } + return work; +} + +UINT NALUUnit::getUe() +{ + int leadbits=-1; + bool bit; + for( bit = 0; !bit && !eonalu; leadbits++ ) + bit = getBits(1); + if (eonalu) return true; + return ((1 << leadbits)-1)+getBits(leadbits); +} + +int NALUUnit::getSe() +{ + UINT input=getUe(); + if (input==0) return 0; + int output=((input+1)>>1); + if (input & 0x1) output*=-1; + return output; +} + + + +static const int PESPacket_initial_size = 2000; + +// PESPacket methods +PESPacket::PESPacket() +{ + data_size = PESPacket_initial_size; + data = (UCHAR*)malloc(data_size); + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0x01; + init(0); +} + +PESPacket::PESPacket(const PESPacket& packet) +{ + copyFrom(packet); +} + +PESPacket& PESPacket::operator=(const PESPacket& packet) +{ + if (this != &packet) + { + if (data) free(data); + copyFrom(packet); + } + return *this; +} + +PESPacket::~PESPacket() +{ + if (data) free(data); +} + +void PESPacket::copyFrom(const PESPacket& packet) +{ + length = packet.length; + size = packet.size; + packetType = packet.packetType; + substream = packet.substream; + seq_header = packet.seq_header; + data_size = size; + data = (UCHAR*)malloc(data_size); + memcpy(data, packet.data, data_size); +} + +void PESPacket::init(UCHAR type, UCHAR sub) +{ + length = 0; + size = 6; + data[4] = data[5] = 0; + data[3] = type; + packetType = type; + substream = sub; + seq_header = 1; // Unknown seq_header status + +} + +void PESPacket::truncate() +{ + init(packetType,substream); +} + + + +int PESPacket::write(const UCHAR *buf, int len) +{ + + + if (size + len > 0x10000) return 0; + if (size + len > data_size) + { // Reallocate + UINT new_data_size = max(data_size + data_size / 2, data_size + len); + if (new_data_size > 0x10000) new_data_size = 0x10000; + data_size = new_data_size; + data = (UCHAR*)realloc(data, data_size); + } + memcpy(data + size, buf, len); + length += len; + size += len; + data[4] = (length >> 8); + data[5] = (length & 0xFF); + // We have added data - reset seq_header indicator if necessary + if (seq_header == 0) seq_header = 1; // Reset to 'unknown' + return 1; +} + +ULLONG PESPacket::getPTS() const +{ + if ( ( (packetType >= Demuxer::PESTYPE_AUD0 && + packetType <= Demuxer::PESTYPE_AUDMAX) + || + (packetType >= Demuxer::PESTYPE_VID0 && + packetType <= Demuxer::PESTYPE_VIDMAX) + || + packetType == Demuxer::PESTYPE_PRIVATE_1 + ) + && size >= 14 && data[7] & 0x80) + { + return ( (ULLONG)(data[ 9] & 0x0E) << 29) | + ( (ULLONG)(data[10]) << 22 ) | + ( (ULLONG)(data[11] & 0xFE) << 14 ) | + ( (ULLONG)(data[12]) << 7 ) | + ( (ULLONG)(data[13] & 0xFE) >> 1 ); + } + else return PTS_INVALID; +} + +UCHAR PESPacket::operator[] (UINT index) const +{ + if (index >= size) + return 0; + else + return data[index]; +} + +UINT PESPacket::findPictureHeader(bool h264) const +{ + if (size < 12) return 0; + UINT pattern = ( ((UINT)data[ 8] << 24) | + ((UINT)data[ 9] << 16) | + ((UINT)data[10] << 8) | + (UINT)data[11] ); + UINT pos = 11; + if (h264) { + + while (pattern != DEMUXER_H264_ACCESS_UNIT) + { + if (++pos >= size) return 0; + pattern = (pattern << 8) | data[pos]; + } + return pos-3; + } else { + while (pattern != DEMUXER_PIC_HEAD) + { + if (++pos >= size) return 0; + pattern = (pattern << 8) | data[pos]; + } + return pos-3; + } +} + +UINT PESPacket::countPictureHeaders(bool h264) const +{ + if (size < 12) return 0; + UINT pattern = ( ((UINT)data[ 8] << 24) | + ((UINT)data[ 9] << 16) | + ((UINT)data[10] << 8) | + (UINT)data[11] ); + UINT pos = 11; + UINT count=0; + if (h264) { + + while (pos= size) + { + seq_header = 0; + return 0; + } + pattern = (pattern << 8) | data[pos]; + } + seq_header = pos - 3; + } + else + { + while (pattern != DEMUXER_SEQ_HEAD) + { + if (++pos >= size) + { + seq_header = 0; + return 0; + } + pattern = (pattern << 8) | data[pos]; + } + seq_header = pos - 3; + } + return seq_header; +} + +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;i1) { + 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; +} + + diff --git a/demuxer.h b/demuxer.h index 2d65ea2..54db93c 100644 --- a/demuxer.h +++ b/demuxer.h @@ -1,241 +1,241 @@ -/* - 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 +/* + 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 diff --git a/demuxeraudio.cc b/demuxeraudio.cc index 09c7ea0..23fdcc3 100644 --- a/demuxeraudio.cc +++ b/demuxeraudio.cc @@ -1,1248 +1,1248 @@ -/* - Copyright 2006 Mark Calderbank, Andreas Vogel - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#include "demuxeraudio.h" -#include "audio.h" -#include "i18n.h" -#include "log.h" - -#define HDRBYTE1 0xff -#define HDRBYTE2 0xe0 -#define HDRBYTE2MASK 0xe0 - - - -class PacketBuffer { - - public: - PacketBuffer(Stream *as,UCHAR strtype) { - log=Log::getInstance(); - audio=as; - streamtype=strtype; - newStream(); - } - //just handle the data (do not deal with headers) - int putInternal(UCHAR* buf,int len,unsigned int &packetnum); - void reset(){ - partPacket=0; - bytesWritten=0; - framelen=DemuxerAudio::PACKET_SIZE; - } - void newStream() { - reset(); - numpackets=0; - numbytes=0; - skipfactor=0; - numskip=0; - } - bool bufferFull() { - return (partPacket>=framelen); - } - //can we write a new packet? - bool bufferEmpty() { - return partPacket==0; - } - //only set this, if buffer empty - //otherwise ignored! - bool setFramelen(int len) { - if (! bufferEmpty() ) return false; - if (len > (int)DemuxerAudio::PACKET_SIZE) return false; - framelen=len; - return true; - } - //how much bytes do we need to fill the packet? - int bytesMissing() { - return framelen-partPacket; - } - int getFramelen() { - return framelen; - } - void setSkipFactor(int factor) { - skipfactor=factor; - numskip=0; - } - private: - void packetWritten() { - numbytes+=framelen; - //log->log("DemuxerAudio::PacketBuffer",Log::DEBUG,"written packet %ld l=%d, bytes %ld",numpackets,framelen,numbytes); - numpackets++; - reset(); - } - bool doSkip(); - UCHAR store[DemuxerAudio::PACKET_SIZE]; // Storage for partial packets - int partPacket; // Length of partial packet stored from previous put() - int bytesWritten; //if they are !=0 and != framelength the stream is full... - int framelen; - Log * log; - Stream * audio; - UCHAR streamtype; - //global counters - ULONG numpackets; - ULONG numbytes; - int skipfactor; - int numskip; -}; - - -DemuxerAudio::DemuxerAudio(int p_vID, int p_aID) -{ - inSync=false; - isStarting=true; - log=Log::getInstance(); - readHeaders=0; - streamtype=Audio::MP3; - buffer=new PacketBuffer(&audiostream,streamtype); -// buffer=new PacketBuffer(&teststream,streamtype); - globalBytesWritten=0; - packetnum=0; - id3=NULL; - info=NULL; - vbr=NULL; - reset(); -} - -DemuxerAudio::~DemuxerAudio() { - delete buffer; - if(info) delete info; - if(id3) delete id3; - if (vbr) delete vbr; -} - -void DemuxerAudio::flush() -{ - Demuxer::flushAudio(); - buffer->newStream(); - tmpFill=0; -} - -void DemuxerAudio::reset() { - buffer->newStream(); - tmpFill=0; - readHeaders=0; - packetnum=0; - outOfSync=0; - globalBytesWritten=0; - if (id3) delete id3; - id3=NULL; - if (info) delete info; - info=NULL; - if (vbr) delete vbr; - vbr=NULL; - inSync=false; - hasHdrInfo=false; - hasVBRInfo=false; - isStarting=true; - hdrBitrate=128000; - hdrSamplingRate=44100; - avrBitrate=0; - hdrFramelen=0; - isStarting=true; -} - -int DemuxerAudio::scan(UCHAR *buf, int len) -{ - //no differend pids here - return 0; -} - -void DemuxerAudio::setVID(int p_vID) -{ -} - -void DemuxerAudio::setAID(int p_aID,int type) -{ -} - -static const char * id3_1_genre[] = { - "Blueshhh", - "Classic Rock", - "Country", - "Dance", - "Disco", - "Funk", - "Grunge", - "Hip-Hop", - "Jazz", - "Metal", - "New Age", - "Oldies", - "Other", - "Pop", - "R&B", - "Rap", - "Reggae", - "Rock", - "Techno", - "Industrial", - "Alternative", - "Ska", - "Death Metal", - "Pranks", - "Soundtrack", - "Euro-Techno", - "Ambient", - "Trip-Hop", - "Vocal", - "Jazz+Funk", - "Fusion", - "Trance", - "Classical", - "Instrumental", - "Acid", - "House", - "Game", - "Sound Clip", - "Gospel", - "Noise", - "AlternRock", - "Bass", - "Soul", - "Punk", - "Space", - "Meditative", - "Instrumental Pop", - "Instrumental Rock", - "Ethnic", - "Gothic", - "Darkwave", - "Techno-Industrial", - "Electronic", - "Pop-Folk", - "Eurodance", - "Dream", - "Southern Rock", - "Comedy", - "Cult", - "Gangsta", - "Top 40", - "Christian Rap", - "Pop/Funk", - "Jungle", - "Native American", - "Cabaret", - "New Wave", - "Psychadelic", - "Rave", - "Showtunes", - "Trailer", - "Lo-Fi", - "Tribal", - "Acid Punk", - "Acid Jazz", - "Polka", - "Retro", - "Musical", - "Rock & Roll", - "Hard Rock" -}; - - - -static int bitrateTable[16][5]={ -/* L1,L2,L3,2L1,2L2 */ -/*0000*/ {-1,-1,-1,-1,-1}, -/*0001*/ {32,32,32,32,8}, -/*0010*/ {64,48,40,48,16}, -/*0011*/ {96,56,48,56,24}, -/*0100*/ {128,64,56,64,32}, -/*0101*/ {160,80,64,80,40}, -/*0110*/ {192,96,80,96,48}, -/*0111*/ {224,112,96,112,56}, -/*1000*/ {256,128,112,128,64}, -/*1001*/ {288,160,128,144,80}, -/*1010*/ {320,192,160,160,96}, -/*1011*/ {352,224,192,176,112}, -/*1100*/ {384,256,224,192,128}, -/*1101*/ {416,320,256,224,144}, -/*1110*/ {448,384,320,256,160}, -/*1111*/ {-1,-1,-1,-1,-1} }; - -static int samplingRateTable[4][3]={ -/*00*/ {44100,22050,11025}, -/*01*/ {48000,24000,12000}, -/*10*/ {32000,16000,8000}, -/*11*/ {-1,-1,-1}}; - -//max 7 char! -static const char * mpegString(UCHAR code) { - switch(code) { - case 0: - return "MPEG2.5"; - case 1: - return "RESERV"; - case 2: - return "MPEG 2"; - case 3: - return "MPEG 1"; - } - return "UNKNOWN"; -} - -static const char * layerString(UCHAR code) { - switch(code) { - case 0: - return "Layer reserved"; - case 1: - return "Layer III"; - case 2: - return "Layer II"; - case 3: - return "Layer I"; - } - return "Layer UNKNOWN"; -} -/** - * parse an id3 Header - * provided by Brian Walton - * @returns -1 of nothing found - */ - -int DemuxerAudio::id3_2_3_FrameParse(unsigned char buf[], id3_frame *frame) -{ - if (buf[0] < 0x20 || buf[1] < 0x20 || buf [2] < 0x20 ) return -1; - frame->size = (buf[4] & 0x7F) << 21 | (buf[5] & 0x7F) << 14 | (buf[6] & 0x7F) << 7 | (buf[7] & 0x7F); - if (frame->size == 0) return -1; - //TODO. clearify flags against: - //http://id3.org/id3v2.3.0#head-697d09c50ed7fa96fb66c6b0a9d93585e2652b0b - frame->flags.tagAlterPreserv = (buf[8] & 0x80) >> 7; - frame->flags.filelterPreserv = (buf[8] & 0x40) >> 6; - frame->flags.readOnly = (buf[8] & 0x20) >> 5; - frame->flags.groupId = (buf[9] & 0x20) >> 5; - frame->flags.compression = (buf[9] & 0x80) >> 7; - frame->flags.encryption = (buf[9] & 0x40) >> 6; - frame->flags.unsync = 0; - frame->flags.dataLen = 0; - return 0; -} - - /** - * parse an id3 Header - * provided by Brian Walton - * @returns -1 of nothing found - */ - -int DemuxerAudio::id3_2_2_FrameParse(unsigned char buf[], id3_frame *frame) -{ - if (buf[0] < 0x20 || buf[1] < 0x20 || buf[2] < 0x20) return -1; - frame->size = (buf[3] & 0x7F) << 14 | (buf[4] & 0x7F) << 7 | (buf[5] & 0x7F); - if (frame->size == 0) return -1; - return 0; -} - - -//fill an id3tag from a frame payload -//http://id3.org/id3v2.3.0#head-697d09c50ed7fa96fb66c6b0a9d93585e2652b0b -//http://id3.org/id3v2-00 -static struct tagid { - const char * bytes; - int index; -} knownFrames[]= { - //ID3V2.3 - {"TIT2",1}, //title - {"TPE1",2}, //artist - {"TCON",3}, //genre - {"TRCK",6}, //track - {"TYER",4}, //year - {"TALB",5}, //album - {"TCOM",7}, //composer - {"COMM",8}, //comment - //Text encoding $xx - //Language $xx xx xx - //Short content descrip. $00 (00) - //The actual text - //ID3V2.0 - {"TT2",1 }, - {"TP1",2 }, - {"TCM",7 }, - {"TCO",3 }, //(genreNumber) - {"TAL",5 }, - {"TRK",6 }, - {"TYE",4 }, - {"COM",8 } -}; -#define NUMKNOWN (sizeof(knownFrames)/sizeof(knownFrames[0])) - -/*fill in infos - from an ID3 V2.x, V2.3 frame into the tags structure - frameData must point to the header - framelen is the len without header (10 Bytes for V23, 6 Bytes for v2x) - */ - -#define MAXLEN(tagtype) ((UINT)frameLentagtype)-1?(UINT)frameLen:sizeof(tag->tagtype)-1) -bool DemuxerAudio::fillId3Tag(id3_tag * tag,UCHAR * frameData, int frameLen, int dataOffset, bool v23) { - int tl=v23?4:3; - int tagIndex=-1; - if (tag == NULL) return false; - if (frameLen < 2) return false; - for (UINT i=0;i< NUMKNOWN;i++) { - if(strncmp((char *)frameData,knownFrames[i].bytes,tl) == 0) { - tagIndex=knownFrames[i].index; - break; - } - } - if (tagIndex < 0) return false; - UCHAR encoding=*(frameData+dataOffset); - dataOffset++; - frameLen--; - if (encoding != 0) { - log->log("DemuxerAudio",Log::DEBUG,"unknown encoding for tag %d, tagid %s",encoding, - knownFrames[tagIndex].bytes); - return false; - } - switch(tagIndex) { - case 1: //title - strncpy(tag->title,(char*)(frameData+dataOffset),MAXLEN(title)); - tag->title[MAXLEN(title)]=0; - break; - case 2: //artist - strncpy(tag->artist,(char*)(frameData+dataOffset),MAXLEN(artist)); - tag->artist[MAXLEN(artist)]=0; - break; - case 3: //genre - { - UCHAR * st=frameData+dataOffset; - int genre=0; - if (*st=='(') { - genre=atoi((const char *)(st+1)) && 31; - st=(UCHAR *)id3_1_genre[genre]; - } - strncpy(tag->genre,(char*)st,MAXLEN(genre)); - tag->genre[MAXLEN(genre)]=0; - break; - } - case 4: //year - strncpy(tag->year,(char *)(frameData+dataOffset),MAXLEN(year)); - tag->year[MAXLEN(year)]=0; - break; - case 5: //album - strncpy(tag->album,(char *)(frameData+dataOffset),MAXLEN(album)); - tag->album[MAXLEN(album)]=0; - break; - case 6: //track - strncpy(tag->track,(char *)(frameData+dataOffset),MAXLEN(track)); - tag->track[MAXLEN(track)]=0; - break; - case 7: //composer - strncpy(tag->composer,(char *)(frameData+dataOffset),MAXLEN(composer)); - tag->composer[MAXLEN(composer)]=0; - break; - case 8: //comment - strncpy(tag->comment,(char *)(frameData+dataOffset),MAXLEN(comment)); - tag->comment[MAXLEN(comment)]=0; - break; - default: - return false; - } - - return true; -} - -/** - * parse an id3 Header - * based on code provided by Brian Walton - * @returns -1 of nothing found - * otherwise the id3 info is filled - */ - -int DemuxerAudio::parseID3V2(UCHAR *data, int len) { - int debug=0; - UCHAR * start=data; - id3_header id3header; - id3_frame id3frame; - id3_tag * id3tag=NULL; - //len = read(fd, data, 10); - if (len < 10) { - delete id3tag; - return -1; - } - len-=10; - if(data[0]=='I' && data[1]=='D' && data[2]=='3') - { - id3tag=new id3_tag(); - id3header.major = data[3]; - id3header.minor = data[4]; - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"ID3 V2.%d.%d found\n", id3header.major, id3header.minor); - id3header.flags.unsynchronisation = (data[5] & 0x80)>>7; - id3header.flags.extended_header = (data[5] & 0x40)>>6; - id3header.flags.experimental = (data[5] & 0x20)>>5; - id3header.flags.footer = (data[5] & 0x10)>>4; - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Unsynchronisation flag: %d\n", id3header.flags.unsynchronisation); - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Extended header flag: %d\n", id3header.flags.extended_header); - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Experimental indicator flag: %d\n", id3header.flags.experimental); - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Footer present flag: %d\n", id3header.flags.footer); - id3header.size = (data[6] & 0x7F) << 21 | (data[7] & 0x7F) << 14 | (data[8] & 0x7F) << 7 | (data[9] & 0x7F); - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"ID3 Size: %d\n", id3header.size); - data=start+10; - if (len <= id3header.size) { - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"header size to big %d, only %d bytes available\n",id3header.size,len); - delete id3tag; - return -1; - } - if (id3header.flags.extended_header) - { - int extended_hdr_hdr=4; //still to be discussed (id3.org...) - //read extended header size - if (len < extended_hdr_hdr) { - if (debug) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"extended header found but cannot read\n"); - delete id3tag; - return -1; - } - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"remaining %d chars after extended hdr hdr\n", len); - id3header.extended_header.size = (data[0] & 0x7F) << 21 | (data[1] & 0x7F) << 14 | (data[2] & 0x7F) << 7 | (data[3] & 0x7F); - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Extended header size: %d\n", id3header.extended_header.size); - if (len <= id3header.extended_header.size+extended_hdr_hdr) { - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"extended Header to big, only %d bytes available\n",len); - delete id3tag; - return -1; - } - //lseek(fd, id3header.extended_header.size - 6, SEEK_CUR); - data+=id3header.extended_header.size+extended_hdr_hdr; - len-=id3header.extended_header.size+extended_hdr_hdr; - } - //set the end of the header - UCHAR * eob=start+id3header.size+10; - bool readNext=true; - while (data < eob && readNext) - { - //skip over some padding - found this in lame MCDI tag... - if (*data == 0) { - data++; - continue; - } - readNext=false; - switch(id3header.major) - { - case 2: //ID3 V2.2.x - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"version 2.2 frame, %d : %c %c %c\n", data-start,*data,*(data+1),*(data+2)); - if (data + 6 >= eob) - { - break; - } - if (id3_2_2_FrameParse(data, &id3frame) < 0) - { - break; - } - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"frame size: %d\n", id3frame.size); - fillId3Tag(id3tag,data,id3frame.size,6,false); - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"frame payload: %s\n", data + 6 +1); - data+=6+id3frame.size; - readNext=true; - break; - case 3: //ID3 V2.3.x - { - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"version 2.3 frame, %d : %c %c %c %c\n", data-start, - *data,*(data+1),*(data+2),*(data+3)); - if (data + 10 >= eob) - { - break; - } - if (id3_2_3_FrameParse(data, &id3frame) <0) - { - break; - } - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Frame size: %d\n", id3frame.size); - int dataOffset=10; - if (id3frame.flags.groupId) - { - dataOffset++; - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Frame group: %d\n", data[dataOffset]); - } - if (id3frame.flags.compression) - { - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Frame compressed: %d\n", id3frame.flags.compression); - } - if (id3frame.flags.encryption) - { - dataOffset+=1; - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Frame encryption method: %d\n", data[dataOffset]); - } - fillId3Tag(id3tag,data,id3frame.size-dataOffset+10,dataOffset,true); - if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"frame payload: %s\n", data + dataOffset +1); - data+=10+id3frame.size; - readNext=true; - break; - } - default: - //don't support this version - delete id3tag; - return -1; - } - } - - data=eob; - //store the found tag - if (id3) delete id3; - id3=id3tag; - return data-start; - } - return -1; -} - -/** - * parse an id3v1 Header - * based on code provided by Brian Walton - * @returns -1 of nothing found - * otherwise the id3 info is filled - */ -#define MEMCPY(type,len,offset) {int type##max=sizeof(tag->type)-1type)-1:len;strncpy(tag->type,(char*)&data[offset],type##max);tag->type[type##max]=0;} - -int DemuxerAudio::parseID3V1(UCHAR *data, int len) { - int debug=1; - if (len < 128) return -1; - if(data[0]=='T' && data[1]=='A' && data[2]=='G') - { - id3_tag * tag=new id3_tag(); - if (debug != 0)log->log("DemuxerAudio::parseID3V1",Log::DEBUG,"ID3 V1 tag found\n"); - MEMCPY(title,30,3); - MEMCPY(artist,30,33); - MEMCPY(album,30,63); - MEMCPY(year,4,93); - if (data[125]==0 && data[126]!=0) - { //ID3 V1.1 - if (debug != 0)log->log("DemuxerAudio::parseID3V1",Log::DEBUG,"ID3 V1.1 tag\n"); - MEMCPY(comment,29,97); - sprintf(tag->track, "%d", data[126]); - } else { - if (debug != 0)log->log("DemuxerAudio::parseID3V1",Log::DEBUG,"ID3 V1.0 tag\n"); - MEMCPY(comment,30,97); - } - if (data[127] < sizeof(id3_1_genre)/sizeof(id3_1_genre[0])) - { - sprintf(tag->genre, id3_1_genre[data[127]]); - } - if (id3) delete id3; - id3=tag; - return 0; - } - return -1; -} - -//infos from http://www.multiweb.cz/twoinches/MP3inside.htm -int DemuxerAudio::parseVBR(UCHAR *data, int len) { - UCHAR *hdr=findHeader(data,len); - //we expect the header exactly here - if (hdr != data) return -1; - const static char * VBRHDR="Xing"; - int vbridpos=36; - UCHAR mpgtype=(data[1] & 0x18)>>3; - UCHAR chmode=(data[2] & 0xc0) >> 6; - UCHAR layer=(data[2] & 0x06) >>1; - if ( mpgtype == 3 && chmode == 11) vbridpos=21; - if ( mpgtype != 3 && chmode != 11) vbridpos=21; - if ( mpgtype != 3 && chmode == 11) vbridpos=13; - //check for the header ID - if (vbridpos+(int)strlen(VBRHDR)+4 >= len) { - Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"frame to short for VBR header %d",len); - return -1; - } - for (int i=4;ilog("DemuxerAudio::parseVBR",Log::DEBUG,"garbage when searching VBR header at pos %d",i); - return -1; - } - } - if ( strncmp((char *)&data[vbridpos],VBRHDR,strlen(VBRHDR)) != 0) { - Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"no VBR header at pos %d",vbridpos); - return -1; - } - int framedata=vbridpos+strlen(VBRHDR); - int expectedLen=0; - //OK we should now have a valid vbr header - bool hasFramenum=data[framedata+3] & 1; - bool hasBytes=data[framedata+3] & 0x2; - bool hasTOC=data[framedata+3] & 0x4; - expectedLen+=8; - if (hasTOC) expectedLen+=100; - if (framedata+expectedLen > len) { - Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"frame to short for VBR header data %d, expected %d", - len,framedata+expectedLen); - return -1; - } - if (!hasFramenum || ! hasBytes || ! hasTOC) { - //not usefull for us.. - Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"not all parts in VBR header - ignore"); - return -1; - } - framedata+=4; - if (vbr) delete vbr; - vbr=new vbrInfo(); - vbr->numFrames=data[framedata] << 24 | data[framedata+1]<<16| - data[framedata+2]<<8 |data[framedata+3]; - framedata+=4; - vbr->numBytes=data[framedata] << 24 | data[framedata+1]<<16| - data[framedata+2]<<8 |data[framedata+3]; - framedata+=4; - for (int ti=0;ti<100;ti++) { - vbr->table[ti]=data[framedata+ti]; - } - //compute file size in seconds - //should be (#of frames -1) *samplesPerFrame / sampleRate - //TODO: difference for Mono? - ULONG samplesPerFrame=384; //layer1 - if (layer != 3) samplesPerFrame=1152; - vbr->fileSeconds=(vbr->numFrames-1)*samplesPerFrame/hdrSamplingRate; - Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"successfully read VBR %ldbytes, %ld frames, %ldsec", - vbr->numBytes,vbr->numFrames,vbr->fileSeconds); - return hdrFramelen; -} - - - - - - - -UCHAR * DemuxerAudio::findHeader(UCHAR *buf, int len, bool writeInfo) { - while (len >= 3) //assume hdr+crc - { - UCHAR pattern=*buf; - buf++; len--; - if (pattern != HDRBYTE1 ) continue; - if ((*buf & HDRBYTE2MASK) != HDRBYTE2) continue; - if (readHeader((buf-1),4,writeInfo) != 0) continue; - return buf-1; - } - return NULL; -} - - - -int DemuxerAudio::readHeader(UCHAR * hbuf,int len,bool writeInfo) { - int curFramelen=0; - int curBitrate=0; - int curSamplingRate=0; - if (*hbuf != HDRBYTE1) return -1; - hbuf++; - if ((*hbuf & HDRBYTE2MASK) != HDRBYTE2) return -1; - UCHAR mpgtype=(*hbuf & 0x18)>>3; - if (mpgtype == 1) { - log->log("DemuxerAudio",Log::DEBUG,"header invalid mpgtype %s %i %i %i", - mpegString(mpgtype),*hbuf,*(hbuf+1),*(hbuf+2)); - return 1; - } - UCHAR layer=(*hbuf & 0x06) >>1; - //bool hasCRC=!(*hbuf & 1); - hbuf++; - UCHAR bitrateCode=(*hbuf & 0xf0) >>4; - UCHAR samplingCode=(*hbuf & 0x0c) >> 2; - bool padding=*hbuf & 0x02; - hbuf++; - //0 Stereo, 1 JointStereo, 2 Dual, 3 Mono - UCHAR chmode=(*hbuf & 0xc0) >> 6; - //UCHAR extension=(*hbuf & 0x30) >> 4; - - //layercode: 1-L3, 2-L2, 3-L1 - //columns 0,1,2 for MPEG1 - UCHAR bitrateColumn=3-layer; - if (bitrateColumn > 2) { - log->log("DemuxerAudio",Log::DEBUG,"header invalid layer %s %i %i %i", - layerString(layer),*(hbuf-2),*(hbuf-1),*hbuf); - return 1; - } - if (mpgtype != 3) bitrateColumn+=3; - if (bitrateColumn>4) bitrateColumn=4; - curBitrate=1000*bitrateTable[bitrateCode][bitrateColumn]; - UCHAR sampleRateColumn=0; - if (mpgtype == 10) sampleRateColumn=1; - if (mpgtype == 0) sampleRateColumn=2; - curSamplingRate=samplingRateTable[samplingCode][sampleRateColumn]; - if (curSamplingRate < 0 || curBitrate < 0) { - log->log("DemuxerAudio",Log::DEBUG,"header invalid rates br=%d sr=%d %i %i %i", - curBitrate,curSamplingRate,*(hbuf-2),*(hbuf-1),*hbuf); - return 1; - } - int padbytes=0; - if (padding) { - if (layer == 3) padbytes=4; - else padbytes=1; - } - if (layer == 3) { - //Layer 1 - //FrameLengthInBytes = (12 * BitRate / SampleRate + Padding) * 4 - curFramelen=(12*curBitrate/curSamplingRate+padbytes) * 4; - } - else { - //Layer 2/3 - //FrameLengthInBytes = 144 * BitRate / SampleRate + Padding - curFramelen=144*curBitrate/curSamplingRate+padbytes; - } - //the header itself - if (curFramelen < 32) { - log->log("DemuxerAudio",Log::DEBUG,"read header %ld mpgv=%s lc=%s br=%d sr=%d, fl=%d-invalid %i %i %i", - readHeaders,mpegString(mpgtype),layerString(layer), - curBitrate,curSamplingRate,curFramelen,*(hbuf-2),*(hbuf-1),*hbuf); - return 1; - } - if (writeInfo || isStarting){ - log->log("DemuxerAudio",Log::DEBUG,"read header %ld mpgv=%s lc=%s br=%d sr=%d, fl=%d %i %i %i", - readHeaders,mpegString(mpgtype),layerString(layer), - curBitrate,curSamplingRate,curFramelen,*(hbuf-2),*(hbuf-1),*hbuf); - if (info) delete info; - info=new mpegInfo(); - strcpy(info->mpegVersion,mpegString(mpgtype)); - info->mpegLayer=4-layer; - info->bitRate=curBitrate; - info->avrBitrate=curBitrate; - info->sampleRate=curSamplingRate; - const char *chmodStr=tr("Stereo"); - switch (chmode) { - case 1: chmodStr=tr("JointStereo");break; - case 2: chmodStr=tr("Dual");break; - case 3: chmodStr=tr("Mono");break; - } - SNPRINTF(info->info,sizeof(info->info)-1,"%s",chmodStr); - } - if (isStarting) avrBitrate=curBitrate; - isStarting=false; - readHeaders++; - //moving average F=0.005 - avrBitrate=avrBitrate+((5*(curBitrate-avrBitrate))/1024); - hdrBitrate=curBitrate; - hdrFramelen=curFramelen; - hdrSamplingRate=curSamplingRate; - hasHdrInfo=true; - return 0; -} - -int DemuxerAudio::findPTS(UCHAR* buf, int len, ULLONG* dest) -{ - //we have no PTS number ... - *dest=0; - return (findHeader(buf,len) != NULL)?1:0; -} - -bool PacketBuffer::doSkip() { - if (!bufferFull()) return false; - if (skipfactor == 0) return false; - numskip++; - if (numskip >= skipfactor) { - //sent at least always 2 packets - if (numskip > skipfactor) numskip=0; - return false; - } - packetWritten(); - return true; -} - -// just handle the real stream without dealing with the header -int PacketBuffer::putInternal(UCHAR * wbuf, int len,unsigned int &packetnum) -{ - /* Important, the type passed to stream must be a mediapacket type as defined in - Draintarget.h and not the device setting of the mvp, so we have to translate it here, - in order to get it working on windows - --MR */ - UCHAR mptype=0; - switch (streamtype) { - case Audio::MPEG1_PES: //? - case Audio::MPEG2_PES: //Important, this must be a PES ! - mptype=MPTYPE_MPEG_AUDIO; break; - default: - case Audio::MP3: //this can be any Mpeg1 Audio not only layer 3 not packed into PES - mptype=MPTYPE_MPEG_AUDIO_LAYER3;break; - }; - if (bufferFull()) { -#ifndef WIN32 - if (doSkip()) return 0;//NoSkip on Windows -#endif - //we are still full - so try to write - int sent=audio->put(store+bytesWritten,framelen-bytesWritten,/*streamtype*/mptype,packetnum);packetnum++; - //log->log("DemuxerAudio::PacketBuffer",Log::DEBUG,"written %d bytes to stream (still full) pp=%d, framelen=%d, written=%d",sent,partPacket,framelen, bytesWritten ); - if (sent < (framelen - bytesWritten)) { - //packet still not written - bytesWritten+=sent; - return 0; - } - packetWritten(); - //let the demuxer come back with the rest - need to check header first - return 0; - } - if (partPacket+len >= framelen) { - //now we have at least a complete packet - int bytesConsumed=framelen-partPacket; - memcpy(store+partPacket,wbuf,bytesConsumed); - partPacket=framelen; - //log->log("DemuxerAudio::PacketBuffer",Log::DEBUG,"stored packet %ld, length %d (last %d) for stream %p",numpackets,framelen,bytesConsumed,audio ); -#ifndef WIN32 //No Skip on Windows - if (doSkip()) return bytesConsumed; -#endif - int sent=audio->put(store,framelen,mptype,packetnum);packetnum++; - bytesWritten+=sent; - //log->log("DemuxerAudio::PacketBuffer",Log::DEBUG,"written %d bytes to stream",sent ); - if (bytesWritten < framelen) { - //still not completely written - return bytesConsumed; - } - packetWritten(); - //let the player come back... - return bytesConsumed; - } - //OK packet still not complete - if (len == 0) return 0; - int bytesConsumed=len; - memcpy(store+partPacket,wbuf,bytesConsumed); - partPacket+=bytesConsumed; - return bytesConsumed; -} - -/** - major entry for data from a player - the demuxer is either in the state headerSearch (packet written or - just at the beginning), writing garbage (inSync=false) or - handling data (none set) - A header is expected at the first byte after the previous packet - - otherwise we switch to garbage where we always search for a header - (but anyway provide the data to the underlying device - it's probably - more intelligent then we are... - We only loose a correct position display. - */ -int DemuxerAudio::put(UCHAR* wbuf, int len) -{ - //return audiostream.put(wbuf,len,streamtype); - int framelen=PACKET_SIZE; - UCHAR *hdr; - int bytesConsumed=0; - int oldBytes=0; - if (tmpFill != 0 || (buffer->bufferEmpty() && len < HDRLEN)) { - //OK we have to copy everything to the tmp buffer - int cp=(UINT)len<(PACKET_SIZE-tmpFill)?(UINT)len:(PACKET_SIZE-tmpFill); - memcpy(&tmpBuffer[tmpFill],wbuf,cp); - oldBytes=tmpFill; - tmpFill+=cp; - wbuf=tmpBuffer; - len=tmpFill; - //if there is no header here and our buffer - //is empty - just wait for the next header - if (len < HDRLEN && buffer->bufferEmpty()) { - log->log("DemuxerAudio",Log::DEBUG,"len to small for header %d at bytes %ld",len,globalBytesWritten); - return cp; - } - } - while (bytesConsumed < len ) { - if (buffer->bufferFull()) { - //if this is the first part of the loop, try to write to the stream - if (bytesConsumed == 0) buffer->putInternal(wbuf,0,packetnum); - //if the buffer is full, no need to continue - if (buffer->bufferFull()) break; - } - //either we are in a packet (buffer != full && buffer != empty) - //or we are searching a header - if (buffer->bufferEmpty()) { - if (len-bytesConsumed < HDRLEN) { - // we cannot still search - if (tmpFill != 0) { - //we are already working at the buffer - break; - } - memcpy(tmpBuffer,wbuf,len-bytesConsumed); - tmpFill=len-bytesConsumed; - log->log("DemuxerAudio",Log::DEBUG,"len to small for header %d at bytes %ld",len,globalBytesWritten); - return len; - } - - int lastFramelen=hdrFramelen; - //if the header has been valid before and we are searching - //it should be here - int maxsearch=((len-bytesConsumed) < (int)PACKET_SIZE)?len-bytesConsumed:(int)PACKET_SIZE; - hdr=findHeader(wbuf,maxsearch); - if (hdr != NULL) { - //log->log("DemuxerAudio",Log::DEBUG,"header found at offset %d",hdr-wbuf); - } - //hdr now points to the new header - if (hdr != wbuf) { - //still at least bytes before the header - inSync=false; - outOfSync++; - int garbageBytes=(hdr!=NULL)?(hdr-wbuf):maxsearch; - //we consider the garbage now as an own packet - //we can consume at most our buffer - //basically the buffer should be empty here (we are - //in header search - and we fill up each garbage - - int nframesize=garbageBytes; - if (nframesize > (int)PACKET_SIZE) { - nframesize=(int)PACKET_SIZE; - } - if (! buffer->bufferEmpty()) { - log->log("DemuxerAudio",Log::WARN,"buffer not empty when having garbage to store"); - //at the end no big problem, we only write the remaining bytes that we have... - } - else { - buffer->setFramelen(nframesize); - } - log->log("DemuxerAudio",Log::DEBUG,"garbage found at packet %ld (bytes %ld) of length %d, " - "framelen set to %d (last fl=%d)", - readHeaders,globalBytesWritten,garbageBytes,buffer->getFramelen(),lastFramelen); -#ifndef WIN32 - //hmm - we assume that he low level driver is more intelligent - //and give him the data "as is" - int written=buffer->putInternal(wbuf,garbageBytes,packetnum); - globalBytesWritten+=written; - bytesConsumed+=written; - if (written != garbageBytes || hdr == NULL ) { - break; - } -#else //DS is not intelligent - globalBytesWritten+=garbageBytes; - bytesConsumed+=garbageBytes; -#endif - //OK either all the "garbage" is written or - //we found the next header as expected - //continue with the next package - wbuf=hdr; - } - //we have to wait now until the buffer is - //free for the next package - if ( ! buffer->bufferEmpty()) return bytesConsumed; - //this is the place where we start a new packet - framelen=hdrFramelen; - if ( !buffer->setFramelen(framelen) ) { - log->log("DemuxerAudio",Log::DEBUG,"unable to set framelen should=%d, current=%d", - framelen,buffer->getFramelen()); - } - inSync=true; - } - //now we are surely within a packet - int written=buffer->putInternal(wbuf,len-bytesConsumed,packetnum); - //update the status - globalBytesWritten+=written; - wbuf+=written; - bytesConsumed+=written; - if (written == 0) { - //stream is full - break; - } - //OK - handle the rest - } - if (bytesConsumed >= oldBytes) { - //if we consumed more than the old bytes - OK the buffer - //can be thrown away - tmpFill=0; - return bytesConsumed-oldBytes; - } - else { - //only consumed bytes from the buffer - if (bytesConsumed == 0) { - //restore the buffer - tmpFill=oldBytes; - return 0; - } - //shift bytes in buffer - for (int i=0;iavrBitrate=avrBitrate; - info->bitRate=hdrBitrate; - return info; -} - -const DemuxerAudio::vbrInfo * DemuxerAudio::getVBRINfo() { - return vbr; -} - -int DemuxerAudio::checkStart(UCHAR *b, int len) { - UCHAR *start=b; - int id3len=parseID3V2(b,len); - if (id3len > 0) { - char * str=id3->toString(); - log->log("DemuxerAudio",Log::DEBUG,"parseID3V2 %d bytes of %d",id3len,len); - log->log("DemuxerAudio",Log::DEBUG,"parseID3V2 found:%s",str); - delete str; - b+=id3len; - len-=id3len; - } - int vbrlen=parseVBR(b,len); - if (vbrlen > 0 ) { - hasVBRInfo=true; - b+=vbrlen; - len-=vbrlen; - } - UCHAR * hdr=findHeader(b,len,true); - if (hdr == NULL) return -1; - return hdr-start; -} - -int DemuxerAudio::checkID3(UCHAR *b, int len) { - if (len != 128) return -1; - int rt=parseID3V1(b,len); - if (rt >= 0) { - char * str=id3->toString(); - log->log("DemuxerAudio",Log::DEBUG,"parseID3V1 found:%s",str); - delete str; - } - return rt; -} - -bool DemuxerAudio::isSync() { - return inSync; -} - -UINT DemuxerAudio::getSyncErrors() { - return outOfSync; -} - -ULONG DemuxerAudio::getBytesPerSecond() -{ - ULONG bps=hdrBitrate; - if (! hasHdrInfo) return 0; - if (hdrBitrate != avrBitrate) { - //we seem to have vbr - if (hasVBRInfo) { - //vbr stuff TODO - bps=avrBitrate; - } - else { - //some guessing - bps=avrBitrate; - } - } - bps=bps/8; - return bps; -} - -ULONG DemuxerAudio::getSecondsFromLen(ULONG len) { - if (! hasHdrInfo) return 0; - if (vbr) { - //first find the index where we are between - //rough starting point: - ULONG idx=100*len/vbr->numBytes; - if (idx >= 100) idx=99; - ULONG idxPos=(vbr->table[idx]) * vbr->numBytes/256; - ULONG pbefore=idxPos; - ULONG pafter=idxPos; - //OK now we know whether we have to go up or down - if (idxPos > len) { - //down - while (idxPos > len && idx > 0) { - pafter=idxPos; - idx--; - idxPos=(vbr->table[idx]) * vbr->numBytes/256; - pbefore=idxPos; - } - //OK we are now before our postion - } - else { - //up - while (idxPos < len && idx < 100 ) { - pbefore=idxPos; - idx++; - idxPos=(vbr->table[idx]) * vbr->numBytes/256; - pafter=idxPos; - } - //after our position - if (idx > 0) idx --; - } - //idx is now the index before our position - //approximate between the 2 points - ULONG idxTime=idx * vbr->fileSeconds/100; - if (pafter == pbefore) return idxTime; - ULONG rt=idxTime+ (len-pbefore)* vbr->fileSeconds/(100 * (pafter-pbefore)) ; - if (rt > vbr -> fileSeconds) return vbr->fileSeconds; - if (rt < idxTime) return idxTime; - return rt; - } - else { - ULONG bps=getBytesPerSecond(); - if (bps == 0) return 0; - return len/bps; - } -} - -ULONG DemuxerAudio::positionFromSeconds(ULONG seconds) { - if (! hasHdrInfo) return 0; - if (vbr) { - ULONG idx=seconds*100/vbr->fileSeconds; - if (idx > 99) idx=99; - ULONG idxPos=vbr->table[idx] * vbr->numBytes/256; - ULONG idx2=idx; - if (idx < 99) idx2++; - ULONG nextPos=vbr->table[idx] * vbr->numBytes/256; - ULONG rt=idxPos+(nextPos-idxPos) * (seconds - idx * vbr->fileSeconds /256); - if (rt < idxPos) return idxPos; - if ( rt > vbr->numBytes) return vbr->numBytes; - return rt; - } - else { - ULONG bps=getBytesPerSecond(); - return bps*seconds; - } -} - -int id3_tag::stringlen(bool withTitle) const { - if (!withTitle) - return strlen(artist)+strlen(genre)+strlen(year)+strlen(album)+ - strlen(track)+strlen(composer)+strlen(comment)+8+3+ - 90; //30 chars for each name... - else - return strlen(title)+strlen(artist)+strlen(genre)+strlen(year)+strlen(album)+ - strlen(track)+strlen(composer)+strlen(comment)+8+3+ - 120; //30 chars for each name... -} -//create a string out of the id3 tag info -//delete this string afterwards if you did not provide the buffer -char * id3_tag::toString(char *b, int len, bool withTitle) const { - const char *ta=tr("Artist"); -//char *tg=tr("Genre"); -//char *ty=tr("Year"); - const char *tx=tr("Album"); -//char *to=tr("Composer"); -//char *tc=tr("Comment"); - const char *tn=tr("Track"); - /* in the moment: - Title: - Artist: - Album: year - name - Track: - */ - if (b == NULL) { - len=stringlen(withTitle); - b=new char[len]; - } - const char * del=" - "; - if (strlen(year) == 0) del=""; - if (withTitle){ - const char *tt=tr("Title"); - SNPRINTF(b,len-1,"%s: %s\n%s: %s\n%s: %s%s%s\n%s: %s", - tt,title,ta,artist,tx,year,del,album,tn,track); - } - else { -SNPRINTF(b,len-1,"%s: %s\n%s: %s%s%s\n%s: %s", - ta,artist,tx,year,del,album,tn,track); - } - b[len-1]=0; - return b; -} - -void DemuxerAudio::setSkipFactor(int factor) { - Log::getInstance()->log("DemuxerAudio",Log::DEBUG,"set skipfactor %d\n",factor); - buffer->setSkipFactor(factor); -} - +/* + Copyright 2006 Mark Calderbank, Andreas Vogel + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "demuxeraudio.h" +#include "audio.h" +#include "i18n.h" +#include "log.h" + +#define HDRBYTE1 0xff +#define HDRBYTE2 0xe0 +#define HDRBYTE2MASK 0xe0 + + + +class PacketBuffer { + + public: + PacketBuffer(Stream *as,UCHAR strtype) { + log=Log::getInstance(); + audio=as; + streamtype=strtype; + newStream(); + } + //just handle the data (do not deal with headers) + int putInternal(UCHAR* buf,int len,unsigned int &packetnum); + void reset(){ + partPacket=0; + bytesWritten=0; + framelen=DemuxerAudio::PACKET_SIZE; + } + void newStream() { + reset(); + numpackets=0; + numbytes=0; + skipfactor=0; + numskip=0; + } + bool bufferFull() { + return (partPacket>=framelen); + } + //can we write a new packet? + bool bufferEmpty() { + return partPacket==0; + } + //only set this, if buffer empty + //otherwise ignored! + bool setFramelen(int len) { + if (! bufferEmpty() ) return false; + if (len > (int)DemuxerAudio::PACKET_SIZE) return false; + framelen=len; + return true; + } + //how much bytes do we need to fill the packet? + int bytesMissing() { + return framelen-partPacket; + } + int getFramelen() { + return framelen; + } + void setSkipFactor(int factor) { + skipfactor=factor; + numskip=0; + } + private: + void packetWritten() { + numbytes+=framelen; + //log->log("DemuxerAudio::PacketBuffer",Log::DEBUG,"written packet %ld l=%d, bytes %ld",numpackets,framelen,numbytes); + numpackets++; + reset(); + } + bool doSkip(); + UCHAR store[DemuxerAudio::PACKET_SIZE]; // Storage for partial packets + int partPacket; // Length of partial packet stored from previous put() + int bytesWritten; //if they are !=0 and != framelength the stream is full... + int framelen; + Log * log; + Stream * audio; + UCHAR streamtype; + //global counters + ULONG numpackets; + ULONG numbytes; + int skipfactor; + int numskip; +}; + + +DemuxerAudio::DemuxerAudio(int p_vID, int p_aID) +{ + inSync=false; + isStarting=true; + log=Log::getInstance(); + readHeaders=0; + streamtype=Audio::MP3; + buffer=new PacketBuffer(&audiostream,streamtype); +// buffer=new PacketBuffer(&teststream,streamtype); + globalBytesWritten=0; + packetnum=0; + id3=NULL; + info=NULL; + vbr=NULL; + reset(); +} + +DemuxerAudio::~DemuxerAudio() { + delete buffer; + if(info) delete info; + if(id3) delete id3; + if (vbr) delete vbr; +} + +void DemuxerAudio::flush() +{ + Demuxer::flushAudio(); + buffer->newStream(); + tmpFill=0; +} + +void DemuxerAudio::reset() { + buffer->newStream(); + tmpFill=0; + readHeaders=0; + packetnum=0; + outOfSync=0; + globalBytesWritten=0; + if (id3) delete id3; + id3=NULL; + if (info) delete info; + info=NULL; + if (vbr) delete vbr; + vbr=NULL; + inSync=false; + hasHdrInfo=false; + hasVBRInfo=false; + isStarting=true; + hdrBitrate=128000; + hdrSamplingRate=44100; + avrBitrate=0; + hdrFramelen=0; + isStarting=true; +} + +int DemuxerAudio::scan(UCHAR *buf, int len) +{ + //no differend pids here + return 0; +} + +void DemuxerAudio::setVID(int p_vID) +{ +} + +void DemuxerAudio::setAID(int p_aID,int type) +{ +} + +static const char * id3_1_genre[] = { + "Blueshhh", + "Classic Rock", + "Country", + "Dance", + "Disco", + "Funk", + "Grunge", + "Hip-Hop", + "Jazz", + "Metal", + "New Age", + "Oldies", + "Other", + "Pop", + "R&B", + "Rap", + "Reggae", + "Rock", + "Techno", + "Industrial", + "Alternative", + "Ska", + "Death Metal", + "Pranks", + "Soundtrack", + "Euro-Techno", + "Ambient", + "Trip-Hop", + "Vocal", + "Jazz+Funk", + "Fusion", + "Trance", + "Classical", + "Instrumental", + "Acid", + "House", + "Game", + "Sound Clip", + "Gospel", + "Noise", + "AlternRock", + "Bass", + "Soul", + "Punk", + "Space", + "Meditative", + "Instrumental Pop", + "Instrumental Rock", + "Ethnic", + "Gothic", + "Darkwave", + "Techno-Industrial", + "Electronic", + "Pop-Folk", + "Eurodance", + "Dream", + "Southern Rock", + "Comedy", + "Cult", + "Gangsta", + "Top 40", + "Christian Rap", + "Pop/Funk", + "Jungle", + "Native American", + "Cabaret", + "New Wave", + "Psychadelic", + "Rave", + "Showtunes", + "Trailer", + "Lo-Fi", + "Tribal", + "Acid Punk", + "Acid Jazz", + "Polka", + "Retro", + "Musical", + "Rock & Roll", + "Hard Rock" +}; + + + +static int bitrateTable[16][5]={ +/* L1,L2,L3,2L1,2L2 */ +/*0000*/ {-1,-1,-1,-1,-1}, +/*0001*/ {32,32,32,32,8}, +/*0010*/ {64,48,40,48,16}, +/*0011*/ {96,56,48,56,24}, +/*0100*/ {128,64,56,64,32}, +/*0101*/ {160,80,64,80,40}, +/*0110*/ {192,96,80,96,48}, +/*0111*/ {224,112,96,112,56}, +/*1000*/ {256,128,112,128,64}, +/*1001*/ {288,160,128,144,80}, +/*1010*/ {320,192,160,160,96}, +/*1011*/ {352,224,192,176,112}, +/*1100*/ {384,256,224,192,128}, +/*1101*/ {416,320,256,224,144}, +/*1110*/ {448,384,320,256,160}, +/*1111*/ {-1,-1,-1,-1,-1} }; + +static int samplingRateTable[4][3]={ +/*00*/ {44100,22050,11025}, +/*01*/ {48000,24000,12000}, +/*10*/ {32000,16000,8000}, +/*11*/ {-1,-1,-1}}; + +//max 7 char! +static const char * mpegString(UCHAR code) { + switch(code) { + case 0: + return "MPEG2.5"; + case 1: + return "RESERV"; + case 2: + return "MPEG 2"; + case 3: + return "MPEG 1"; + } + return "UNKNOWN"; +} + +static const char * layerString(UCHAR code) { + switch(code) { + case 0: + return "Layer reserved"; + case 1: + return "Layer III"; + case 2: + return "Layer II"; + case 3: + return "Layer I"; + } + return "Layer UNKNOWN"; +} +/** + * parse an id3 Header + * provided by Brian Walton + * @returns -1 of nothing found + */ + +int DemuxerAudio::id3_2_3_FrameParse(unsigned char buf[], id3_frame *frame) +{ + if (buf[0] < 0x20 || buf[1] < 0x20 || buf [2] < 0x20 ) return -1; + frame->size = (buf[4] & 0x7F) << 21 | (buf[5] & 0x7F) << 14 | (buf[6] & 0x7F) << 7 | (buf[7] & 0x7F); + if (frame->size == 0) return -1; + //TODO. clearify flags against: + //http://id3.org/id3v2.3.0#head-697d09c50ed7fa96fb66c6b0a9d93585e2652b0b + frame->flags.tagAlterPreserv = (buf[8] & 0x80) >> 7; + frame->flags.filelterPreserv = (buf[8] & 0x40) >> 6; + frame->flags.readOnly = (buf[8] & 0x20) >> 5; + frame->flags.groupId = (buf[9] & 0x20) >> 5; + frame->flags.compression = (buf[9] & 0x80) >> 7; + frame->flags.encryption = (buf[9] & 0x40) >> 6; + frame->flags.unsync = 0; + frame->flags.dataLen = 0; + return 0; +} + + /** + * parse an id3 Header + * provided by Brian Walton + * @returns -1 of nothing found + */ + +int DemuxerAudio::id3_2_2_FrameParse(unsigned char buf[], id3_frame *frame) +{ + if (buf[0] < 0x20 || buf[1] < 0x20 || buf[2] < 0x20) return -1; + frame->size = (buf[3] & 0x7F) << 14 | (buf[4] & 0x7F) << 7 | (buf[5] & 0x7F); + if (frame->size == 0) return -1; + return 0; +} + + +//fill an id3tag from a frame payload +//http://id3.org/id3v2.3.0#head-697d09c50ed7fa96fb66c6b0a9d93585e2652b0b +//http://id3.org/id3v2-00 +static struct tagid { + const char * bytes; + int index; +} knownFrames[]= { + //ID3V2.3 + {"TIT2",1}, //title + {"TPE1",2}, //artist + {"TCON",3}, //genre + {"TRCK",6}, //track + {"TYER",4}, //year + {"TALB",5}, //album + {"TCOM",7}, //composer + {"COMM",8}, //comment + //Text encoding $xx + //Language $xx xx xx + //Short content descrip. $00 (00) + //The actual text + //ID3V2.0 + {"TT2",1 }, + {"TP1",2 }, + {"TCM",7 }, + {"TCO",3 }, //(genreNumber) + {"TAL",5 }, + {"TRK",6 }, + {"TYE",4 }, + {"COM",8 } +}; +#define NUMKNOWN (sizeof(knownFrames)/sizeof(knownFrames[0])) + +/*fill in infos + from an ID3 V2.x, V2.3 frame into the tags structure + frameData must point to the header + framelen is the len without header (10 Bytes for V23, 6 Bytes for v2x) + */ + +#define MAXLEN(tagtype) ((UINT)frameLentagtype)-1?(UINT)frameLen:sizeof(tag->tagtype)-1) +bool DemuxerAudio::fillId3Tag(id3_tag * tag,UCHAR * frameData, int frameLen, int dataOffset, bool v23) { + int tl=v23?4:3; + int tagIndex=-1; + if (tag == NULL) return false; + if (frameLen < 2) return false; + for (UINT i=0;i< NUMKNOWN;i++) { + if(strncmp((char *)frameData,knownFrames[i].bytes,tl) == 0) { + tagIndex=knownFrames[i].index; + break; + } + } + if (tagIndex < 0) return false; + UCHAR encoding=*(frameData+dataOffset); + dataOffset++; + frameLen--; + if (encoding != 0) { + log->log("DemuxerAudio",Log::DEBUG,"unknown encoding for tag %d, tagid %s",encoding, + knownFrames[tagIndex].bytes); + return false; + } + switch(tagIndex) { + case 1: //title + strncpy(tag->title,(char*)(frameData+dataOffset),MAXLEN(title)); + tag->title[MAXLEN(title)]=0; + break; + case 2: //artist + strncpy(tag->artist,(char*)(frameData+dataOffset),MAXLEN(artist)); + tag->artist[MAXLEN(artist)]=0; + break; + case 3: //genre + { + UCHAR * st=frameData+dataOffset; + int genre=0; + if (*st=='(') { + genre=atoi((const char *)(st+1)) && 31; + st=(UCHAR *)id3_1_genre[genre]; + } + strncpy(tag->genre,(char*)st,MAXLEN(genre)); + tag->genre[MAXLEN(genre)]=0; + break; + } + case 4: //year + strncpy(tag->year,(char *)(frameData+dataOffset),MAXLEN(year)); + tag->year[MAXLEN(year)]=0; + break; + case 5: //album + strncpy(tag->album,(char *)(frameData+dataOffset),MAXLEN(album)); + tag->album[MAXLEN(album)]=0; + break; + case 6: //track + strncpy(tag->track,(char *)(frameData+dataOffset),MAXLEN(track)); + tag->track[MAXLEN(track)]=0; + break; + case 7: //composer + strncpy(tag->composer,(char *)(frameData+dataOffset),MAXLEN(composer)); + tag->composer[MAXLEN(composer)]=0; + break; + case 8: //comment + strncpy(tag->comment,(char *)(frameData+dataOffset),MAXLEN(comment)); + tag->comment[MAXLEN(comment)]=0; + break; + default: + return false; + } + + return true; +} + +/** + * parse an id3 Header + * based on code provided by Brian Walton + * @returns -1 of nothing found + * otherwise the id3 info is filled + */ + +int DemuxerAudio::parseID3V2(UCHAR *data, int len) { + int debug=0; + UCHAR * start=data; + id3_header id3header; + id3_frame id3frame; + id3_tag * id3tag=NULL; + //len = read(fd, data, 10); + if (len < 10) { + delete id3tag; + return -1; + } + len-=10; + if(data[0]=='I' && data[1]=='D' && data[2]=='3') + { + id3tag=new id3_tag(); + id3header.major = data[3]; + id3header.minor = data[4]; + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"ID3 V2.%d.%d found\n", id3header.major, id3header.minor); + id3header.flags.unsynchronisation = (data[5] & 0x80)>>7; + id3header.flags.extended_header = (data[5] & 0x40)>>6; + id3header.flags.experimental = (data[5] & 0x20)>>5; + id3header.flags.footer = (data[5] & 0x10)>>4; + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Unsynchronisation flag: %d\n", id3header.flags.unsynchronisation); + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Extended header flag: %d\n", id3header.flags.extended_header); + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Experimental indicator flag: %d\n", id3header.flags.experimental); + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Footer present flag: %d\n", id3header.flags.footer); + id3header.size = (data[6] & 0x7F) << 21 | (data[7] & 0x7F) << 14 | (data[8] & 0x7F) << 7 | (data[9] & 0x7F); + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"ID3 Size: %d\n", id3header.size); + data=start+10; + if (len <= id3header.size) { + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"header size to big %d, only %d bytes available\n",id3header.size,len); + delete id3tag; + return -1; + } + if (id3header.flags.extended_header) + { + int extended_hdr_hdr=4; //still to be discussed (id3.org...) + //read extended header size + if (len < extended_hdr_hdr) { + if (debug) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"extended header found but cannot read\n"); + delete id3tag; + return -1; + } + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"remaining %d chars after extended hdr hdr\n", len); + id3header.extended_header.size = (data[0] & 0x7F) << 21 | (data[1] & 0x7F) << 14 | (data[2] & 0x7F) << 7 | (data[3] & 0x7F); + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Extended header size: %d\n", id3header.extended_header.size); + if (len <= id3header.extended_header.size+extended_hdr_hdr) { + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"extended Header to big, only %d bytes available\n",len); + delete id3tag; + return -1; + } + //lseek(fd, id3header.extended_header.size - 6, SEEK_CUR); + data+=id3header.extended_header.size+extended_hdr_hdr; + len-=id3header.extended_header.size+extended_hdr_hdr; + } + //set the end of the header + UCHAR * eob=start+id3header.size+10; + bool readNext=true; + while (data < eob && readNext) + { + //skip over some padding - found this in lame MCDI tag... + if (*data == 0) { + data++; + continue; + } + readNext=false; + switch(id3header.major) + { + case 2: //ID3 V2.2.x + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"version 2.2 frame, %d : %c %c %c\n", data-start,*data,*(data+1),*(data+2)); + if (data + 6 >= eob) + { + break; + } + if (id3_2_2_FrameParse(data, &id3frame) < 0) + { + break; + } + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"frame size: %d\n", id3frame.size); + fillId3Tag(id3tag,data,id3frame.size,6,false); + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"frame payload: %s\n", data + 6 +1); + data+=6+id3frame.size; + readNext=true; + break; + case 3: //ID3 V2.3.x + { + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"version 2.3 frame, %d : %c %c %c %c\n", data-start, + *data,*(data+1),*(data+2),*(data+3)); + if (data + 10 >= eob) + { + break; + } + if (id3_2_3_FrameParse(data, &id3frame) <0) + { + break; + } + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Frame size: %d\n", id3frame.size); + int dataOffset=10; + if (id3frame.flags.groupId) + { + dataOffset++; + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Frame group: %d\n", data[dataOffset]); + } + if (id3frame.flags.compression) + { + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Frame compressed: %d\n", id3frame.flags.compression); + } + if (id3frame.flags.encryption) + { + dataOffset+=1; + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"Frame encryption method: %d\n", data[dataOffset]); + } + fillId3Tag(id3tag,data,id3frame.size-dataOffset+10,dataOffset,true); + if (debug != 0) log->log("DemuxerAudio::parseID3V2",Log::DEBUG,"frame payload: %s\n", data + dataOffset +1); + data+=10+id3frame.size; + readNext=true; + break; + } + default: + //don't support this version + delete id3tag; + return -1; + } + } + + data=eob; + //store the found tag + if (id3) delete id3; + id3=id3tag; + return data-start; + } + return -1; +} + +/** + * parse an id3v1 Header + * based on code provided by Brian Walton + * @returns -1 of nothing found + * otherwise the id3 info is filled + */ +#define MEMCPY(type,len,offset) {int type##max=sizeof(tag->type)-1type)-1:len;strncpy(tag->type,(char*)&data[offset],type##max);tag->type[type##max]=0;} + +int DemuxerAudio::parseID3V1(UCHAR *data, int len) { + int debug=1; + if (len < 128) return -1; + if(data[0]=='T' && data[1]=='A' && data[2]=='G') + { + id3_tag * tag=new id3_tag(); + if (debug != 0)log->log("DemuxerAudio::parseID3V1",Log::DEBUG,"ID3 V1 tag found\n"); + MEMCPY(title,30,3); + MEMCPY(artist,30,33); + MEMCPY(album,30,63); + MEMCPY(year,4,93); + if (data[125]==0 && data[126]!=0) + { //ID3 V1.1 + if (debug != 0)log->log("DemuxerAudio::parseID3V1",Log::DEBUG,"ID3 V1.1 tag\n"); + MEMCPY(comment,29,97); + sprintf(tag->track, "%d", data[126]); + } else { + if (debug != 0)log->log("DemuxerAudio::parseID3V1",Log::DEBUG,"ID3 V1.0 tag\n"); + MEMCPY(comment,30,97); + } + if (data[127] < sizeof(id3_1_genre)/sizeof(id3_1_genre[0])) + { + sprintf(tag->genre, id3_1_genre[data[127]]); + } + if (id3) delete id3; + id3=tag; + return 0; + } + return -1; +} + +//infos from http://www.multiweb.cz/twoinches/MP3inside.htm +int DemuxerAudio::parseVBR(UCHAR *data, int len) { + UCHAR *hdr=findHeader(data,len); + //we expect the header exactly here + if (hdr != data) return -1; + const static char * VBRHDR="Xing"; + int vbridpos=36; + UCHAR mpgtype=(data[1] & 0x18)>>3; + UCHAR chmode=(data[2] & 0xc0) >> 6; + UCHAR layer=(data[2] & 0x06) >>1; + if ( mpgtype == 3 && chmode == 11) vbridpos=21; + if ( mpgtype != 3 && chmode != 11) vbridpos=21; + if ( mpgtype != 3 && chmode == 11) vbridpos=13; + //check for the header ID + if (vbridpos+(int)strlen(VBRHDR)+4 >= len) { + Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"frame to short for VBR header %d",len); + return -1; + } + for (int i=4;ilog("DemuxerAudio::parseVBR",Log::DEBUG,"garbage when searching VBR header at pos %d",i); + return -1; + } + } + if ( strncmp((char *)&data[vbridpos],VBRHDR,strlen(VBRHDR)) != 0) { + Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"no VBR header at pos %d",vbridpos); + return -1; + } + int framedata=vbridpos+strlen(VBRHDR); + int expectedLen=0; + //OK we should now have a valid vbr header + bool hasFramenum=data[framedata+3] & 1; + bool hasBytes=data[framedata+3] & 0x2; + bool hasTOC=data[framedata+3] & 0x4; + expectedLen+=8; + if (hasTOC) expectedLen+=100; + if (framedata+expectedLen > len) { + Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"frame to short for VBR header data %d, expected %d", + len,framedata+expectedLen); + return -1; + } + if (!hasFramenum || ! hasBytes || ! hasTOC) { + //not usefull for us.. + Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"not all parts in VBR header - ignore"); + return -1; + } + framedata+=4; + if (vbr) delete vbr; + vbr=new vbrInfo(); + vbr->numFrames=data[framedata] << 24 | data[framedata+1]<<16| + data[framedata+2]<<8 |data[framedata+3]; + framedata+=4; + vbr->numBytes=data[framedata] << 24 | data[framedata+1]<<16| + data[framedata+2]<<8 |data[framedata+3]; + framedata+=4; + for (int ti=0;ti<100;ti++) { + vbr->table[ti]=data[framedata+ti]; + } + //compute file size in seconds + //should be (#of frames -1) *samplesPerFrame / sampleRate + //TODO: difference for Mono? + ULONG samplesPerFrame=384; //layer1 + if (layer != 3) samplesPerFrame=1152; + vbr->fileSeconds=(vbr->numFrames-1)*samplesPerFrame/hdrSamplingRate; + Log::getInstance()->log("DemuxerAudio::parseVBR",Log::DEBUG,"successfully read VBR %ldbytes, %ld frames, %ldsec", + vbr->numBytes,vbr->numFrames,vbr->fileSeconds); + return hdrFramelen; +} + + + + + + + +UCHAR * DemuxerAudio::findHeader(UCHAR *buf, int len, bool writeInfo) { + while (len >= 3) //assume hdr+crc + { + UCHAR pattern=*buf; + buf++; len--; + if (pattern != HDRBYTE1 ) continue; + if ((*buf & HDRBYTE2MASK) != HDRBYTE2) continue; + if (readHeader((buf-1),4,writeInfo) != 0) continue; + return buf-1; + } + return NULL; +} + + + +int DemuxerAudio::readHeader(UCHAR * hbuf,int len,bool writeInfo) { + int curFramelen=0; + int curBitrate=0; + int curSamplingRate=0; + if (*hbuf != HDRBYTE1) return -1; + hbuf++; + if ((*hbuf & HDRBYTE2MASK) != HDRBYTE2) return -1; + UCHAR mpgtype=(*hbuf & 0x18)>>3; + if (mpgtype == 1) { + log->log("DemuxerAudio",Log::DEBUG,"header invalid mpgtype %s %i %i %i", + mpegString(mpgtype),*hbuf,*(hbuf+1),*(hbuf+2)); + return 1; + } + UCHAR layer=(*hbuf & 0x06) >>1; + //bool hasCRC=!(*hbuf & 1); + hbuf++; + UCHAR bitrateCode=(*hbuf & 0xf0) >>4; + UCHAR samplingCode=(*hbuf & 0x0c) >> 2; + bool padding=*hbuf & 0x02; + hbuf++; + //0 Stereo, 1 JointStereo, 2 Dual, 3 Mono + UCHAR chmode=(*hbuf & 0xc0) >> 6; + //UCHAR extension=(*hbuf & 0x30) >> 4; + + //layercode: 1-L3, 2-L2, 3-L1 + //columns 0,1,2 for MPEG1 + UCHAR bitrateColumn=3-layer; + if (bitrateColumn > 2) { + log->log("DemuxerAudio",Log::DEBUG,"header invalid layer %s %i %i %i", + layerString(layer),*(hbuf-2),*(hbuf-1),*hbuf); + return 1; + } + if (mpgtype != 3) bitrateColumn+=3; + if (bitrateColumn>4) bitrateColumn=4; + curBitrate=1000*bitrateTable[bitrateCode][bitrateColumn]; + UCHAR sampleRateColumn=0; + if (mpgtype == 10) sampleRateColumn=1; + if (mpgtype == 0) sampleRateColumn=2; + curSamplingRate=samplingRateTable[samplingCode][sampleRateColumn]; + if (curSamplingRate < 0 || curBitrate < 0) { + log->log("DemuxerAudio",Log::DEBUG,"header invalid rates br=%d sr=%d %i %i %i", + curBitrate,curSamplingRate,*(hbuf-2),*(hbuf-1),*hbuf); + return 1; + } + int padbytes=0; + if (padding) { + if (layer == 3) padbytes=4; + else padbytes=1; + } + if (layer == 3) { + //Layer 1 + //FrameLengthInBytes = (12 * BitRate / SampleRate + Padding) * 4 + curFramelen=(12*curBitrate/curSamplingRate+padbytes) * 4; + } + else { + //Layer 2/3 + //FrameLengthInBytes = 144 * BitRate / SampleRate + Padding + curFramelen=144*curBitrate/curSamplingRate+padbytes; + } + //the header itself + if (curFramelen < 32) { + log->log("DemuxerAudio",Log::DEBUG,"read header %ld mpgv=%s lc=%s br=%d sr=%d, fl=%d-invalid %i %i %i", + readHeaders,mpegString(mpgtype),layerString(layer), + curBitrate,curSamplingRate,curFramelen,*(hbuf-2),*(hbuf-1),*hbuf); + return 1; + } + if (writeInfo || isStarting){ + log->log("DemuxerAudio",Log::DEBUG,"read header %ld mpgv=%s lc=%s br=%d sr=%d, fl=%d %i %i %i", + readHeaders,mpegString(mpgtype),layerString(layer), + curBitrate,curSamplingRate,curFramelen,*(hbuf-2),*(hbuf-1),*hbuf); + if (info) delete info; + info=new mpegInfo(); + strcpy(info->mpegVersion,mpegString(mpgtype)); + info->mpegLayer=4-layer; + info->bitRate=curBitrate; + info->avrBitrate=curBitrate; + info->sampleRate=curSamplingRate; + const char *chmodStr=tr("Stereo"); + switch (chmode) { + case 1: chmodStr=tr("JointStereo");break; + case 2: chmodStr=tr("Dual");break; + case 3: chmodStr=tr("Mono");break; + } + SNPRINTF(info->info,sizeof(info->info)-1,"%s",chmodStr); + } + if (isStarting) avrBitrate=curBitrate; + isStarting=false; + readHeaders++; + //moving average F=0.005 + avrBitrate=avrBitrate+((5*(curBitrate-avrBitrate))/1024); + hdrBitrate=curBitrate; + hdrFramelen=curFramelen; + hdrSamplingRate=curSamplingRate; + hasHdrInfo=true; + return 0; +} + +int DemuxerAudio::findPTS(UCHAR* buf, int len, ULLONG* dest) +{ + //we have no PTS number ... + *dest=0; + return (findHeader(buf,len) != NULL)?1:0; +} + +bool PacketBuffer::doSkip() { + if (!bufferFull()) return false; + if (skipfactor == 0) return false; + numskip++; + if (numskip >= skipfactor) { + //sent at least always 2 packets + if (numskip > skipfactor) numskip=0; + return false; + } + packetWritten(); + return true; +} + +// just handle the real stream without dealing with the header +int PacketBuffer::putInternal(UCHAR * wbuf, int len,unsigned int &packetnum) +{ + /* Important, the type passed to stream must be a mediapacket type as defined in + Draintarget.h and not the device setting of the mvp, so we have to translate it here, + in order to get it working on windows + --MR */ + UCHAR mptype=0; + switch (streamtype) { + case Audio::MPEG1_PES: //? + case Audio::MPEG2_PES: //Important, this must be a PES ! + mptype=MPTYPE_MPEG_AUDIO; break; + default: + case Audio::MP3: //this can be any Mpeg1 Audio not only layer 3 not packed into PES + mptype=MPTYPE_MPEG_AUDIO_LAYER3;break; + }; + if (bufferFull()) { +#ifndef WIN32 + if (doSkip()) return 0;//NoSkip on Windows +#endif + //we are still full - so try to write + int sent=audio->put(store+bytesWritten,framelen-bytesWritten,/*streamtype*/mptype,packetnum);packetnum++; + //log->log("DemuxerAudio::PacketBuffer",Log::DEBUG,"written %d bytes to stream (still full) pp=%d, framelen=%d, written=%d",sent,partPacket,framelen, bytesWritten ); + if (sent < (framelen - bytesWritten)) { + //packet still not written + bytesWritten+=sent; + return 0; + } + packetWritten(); + //let the demuxer come back with the rest - need to check header first + return 0; + } + if (partPacket+len >= framelen) { + //now we have at least a complete packet + int bytesConsumed=framelen-partPacket; + memcpy(store+partPacket,wbuf,bytesConsumed); + partPacket=framelen; + //log->log("DemuxerAudio::PacketBuffer",Log::DEBUG,"stored packet %ld, length %d (last %d) for stream %p",numpackets,framelen,bytesConsumed,audio ); +#ifndef WIN32 //No Skip on Windows + if (doSkip()) return bytesConsumed; +#endif + int sent=audio->put(store,framelen,mptype,packetnum);packetnum++; + bytesWritten+=sent; + //log->log("DemuxerAudio::PacketBuffer",Log::DEBUG,"written %d bytes to stream",sent ); + if (bytesWritten < framelen) { + //still not completely written + return bytesConsumed; + } + packetWritten(); + //let the player come back... + return bytesConsumed; + } + //OK packet still not complete + if (len == 0) return 0; + int bytesConsumed=len; + memcpy(store+partPacket,wbuf,bytesConsumed); + partPacket+=bytesConsumed; + return bytesConsumed; +} + +/** + major entry for data from a player + the demuxer is either in the state headerSearch (packet written or + just at the beginning), writing garbage (inSync=false) or + handling data (none set) + A header is expected at the first byte after the previous packet - + otherwise we switch to garbage where we always search for a header + (but anyway provide the data to the underlying device - it's probably + more intelligent then we are... + We only loose a correct position display. + */ +int DemuxerAudio::put(UCHAR* wbuf, int len) +{ + //return audiostream.put(wbuf,len,streamtype); + int framelen=PACKET_SIZE; + UCHAR *hdr; + int bytesConsumed=0; + int oldBytes=0; + if (tmpFill != 0 || (buffer->bufferEmpty() && len < HDRLEN)) { + //OK we have to copy everything to the tmp buffer + int cp=(UINT)len<(PACKET_SIZE-tmpFill)?(UINT)len:(PACKET_SIZE-tmpFill); + memcpy(&tmpBuffer[tmpFill],wbuf,cp); + oldBytes=tmpFill; + tmpFill+=cp; + wbuf=tmpBuffer; + len=tmpFill; + //if there is no header here and our buffer + //is empty - just wait for the next header + if (len < HDRLEN && buffer->bufferEmpty()) { + log->log("DemuxerAudio",Log::DEBUG,"len to small for header %d at bytes %ld",len,globalBytesWritten); + return cp; + } + } + while (bytesConsumed < len ) { + if (buffer->bufferFull()) { + //if this is the first part of the loop, try to write to the stream + if (bytesConsumed == 0) buffer->putInternal(wbuf,0,packetnum); + //if the buffer is full, no need to continue + if (buffer->bufferFull()) break; + } + //either we are in a packet (buffer != full && buffer != empty) + //or we are searching a header + if (buffer->bufferEmpty()) { + if (len-bytesConsumed < HDRLEN) { + // we cannot still search + if (tmpFill != 0) { + //we are already working at the buffer + break; + } + memcpy(tmpBuffer,wbuf,len-bytesConsumed); + tmpFill=len-bytesConsumed; + log->log("DemuxerAudio",Log::DEBUG,"len to small for header %d at bytes %ld",len,globalBytesWritten); + return len; + } + + int lastFramelen=hdrFramelen; + //if the header has been valid before and we are searching + //it should be here + int maxsearch=((len-bytesConsumed) < (int)PACKET_SIZE)?len-bytesConsumed:(int)PACKET_SIZE; + hdr=findHeader(wbuf,maxsearch); + if (hdr != NULL) { + //log->log("DemuxerAudio",Log::DEBUG,"header found at offset %d",hdr-wbuf); + } + //hdr now points to the new header + if (hdr != wbuf) { + //still at least bytes before the header + inSync=false; + outOfSync++; + int garbageBytes=(hdr!=NULL)?(hdr-wbuf):maxsearch; + //we consider the garbage now as an own packet + //we can consume at most our buffer + //basically the buffer should be empty here (we are + //in header search - and we fill up each garbage + + int nframesize=garbageBytes; + if (nframesize > (int)PACKET_SIZE) { + nframesize=(int)PACKET_SIZE; + } + if (! buffer->bufferEmpty()) { + log->log("DemuxerAudio",Log::WARN,"buffer not empty when having garbage to store"); + //at the end no big problem, we only write the remaining bytes that we have... + } + else { + buffer->setFramelen(nframesize); + } + log->log("DemuxerAudio",Log::DEBUG,"garbage found at packet %ld (bytes %ld) of length %d, " + "framelen set to %d (last fl=%d)", + readHeaders,globalBytesWritten,garbageBytes,buffer->getFramelen(),lastFramelen); +#ifndef WIN32 + //hmm - we assume that he low level driver is more intelligent + //and give him the data "as is" + int written=buffer->putInternal(wbuf,garbageBytes,packetnum); + globalBytesWritten+=written; + bytesConsumed+=written; + if (written != garbageBytes || hdr == NULL ) { + break; + } +#else //DS is not intelligent + globalBytesWritten+=garbageBytes; + bytesConsumed+=garbageBytes; +#endif + //OK either all the "garbage" is written or + //we found the next header as expected + //continue with the next package + wbuf=hdr; + } + //we have to wait now until the buffer is + //free for the next package + if ( ! buffer->bufferEmpty()) return bytesConsumed; + //this is the place where we start a new packet + framelen=hdrFramelen; + if ( !buffer->setFramelen(framelen) ) { + log->log("DemuxerAudio",Log::DEBUG,"unable to set framelen should=%d, current=%d", + framelen,buffer->getFramelen()); + } + inSync=true; + } + //now we are surely within a packet + int written=buffer->putInternal(wbuf,len-bytesConsumed,packetnum); + //update the status + globalBytesWritten+=written; + wbuf+=written; + bytesConsumed+=written; + if (written == 0) { + //stream is full + break; + } + //OK - handle the rest + } + if (bytesConsumed >= oldBytes) { + //if we consumed more than the old bytes - OK the buffer + //can be thrown away + tmpFill=0; + return bytesConsumed-oldBytes; + } + else { + //only consumed bytes from the buffer + if (bytesConsumed == 0) { + //restore the buffer + tmpFill=oldBytes; + return 0; + } + //shift bytes in buffer + for (int i=0;iavrBitrate=avrBitrate; + info->bitRate=hdrBitrate; + return info; +} + +const DemuxerAudio::vbrInfo * DemuxerAudio::getVBRINfo() { + return vbr; +} + +int DemuxerAudio::checkStart(UCHAR *b, int len) { + UCHAR *start=b; + int id3len=parseID3V2(b,len); + if (id3len > 0) { + char * str=id3->toString(); + log->log("DemuxerAudio",Log::DEBUG,"parseID3V2 %d bytes of %d",id3len,len); + log->log("DemuxerAudio",Log::DEBUG,"parseID3V2 found:%s",str); + delete str; + b+=id3len; + len-=id3len; + } + int vbrlen=parseVBR(b,len); + if (vbrlen > 0 ) { + hasVBRInfo=true; + b+=vbrlen; + len-=vbrlen; + } + UCHAR * hdr=findHeader(b,len,true); + if (hdr == NULL) return -1; + return hdr-start; +} + +int DemuxerAudio::checkID3(UCHAR *b, int len) { + if (len != 128) return -1; + int rt=parseID3V1(b,len); + if (rt >= 0) { + char * str=id3->toString(); + log->log("DemuxerAudio",Log::DEBUG,"parseID3V1 found:%s",str); + delete str; + } + return rt; +} + +bool DemuxerAudio::isSync() { + return inSync; +} + +UINT DemuxerAudio::getSyncErrors() { + return outOfSync; +} + +ULONG DemuxerAudio::getBytesPerSecond() +{ + ULONG bps=hdrBitrate; + if (! hasHdrInfo) return 0; + if (hdrBitrate != avrBitrate) { + //we seem to have vbr + if (hasVBRInfo) { + //vbr stuff TODO + bps=avrBitrate; + } + else { + //some guessing + bps=avrBitrate; + } + } + bps=bps/8; + return bps; +} + +ULONG DemuxerAudio::getSecondsFromLen(ULONG len) { + if (! hasHdrInfo) return 0; + if (vbr) { + //first find the index where we are between + //rough starting point: + ULONG idx=100*len/vbr->numBytes; + if (idx >= 100) idx=99; + ULONG idxPos=(vbr->table[idx]) * vbr->numBytes/256; + ULONG pbefore=idxPos; + ULONG pafter=idxPos; + //OK now we know whether we have to go up or down + if (idxPos > len) { + //down + while (idxPos > len && idx > 0) { + pafter=idxPos; + idx--; + idxPos=(vbr->table[idx]) * vbr->numBytes/256; + pbefore=idxPos; + } + //OK we are now before our postion + } + else { + //up + while (idxPos < len && idx < 100 ) { + pbefore=idxPos; + idx++; + idxPos=(vbr->table[idx]) * vbr->numBytes/256; + pafter=idxPos; + } + //after our position + if (idx > 0) idx --; + } + //idx is now the index before our position + //approximate between the 2 points + ULONG idxTime=idx * vbr->fileSeconds/100; + if (pafter == pbefore) return idxTime; + ULONG rt=idxTime+ (len-pbefore)* vbr->fileSeconds/(100 * (pafter-pbefore)) ; + if (rt > vbr -> fileSeconds) return vbr->fileSeconds; + if (rt < idxTime) return idxTime; + return rt; + } + else { + ULONG bps=getBytesPerSecond(); + if (bps == 0) return 0; + return len/bps; + } +} + +ULONG DemuxerAudio::positionFromSeconds(ULONG seconds) { + if (! hasHdrInfo) return 0; + if (vbr) { + ULONG idx=seconds*100/vbr->fileSeconds; + if (idx > 99) idx=99; + ULONG idxPos=vbr->table[idx] * vbr->numBytes/256; + ULONG idx2=idx; + if (idx < 99) idx2++; + ULONG nextPos=vbr->table[idx] * vbr->numBytes/256; + ULONG rt=idxPos+(nextPos-idxPos) * (seconds - idx * vbr->fileSeconds /256); + if (rt < idxPos) return idxPos; + if ( rt > vbr->numBytes) return vbr->numBytes; + return rt; + } + else { + ULONG bps=getBytesPerSecond(); + return bps*seconds; + } +} + +int id3_tag::stringlen(bool withTitle) const { + if (!withTitle) + return strlen(artist)+strlen(genre)+strlen(year)+strlen(album)+ + strlen(track)+strlen(composer)+strlen(comment)+8+3+ + 90; //30 chars for each name... + else + return strlen(title)+strlen(artist)+strlen(genre)+strlen(year)+strlen(album)+ + strlen(track)+strlen(composer)+strlen(comment)+8+3+ + 120; //30 chars for each name... +} +//create a string out of the id3 tag info +//delete this string afterwards if you did not provide the buffer +char * id3_tag::toString(char *b, int len, bool withTitle) const { + const char *ta=tr("Artist"); +//char *tg=tr("Genre"); +//char *ty=tr("Year"); + const char *tx=tr("Album"); +//char *to=tr("Composer"); +//char *tc=tr("Comment"); + const char *tn=tr("Track"); + /* in the moment: + Title: + Artist: + Album: year - name + Track: + */ + if (b == NULL) { + len=stringlen(withTitle); + b=new char[len]; + } + const char * del=" - "; + if (strlen(year) == 0) del=""; + if (withTitle){ + const char *tt=tr("Title"); + SNPRINTF(b,len-1,"%s: %s\n%s: %s\n%s: %s%s%s\n%s: %s", + tt,title,ta,artist,tx,year,del,album,tn,track); + } + else { +SNPRINTF(b,len-1,"%s: %s\n%s: %s%s%s\n%s: %s", + ta,artist,tx,year,del,album,tn,track); + } + b[len-1]=0; + return b; +} + +void DemuxerAudio::setSkipFactor(int factor) { + Log::getInstance()->log("DemuxerAudio",Log::DEBUG,"set skipfactor %d\n",factor); + buffer->setSkipFactor(factor); +} + diff --git a/demuxerts.h b/demuxerts.h index fa5c01b..90a1d90 100644 --- a/demuxerts.h +++ b/demuxerts.h @@ -1,96 +1,96 @@ -/* - Copyright 2006-2007 Mark Calderbank - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#ifndef DEMUXERTS_H -#define DEMUXERTS_H - -#include "mutex.h" -#include - -#include "demuxer.h" -#include "defines.h" -#include "channel.h" - -#define TS_SIZE 188 -#define TS_SIG 0x47 - -class DemuxerTS : public Demuxer -{ - public: - DemuxerTS(int p_vID = 0, int p_aID = 0, int p_subID = 0, int p_tID = 0); - void flush(); - int scan(UCHAR* buf, int len); - int findPTS(UCHAR* buf, int len, ULLONG* dest); - void setVID(int p_vID); - void setTID(int p_tID); - void setAID(int p_aID, int type, 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 PTSMap; - PTSMap pts_map; - Mutex pts_map_mutex; - void parseTSPacketDetails(PESPacket &packet); - - -}; - -#endif +/* + Copyright 2006-2007 Mark Calderbank + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef DEMUXERTS_H +#define DEMUXERTS_H + +#include "mutex.h" +#include + +#include "demuxer.h" +#include "defines.h" +#include "channel.h" + +#define TS_SIZE 188 +#define TS_SIG 0x47 + +class DemuxerTS : public Demuxer +{ + public: + DemuxerTS(int p_vID = 0, int p_aID = 0, int p_subID = 0, int p_tID = 0); + void flush(); + int scan(UCHAR* buf, int len); + int findPTS(UCHAR* buf, int len, ULLONG* dest); + void setVID(int p_vID); + void setTID(int p_tID); + void setAID(int p_aID, int type, 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 PTSMap; + PTSMap pts_map; + Mutex pts_map_mutex; + void parseTSPacketDetails(PESPacket &packet); + + +}; + +#endif diff --git a/demuxervdr.cc b/demuxervdr.cc index c3ddfa5..c5bd28d 100644 --- a/demuxervdr.cc +++ b/demuxervdr.cc @@ -1,396 +1,396 @@ -/* - Copyright 2005-2008 Mark Calderbank - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#include "demuxervdr.h" -#include "video.h" -#include "dvbsubtitles.h" -#include "log.h" - -#ifndef WIN32 -#include -#else -#define __LITTLE_ENDIAN 1234 -#define __BIG_ENDIAN 4321 -#define __BYTE_ORDER __LITTLE_ENDIAN -#endif - -#define PTS_JUMP_MARGIN 10000 -#define PTS_ALLOWANCE 90000 - -// TODO: PTS class to handle wrapping arithmetic & comparisons? -static ULLONG PTSDistance(ULLONG pts1, ULLONG pts2) -{ - // Assume pts1, pts2 < 2^33; calculate shortest distance between - ULLONG ret = (pts1 > pts2) ? pts1 - pts2 : pts2 - pts1; - if (ret > (1LL<<32)) ret = (1LL<<33) - ret; - return ret; -} - -static ULLONG PTSDifference(ULLONG pts1, ULLONG pts2) -{ - // Assume pts1, pts2 < 2^33; calculate pts1 - pts2 - if (pts1 > pts2) - return pts1 - pts2; - else - return (1LL<<33) + pts1 - pts2; -} - -DemuxerVDR::DemuxerVDR() -{ - frameCounting = false; - packetCounting = false; - subtitlePacketPosition = 0; -} - -void DemuxerVDR::reset() -{ - frameCounting = false; - packetCounting = false; - pts_map.clear(); - Demuxer::reset(); -} - -void DemuxerVDR::flush() -{ - state = 0; - submitting = false; - subtitlePacketPosition = 0; - Demuxer::flush(); -} - -int DemuxerVDR::scan(UCHAR *buf, int len) { - // Temporarily, just look for the lowest audio stream and return it - UCHAR HiByte = PESTYPE_AUDMAX; - int ret = 0; - - while (len >= 4) { - UINT pattern = 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(); - } - } - } -} +/* + Copyright 2005-2008 Mark Calderbank + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "demuxervdr.h" +#include "video.h" +#include "dvbsubtitles.h" +#include "log.h" + +#ifndef WIN32 +#include +#else +#define __LITTLE_ENDIAN 1234 +#define __BIG_ENDIAN 4321 +#define __BYTE_ORDER __LITTLE_ENDIAN +#endif + +#define PTS_JUMP_MARGIN 10000 +#define PTS_ALLOWANCE 90000 + +// TODO: PTS class to handle wrapping arithmetic & comparisons? +static ULLONG PTSDistance(ULLONG pts1, ULLONG pts2) +{ + // Assume pts1, pts2 < 2^33; calculate shortest distance between + ULLONG ret = (pts1 > pts2) ? pts1 - pts2 : pts2 - pts1; + if (ret > (1LL<<32)) ret = (1LL<<33) - ret; + return ret; +} + +static ULLONG PTSDifference(ULLONG pts1, ULLONG pts2) +{ + // Assume pts1, pts2 < 2^33; calculate pts1 - pts2 + if (pts1 > pts2) + return pts1 - pts2; + else + return (1LL<<33) + pts1 - pts2; +} + +DemuxerVDR::DemuxerVDR() +{ + frameCounting = false; + packetCounting = false; + subtitlePacketPosition = 0; +} + +void DemuxerVDR::reset() +{ + frameCounting = false; + packetCounting = false; + pts_map.clear(); + Demuxer::reset(); +} + +void DemuxerVDR::flush() +{ + state = 0; + submitting = false; + subtitlePacketPosition = 0; + Demuxer::flush(); +} + +int DemuxerVDR::scan(UCHAR *buf, int len) { + // Temporarily, just look for the lowest audio stream and return it + UCHAR HiByte = PESTYPE_AUDMAX; + int ret = 0; + + while (len >= 4) { + UINT pattern = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; + buf++; + len--; + + if (pattern < (UINT)(0x100 | PESTYPE_AUD0) || pattern > (UINT)( + 0x100 | HiByte)) + continue; + HiByte = pattern & 0xFF; + + ret = HiByte; + if (HiByte == PESTYPE_AUD0) + break; + } + return ret; +} + +int DemuxerVDR::findPTS(UCHAR* buf, int len, ULLONG* dest) { + while (len >= 14) { + UINT pattern = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; + buf++; + len--; + if (pattern < (0x100 | PESTYPE_AUD0) || pattern > (0x100 + | PESTYPE_VIDMAX)) + continue; + + if ((buf[5] & 0xC0) != 0x80) + continue; + + UINT packetlength = ((UINT) buf[3] << 8) | buf[4]; + + if (buf[6] & 0x80) // PTS_DTS_flags indicate that PTS is present + { + if ((buf[8] & 0x01) != 0x01 || (buf[10] & 0x01) != 0x01 || (buf[12] + & 0x01) != 0x01) + continue; + + *dest = ((ULLONG)(buf[8] & 0x0E) << 29) | ((ULLONG)(buf[9]) << 22) + | ((ULLONG)(buf[10] & 0xFE) << 14) | ((ULLONG)(buf[11]) + << 7) | ((ULLONG)(buf[12] & 0xFE) >> 1); + return 1; + } + + buf += 5; + len -= 5; + buf += packetlength; + len -= packetlength; + } + // No PTS found. + return 0; +} + +void DemuxerVDR::setFrameNum(ULONG frame) +{ + frameCounting = true; + frameNumber = frame; + Log::getInstance()->log("Demuxer", Log::DEBUG, "setFrameNum %d", frame); +} + +void DemuxerVDR::setPacketNum(ULONG npacket) +{ + packetCounting = true; + packetNumber = npacket; + Log::getInstance()->log("Demuxer", Log::DEBUG, "setPacketNum %d", npacket); +} + +int DemuxerVDR::put(UCHAR* buf, int len) +{ + int ret = 0; // return number of bytes consumed + if (submitting) + { + if (submitPacket(packet) == 0) // Still full! + return ret; + else + submitting = false; + } + + if (state > 0) // We are half way through a PES packet. + { + if (len >= state) // The remainder of the packet is available. + { + packet.write(buf, state); + buf += state; len -= state; ret += state; + state = 0; + parseVDRPacketDetails(); + if (submitPacket(packet) == 0) // Stream is full + { + submitting = true; + return ret; + } + } + else // Write what we have, then exit. + { + packet.write(buf, len); + state -= len; + return len; + } + } + + while (len > 0) + { + switch (state) + { + case 0: + case -1: + if (*buf == 0x00) state--; else state = 0; + buf++; len--; ret++; + break; + case -2: + if (*buf == 0x01) state--; else if (*buf != 0x00) state = 0; + buf++; len--; ret++; + break; + case -3: + if ((*buf >= PESTYPE_VID0 && *buf <= PESTYPE_VIDMAX) || + (*buf >= PESTYPE_AUD0 && *buf <= PESTYPE_AUDMAX) || + (*buf == PESTYPE_PRIVATE_1)) + { + packet.init(*buf); + state--; + } + else if (*buf == 0x00) + state = -1; + else + state = 0; + buf++; len--; ret++; + break; + case -4: + packetLength = ((UINT)*buf) << 8; + state--; + buf++; len--; ret++; + break; + case -5: + packetLength += *buf; + state--; + buf++; len--; ret++; + break; + } + + if (state == -6) // Packet header complete + { + if (len >= packetLength) // The entire packet is available. + { + packet.write(buf, packetLength); + buf += packetLength; len -= packetLength; ret += packetLength; + state = 0; + parseVDRPacketDetails(); + if (submitPacket(packet) == 0) // Stream is full + { + submitting = true; + return ret; + } + } + else // Write what we have. + { + packet.write(buf, len); + state = packetLength - len; + ret += len; + len = 0; + } + } + } + return ret; +} + +ULONG DemuxerVDR::getPacketNum() +{ + return packetNumber; +} + +ULONG DemuxerVDR::getFrameNumFromPTS(ULLONG pts) +{ + ULLONG difference = (1LL<<33); + ULONG ref_frame = 0; + int total = 0, actual = 0; + pts_map_mutex.Lock(); + PTSMap::iterator iter = pts_map.begin(); + while (iter != pts_map.end()) + { + ++total; + if (PTSDifference(iter->pts, pts) < PTS_ALLOWANCE) + { + difference = 0; + ref_frame = iter->frame; + actual = total; + break; + } + ULLONG newdiff = PTSDifference(pts, iter->pts); + if (newdiff < difference) + { + difference = newdiff; + ref_frame = iter->frame; + actual = total; + } + ++iter; + } + if (total > 1 && actual == 1) // We are using the most recent PTS ref. + { // Delete the rest. + iter = pts_map.begin(); iter++; + pts_map.erase(iter, pts_map.end()); + } + pts_map_mutex.Unlock(); + + if (difference == (1LL<<33)) + return 0; // We cannot make sense of the pts + else + return ref_frame + (ULONG)((double) (difference / 90000) *fps); +} + +void DemuxerVDR::dealWithSubtitlePacket() +{ + const UCHAR* data = packet.getData(); + UINT packetSize = packet.getSize(); + if (packetSize < 9) return; + UINT payloadOffset = data[8] + 9; + if (packetSize < payloadOffset + 5) return; + if (data[payloadOffset + 3] == 0x00) + { // Not a continuation packet + if (packetSize < payloadOffset + 7) return; + subtitlePacket.init(data[3]); + subtitlePacket.write(&data[6], payloadOffset - 6); + subtitlePacket.write(&data[payloadOffset+4], packetSize-payloadOffset-4); + subtitlePacketPosition = payloadOffset + 2; + } + else + { // Continuation packet + if (subtitlePacketPosition == 0) return; + subtitlePacket.write(&data[payloadOffset+4], packetSize-payloadOffset-4); + } + + const UCHAR* sub_data = subtitlePacket.getData(); + UINT subSize = subtitlePacket.getSize(); + while (subtitlePacketPosition < subSize) + { + if (sub_data[subtitlePacketPosition] == 0xFF) + { // Packet is complete. Switch it into the "real" packet to be submitted. + packet = subtitlePacket; + parsePacketDetails(packet); + subtitlePacketPosition = 0; // Wait for next non-continuation packet + break; + } + if (sub_data[subtitlePacketPosition] != 0x0F) + { + subtitlePacketPosition = 0; // Wait for next non-continuation packet + break; + } + if (subSize < subtitlePacketPosition + 6) break; + UINT segmentLength = (sub_data[subtitlePacketPosition + 4] << 8) + + sub_data[subtitlePacketPosition + 5]; + subtitlePacketPosition += segmentLength + 6; + } +} + +void DemuxerVDR::parseVDRPacketDetails() +{ + if (packet.getPacketType() == PESTYPE_PRIVATE_1 && packet.getSize() > 8) + { + UINT payload_begin = packet[8] + 9; + if (packet.getSize() > payload_begin) + { + UCHAR substream_type = packet[payload_begin] & 0xF0; + if (substream_type == 0x20) // Subtitles + { + dealWithSubtitlePacket(); + } + else // Not subtitles + parsePacketDetails(packet); + } + } + else // Not a private packet*/ + parsePacketDetails(packet); + + if (packetCounting && packet.getPacketType() >= PESTYPE_AUD0 && + packet.getPacketType() <= PESTYPE_AUDMAX) + { + packetNumber++; + } + + if (frameCounting && packet.findPictureHeader(h264) && + packet.getPacketType() >= PESTYPE_VID0 && + packet.getPacketType() <= PESTYPE_VIDMAX) + { + ULONG frame_num = (frameNumber)++; + if (packet.findSeqHeader(h264) > 1 && packet.hasPTS()) + { + PTSMapEntry me; + pts_map_mutex.Lock(); + if (pts_map.empty()) + { + me.pts = packet.getPTS(); + me.frame = frame_num; + pts_map_mutex.Unlock(); +Log::getInstance()->log("Demuxer", Log::DEBUG, "+* PTS INIT *+ %llu %u", me.pts, me.frame); + pts_map_mutex.Lock(); + pts_map.push_front(me); + } + me = pts_map.front(); + pts_map_mutex.Unlock(); + +// UINT fps = Video::getInstance()->getFPS(); + ULLONG pts_expected = me.pts + 90000*((ULONG)((double)(frame_num - me.frame)) / fps); + while (pts_expected > (1LL<<33)) pts_expected -= (1LL<<33); + + if (PTSDistance(pts_expected, packet.getPTS()) > PTS_JUMP_MARGIN) // PTS jump! + { +Log::getInstance()->log("Demuxer", Log::DEBUG, "+* PTS JUMP *+ %llu %u", packet.getPTS(), frame_num); + me.pts = packet.getPTS(); + me.frame = frame_num; + pts_map_mutex.Lock(); + pts_map.push_front(me); + pts_map_mutex.Unlock(); + } + } + } +} diff --git a/draintarget.h b/draintarget.h index bb8f32e..95d5e06 100644 --- a/draintarget.h +++ b/draintarget.h @@ -1,117 +1,117 @@ -/* - Copyright 2004-2005 Chris Tallon - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#ifndef DRAINTARGET_H -#define DRAINTARGET_H - -#include "defines.h" -#include - -#define MPTYPE_VIDEO_MPEG2 0x00 -#define MPTYPE_MPEG_AUDIO 0x01 -#define MPTYPE_AC3 0x02 -#define MPTYPE_AC3_PRE13 0x03 //old vdr recording compatmode -#define MPTYPE_MPEG_AUDIO_LAYER3 0x04 //for media mp3 playback -#define MPTYPE_TELETEXT 0x05 //for EBU VBI teletext -#define MPTYPE_VIDEO_H264 0x06 -#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 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 +/* + Copyright 2004-2005 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef DRAINTARGET_H +#define DRAINTARGET_H + +#include "defines.h" +#include + +#define MPTYPE_VIDEO_MPEG2 0x00 +#define MPTYPE_MPEG_AUDIO 0x01 +#define MPTYPE_AC3 0x02 +#define MPTYPE_AC3_PRE13 0x03 //old vdr recording compatmode +#define MPTYPE_MPEG_AUDIO_LAYER3 0x04 //for media mp3 playback +#define MPTYPE_TELETEXT 0x05 //for EBU VBI teletext +#define MPTYPE_VIDEO_H264 0x06 +#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 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 diff --git a/feed.h b/feed.h index 5f6c14b..972253d 100644 --- a/feed.h +++ b/feed.h @@ -1,29 +1,29 @@ -/* - 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 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 diff --git a/fonts/licensesourcesans.txt b/fonts/licensesourcesans.txt index d154618..1177330 100644 --- a/fonts/licensesourcesans.txt +++ b/fonts/licensesourcesans.txt @@ -1,93 +1,93 @@ -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. +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. diff --git a/glosdshader.cc b/glosdshader.cc index 8ef5eb0..f5e8c6f 100644 --- a/glosdshader.cc +++ b/glosdshader.cc @@ -1,85 +1,85 @@ -/* - 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; -} +/* + 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; +} diff --git a/glosdshader.h b/glosdshader.h index 3a48ef3..22f01d7 100644 --- a/glosdshader.h +++ b/glosdshader.h @@ -1,44 +1,44 @@ -/* - 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 +/* + 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 diff --git a/glshader.cc b/glshader.cc index fce9c7b..801aca4 100644 --- a/glshader.cc +++ b/glshader.cc @@ -1,107 +1,107 @@ -/* - 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; -} - +/* + 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; +} + diff --git a/glshader.h b/glshader.h index b349e4c..5f772d0 100644 --- a/glshader.h +++ b/glshader.h @@ -1,55 +1,55 @@ -/* - 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 -#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 +/* + 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 +#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 diff --git a/imagereader.h b/imagereader.h index 533a057..11c7334 100644 --- a/imagereader.h +++ b/imagereader.h @@ -1,116 +1,116 @@ -/* - Copyright 2004-2006 Chris Tallon, Andreas Vogel - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#ifndef IMAGEREADER_H -#define IMAGEREADER_H - -#include -#include -#ifndef WIN32 -#include -#endif -#include - -#include "callback.h" -#include "thread.h" - -#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 - +/* + Copyright 2004-2006 Chris Tallon, Andreas Vogel + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef IMAGEREADER_H +#define IMAGEREADER_H + +#include +#include +#ifndef WIN32 +#include +#endif +#include + +#include "callback.h" +#include "thread.h" + +#include "threadsystem.h" + + + +class MediaProvider; +class Log; + + +class ImageReader : public Thread_TYPE, public Callback +{ + public: + + //create an image reader for a media channel + //the channel must already been opened + ImageReader(int channel,MediaProvider *p); + //call shutdown before destroying the reader! + virtual ~ImageReader(); + //request the next image block + //will return the current block (if already read) and request the next from the server + //rsize will return the received len: 0 on EOF, -1 on error + //the returned buffer has to be freed outside (really use free!) + //if the buffer is not filled it will wait until the chunk is received! + int getImageChunk(ULLONG offset,UINT len, UINT * rsize,UCHAR **buffer); + + //is the reader still running? + bool isReaderRunning(); + + void shutdown(); + + //stop the reader (waits until the reader thread does not access anything) + void stop(); + + + virtual void call(void * caller); + + + protected: + void threadMethod(); + void threadPostStopCleanup(); + + private: + MediaProvider * provider; + int channel; + static const int MAXCHUNKS=2; + //start the player thread + void run(); + Log* logger; + + bool running; + bool readerRunning; + void waitTimed(int ms); + + typedef enum { + S_REQUEST=1, + S_FETCHING=2, + S_READY=3, + S_BREAK=4, + S_FREE=0 + } rstate; + + class Chunk{ + public: + UCHAR * buffer; //receive buffer (to be deallocated with free) + ULLONG offset; //offset within stream/file + UINT reqlen; //requested len + ULONG len; //received len + rstate state; //current state + Chunk() { + buffer=NULL; + offset=0; + reqlen=0; + len=0; + state=S_FREE; + } + }; + //received data + //setting state in this array requires the thread lock + Chunk data[MAXCHUNKS]; + +}; + +#endif + diff --git a/log.cc b/log.cc index ac80171..1d8c575 100644 --- a/log.cc +++ b/log.cc @@ -1,228 +1,228 @@ -/* - Copyright 2004-2005 Chris Tallon - Copyright 2003-2004 University Of Bradford - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#include "log.h" - -#include "vdr.h" - -#ifdef __ANDROID__ -#include -#endif - -Log* Log::instance = NULL; - -Log::Log() -{ - if (instance) return; - instance = this; - logfile = NULL; - initted = 0; - logLevel = 0; - extlog = NULL; -} - -Log::~Log() -{ - instance = NULL; -} - -Log* Log::getInstance() -{ - return instance; -} - -void Log::upLogLevel() -{ - if (logLevel == Log::DEBUG) - { - log("Log", logLevel, "Log level is at its highest already"); - return; - } - - logLevel++; - log("Log", logLevel, "Log level is now %i", logLevel); -} - -void Log::downLogLevel() -{ - if (logLevel == Log::CRAZY) - { - log("Log", logLevel, "Log level is at its lowest already"); - return; - } - - logLevel--; - log("Log", logLevel, "Log level is now %i", logLevel); -} - -int Log::init(int startLogLevel,const char* fileName, int tenabled) -{ - initted = 1; - logLevel = startLogLevel; - enabled = tenabled; -// logfile = fopen(fileName, "a"); -// logfile = fopen(stdout, "a"); - logfile = stdout; -// logfile = fopen("/log", "a"); - - if (logfile) return 1; - else return 0; -} - -int Log::shutdown() -{ - if (!initted) return 1; - if (enabled) fclose(logfile); - return 1; -} - -int Log::log(const char *fromModule, int level,const char* message, ...) -{ - if (!instance || !logfile) return 0; - - if (!enabled && !extlog) return 1; - if (level > logLevel) return 1; - - char buffer[151]; - int spaceLeft = 150; - -#ifndef _MSC_VER - struct timeval tv; - gettimeofday(&tv, NULL); - struct tm* tms = localtime(&tv.tv_sec); -#else - struct _timeb tb; - _ftime(&tb); - struct tm* tms = localtime(&tb.time); -#endif - spaceLeft -= strftime(buffer, spaceLeft, "%H:%M:%S.", tms); -#ifndef _MSC_VER - spaceLeft -= SNPRINTF(&buffer[150-spaceLeft], spaceLeft, "%06lu ", (unsigned long)tv.tv_usec); -#else - spaceLeft -= SNPRINTF(&buffer[150-spaceLeft], spaceLeft, "%06lu ", (unsigned long)tb.millitm); -#endif - - char levelString[10]; - if (level == CRAZY) strcpy(levelString, "[CRAZY] "); - if (level == EMERG) strcpy(levelString, "[EMERG] "); - if (level == ALERT) strcpy(levelString, "[ALERT] "); - if (level == CRIT) strcpy(levelString, "[CRIT] "); - if (level == ERR) strcpy(levelString, "[ERR] "); - if (level == WARN) strcpy(levelString, "[WARN] "); - if (level == NOTICE) strcpy(levelString, "[notice]"); - if (level == INFO) strcpy(levelString, "[info] "); - if (level == DEBUG) strcpy(levelString, "[debug] "); - -#ifndef WIN32 - spaceLeft -= SNPRINTF(&buffer[150-spaceLeft], spaceLeft, "%s %d %s - ", levelString, getpid(), fromModule); -#else - spaceLeft -= SNPRINTF(&buffer[150-spaceLeft], spaceLeft, "%s %s - ", levelString, fromModule); -#endif - - va_list ap; - va_start(ap, message); - spaceLeft = VSNPRINTF(&buffer[150-spaceLeft], spaceLeft, message, ap); - va_end(ap); - - int messageLength = strlen(buffer); - if (messageLength < 150) - { - buffer[messageLength] = '\n'; - buffer[messageLength+1] = '\0'; - } - else - { - buffer[149] = '\n'; - buffer[150] = '\0'; - } - - int success = 1; - if (enabled) - { -#ifndef __ANDROID__ - success = fputs(buffer, logfile); - fflush(NULL); -#else - int and_level=0; - switch (level) { - case CRAZY: - case ALERT: - case EMERG : - case CRIT:{ - and_level=ANDROID_LOG_FATAL; - } break; - case ERR: { - and_level=ANDROID_LOG_ERROR; - } break; - case WARN: { - and_level=ANDROID_LOG_WARN; - } break; - case NOTICE: - case INFO: { - and_level=ANDROID_LOG_INFO; - } break; - case DEBUG :{ - and_level=ANDROID_LOG_DEBUG; - } break; - }; - __android_log_vprint(and_level, fromModule, - message, ap); -#endif - } - - if (extlog) extlog->LogExtern(buffer); //Replacement for network logging - - - if (success != EOF) - return 1; - else - return 0; - -} - -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 +#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 -#ifndef WIN32 - #include - #include -#else - #include -#endif - -#include -#include -#include -#include -#include "defines.h" - - -class ExternLogger { -public: - virtual bool LogExtern(const char* message)=0; -}; - - -class Log -{ - public: - Log(); - ~Log(); - static Log* getInstance(); - - int init(int defaultLevel,const char* fileName, int enabled); - int shutdown(); - int log(const char *fromModule, int level,const char *message, ...); - 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("", Log::, ""); - -Or, a pointer can be stored and used: - -Log *myptr = Log::getInstance(); - -myptr->log("", Log::, ""); -myptr->log("", Log::, ""); - -Level usages are above. - -The message parameter in the log function can be used in the same way as printf, eg. - -myptr->log("", Log::, "Success: %s %i", stringpointer, integer); - -*/ +/* + Copyright 2004-2005 Chris Tallon + Copyright 2003-2004 University Of Bradford + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef LOG_H +#define LOG_H + +#include +#ifndef WIN32 + #include + #include +#else + #include +#endif + +#include +#include +#include +#include +#include "defines.h" + + +class ExternLogger { +public: + virtual bool LogExtern(const char* message)=0; +}; + + +class Log +{ + public: + Log(); + ~Log(); + static Log* getInstance(); + + int init(int defaultLevel,const char* fileName, int enabled); + int shutdown(); + int log(const char *fromModule, int level,const char *message, ...); + 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("", Log::, ""); + +Or, a pointer can be stored and used: + +Log *myptr = Log::getInstance(); + +myptr->log("", Log::, ""); +myptr->log("", Log::, ""); + +Level usages are above. + +The message parameter in the log function can be used in the same way as printf, eg. + +myptr->log("", Log::, "Success: %s %i", stringpointer, integer); + +*/ diff --git a/main.cc b/main.cc index ae156b0..360eea6 100644 --- a/main.cc +++ b/main.cc @@ -1,615 +1,615 @@ -/* - Copyright 2004-2005 Chris Tallon - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#include -#include -#include -#include -#ifndef WIN32 -#include -#include -#endif - -#include "defines.h" - -#ifdef HANDLE_VT_SWITCHING -#include -#include -#include -#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; -} - +/* + Copyright 2004-2005 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include +#include +#include +#ifndef WIN32 +#include +#include +#endif + +#include "defines.h" + +#ifdef HANDLE_VT_SWITCHING +#include +#include +#include +#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; +} + diff --git a/message.h b/message.h index a2c854f..b53018a 100644 --- a/message.h +++ b/message.h @@ -1,82 +1,82 @@ -/* - Copyright 2004-2005 Chris Tallon - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#ifndef MESSAGE_H -#define MESSAGE_H - -#include - -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 +/* + Copyright 2004-2005 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef MESSAGE_H +#define MESSAGE_H + +#include + +class Message; +#include "defines.h" + +// Usage of messages is more dubious now that the single master mutex lock +// protects all gui actions. Reason(s) for usage: +// 1. View A wants something to be done by View B *after* View A has been deleted +// 2. A thread wants its object/view deleting *after* the thread has exited + +// Put a justification line after call to Message* m = new Message() line +// So that the sources can be grepped for proper message useage + +class Message +{ + public: + Message(); + + void* from; + void* to; + ULONG message; + ULONG parameter; + ULONG tag; // use this for identifying which object / question is being replied to + + const static ULONG QUESTION_YES = 1; + const static ULONG CLOSE_ME = 2; + const static ULONG PLAY_SELECTED_RECORDING = 3; + const static ULONG DELETE_SELECTED_RECORDING = 4; + const static ULONG SCREENSHOT = 5; + const static ULONG CHANNEL_CHANGE = 6; + const static ULONG RESUME_SELECTED_RECORDING = 7; + const static ULONG STOP_PLAYBACK = 9; + const static ULONG SERVER_SELECTED = 10; + const static ULONG VDR_CONNECTED = 11; + const static ULONG ADD_VIEW = 12; + const static ULONG REDRAW_LANG = 14; + const static ULONG EPG = 16; + const static ULONG EPG_CLOSE = 17; + const static ULONG CHANGED_OPTIONS = 18; + const static ULONG CONNECTION_LOST = 19; + const static ULONG MOVE_RECORDING = 20; + const static ULONG UDP_BUTTON = 21; + const static ULONG PLAYER_EVENT = 22; + const static ULONG AUDIO_CHANGE_CHANNEL = 23; + const static ULONG CHILD_CLOSE = 24; + const static ULONG MOUSE_MOVE = 25; + const static ULONG MOUSE_LBDOWN = 26; + const static ULONG CHANGE_LANGUAGE = 27; + const static ULONG LAST_VIEW_CLOSE = 28; + const static ULONG CHANGED_REMOTECONTROL = 29; + const static ULONG DELETE_SELECTED_TIMER = 30; + const static ULONG CHANGED_DEVICEOPTIONS = 31; + const static ULONG TELETEXTUPDATE = 32; + const static ULONG TELETEXTUPDATEFIRSTLINE = 33; + const static ULONG SUBTITLE_CHANGE_CHANNEL = 34; + const static ULONG MOUSE_ANDROID_SCROLL = 35; +}; + +#endif diff --git a/objects.mk b/objects.mk index b1436c4..5ecff89 100644 --- a/objects.mk +++ b/objects.mk @@ -1,28 +1,28 @@ -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 - +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 + diff --git a/osd.h b/osd.h index cf3a871..0fe00b9 100644 --- a/osd.h +++ b/osd.h @@ -1,56 +1,56 @@ -/* - 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 - -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 +/* + 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 + +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 diff --git a/osdopengl.cc b/osdopengl.cc index a62a2bd..9d68a41 100644 --- a/osdopengl.cc +++ b/osdopengl.cc @@ -1,450 +1,450 @@ -/* - 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(); -} +/* + 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(); +} diff --git a/osdopengl.h b/osdopengl.h index d3825c4..853c334 100644 --- a/osdopengl.h +++ b/osdopengl.h @@ -1,147 +1,147 @@ -/* - 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 - - - -#include -#include -#include - -#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 +/* + 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 + + + +#include +#include +#include + +#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 diff --git a/osdopenvg.cc b/osdopenvg.cc index b78a1b4..28bc226 100644 --- a/osdopenvg.cc +++ b/osdopenvg.cc @@ -1,1061 +1,1061 @@ -/* - 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 - -#include "message.h" -#include "command.h" -#include "teletxt/txtfont.h" - -#include -#include -#include - -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 segments; - vector 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 (;pointlog("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;mlog("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::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); -} - - +/* + 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 + +#include "message.h" +#include "command.h" +#include "teletxt/txtfont.h" + +#include +#include +#include + +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 segments; + vector 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 (;pointlog("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;mlog("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::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); +} + + diff --git a/osdopenvg.h b/osdopenvg.h index 6882c9c..9a1e9ac 100644 --- a/osdopenvg.h +++ b/osdopenvg.h @@ -1,162 +1,162 @@ -/* - 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 - - -#include -#include -#include -#include - - - -#include "osdvector.h" -#include "defines.h" -#include "log.h" -#include "threadp.h" -#include "mutex.h" -#include "videoomx.h" - -#include - -#include -#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 vgcommands; - deque 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 font_exp_x; - - unsigned int loadTTchar(cTeletextChar c); - map 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 +/* + 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 + + +#include +#include +#include +#include + + + +#include "osdvector.h" +#include "defines.h" +#include "log.h" +#include "threadp.h" +#include "mutex.h" +#include "videoomx.h" + +#include + +#include +#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 vgcommands; + deque 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 font_exp_x; + + unsigned int loadTTchar(cTeletextChar c); + map 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 diff --git a/osdvector.cc b/osdvector.cc index fadd161..322ee4b 100644 --- a/osdvector.cc +++ b/osdvector.cc @@ -1,378 +1,378 @@ -/* - 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::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 todraw; //First figure out if a surfaces is below another surface - list::iterator itty1=scommands.begin(); - while (itty1!=scommands.end()) { - list::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::iterator curdraw=todraw.begin(); - while (curdraw!=todraw.end()) { - drawSetTrans(*(*curdraw)); - list::iterator commands=(*(*curdraw)).commands.begin(); - list::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& commands) -{ - surfaces_mutex.Lock(); - //First determine it is already in our system - list::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::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::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& commands ) -{ - - list::iterator sitty = commands.begin(); - while (sitty != commands.end()) { - removeStyleRef((*sitty).getRef()); - ImageIndex ii = (*sitty).getImageIndex(); - if (ii) removeImageRef(ii); - sitty++; - } -} - -void OsdVector::referenceSVGCommand(list& commands ) -{ - list::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::iterator mitty=monobitmaps.begin(); - while (mitty!=monobitmaps.end()) { - map::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::iterator jitty=jpegs.begin(); - while (jitty!=jpegs.end()) { - map::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,unsigned int>::iterator sitty=styles.begin(); - while (sitty!=styles.end()) { - map::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*)&c,c.rgba()))==styles.end()) - { - style_handle=styles[pair((Colour*)&c,c.rgba())]=createStyleRef(c); - } else { - style_handle=styles[pair((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*)&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*)&c,c.rgba()))==styles.end()) - { - style_handle=styles[pair((Colour*)&c,c.rgba())]=createColorRef(c); - } else { - style_handle=styles[pair((Colour*)&c,c.rgba())]; - if (styles_ref.find(style_handle)==styles_ref.end()) { - //invalid handle recreate - style_handle=styles[pair((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; -} +/* + 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::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 todraw; //First figure out if a surfaces is below another surface + list::iterator itty1=scommands.begin(); + while (itty1!=scommands.end()) { + list::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::iterator curdraw=todraw.begin(); + while (curdraw!=todraw.end()) { + drawSetTrans(*(*curdraw)); + list::iterator commands=(*(*curdraw)).commands.begin(); + list::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& commands) +{ + surfaces_mutex.Lock(); + //First determine it is already in our system + list::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::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::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& commands ) +{ + + list::iterator sitty = commands.begin(); + while (sitty != commands.end()) { + removeStyleRef((*sitty).getRef()); + ImageIndex ii = (*sitty).getImageIndex(); + if (ii) removeImageRef(ii); + sitty++; + } +} + +void OsdVector::referenceSVGCommand(list& commands ) +{ + list::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::iterator mitty=monobitmaps.begin(); + while (mitty!=monobitmaps.end()) { + map::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::iterator jitty=jpegs.begin(); + while (jitty!=jpegs.end()) { + map::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,unsigned int>::iterator sitty=styles.begin(); + while (sitty!=styles.end()) { + map::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*)&c,c.rgba()))==styles.end()) + { + style_handle=styles[pair((Colour*)&c,c.rgba())]=createStyleRef(c); + } else { + style_handle=styles[pair((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*)&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*)&c,c.rgba()))==styles.end()) + { + style_handle=styles[pair((Colour*)&c,c.rgba())]=createColorRef(c); + } else { + style_handle=styles[pair((Colour*)&c,c.rgba())]; + if (styles_ref.find(style_handle)==styles_ref.end()) { + //invalid handle recreate + style_handle=styles[pair((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; +} diff --git a/osdvector.h b/osdvector.h index 255d49a..b9474f1 100644 --- a/osdvector.h +++ b/osdvector.h @@ -1,207 +1,207 @@ -/* - 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 -#include -#include - -#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 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& 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 images_ref; - map monobitmaps; - map jpegs; - - void incStyleRef(unsigned int index); - unsigned int getStyleRef(ImageIndex index); - virtual void destroyStyleRef(unsigned int index)=0; - map,unsigned int> styles; - map styles_ref; - virtual unsigned int createStyleRef(const DrawStyle &c)=0; - virtual unsigned int createColorRef(const Colour &c)=0; - - void dereferenceSVGCommand(list& commands ); - void referenceSVGCommand(list& commands ); - void cleanupOrphanedRefs(); - - - - virtual void drawSetTrans(SurfaceCommands & sc)=0; - virtual void executeDrawCommand(SVGCommand & command)=0; - - - - - list scommands; - - Mutex surfaces_mutex; - - void drawSurfaces(); -}; - - - - -#endif +/* + 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 +#include +#include + +#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 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& 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 images_ref; + map monobitmaps; + map jpegs; + + void incStyleRef(unsigned int index); + unsigned int getStyleRef(ImageIndex index); + virtual void destroyStyleRef(unsigned int index)=0; + map,unsigned int> styles; + map styles_ref; + virtual unsigned int createStyleRef(const DrawStyle &c)=0; + virtual unsigned int createColorRef(const Colour &c)=0; + + void dereferenceSVGCommand(list& commands ); + void referenceSVGCommand(list& commands ); + void cleanupOrphanedRefs(); + + + + virtual void drawSetTrans(SurfaceCommands & sc)=0; + virtual void executeDrawCommand(SVGCommand & command)=0; + + + + + list scommands; + + Mutex surfaces_mutex; + + void drawSurfaces(); +}; + + + + +#endif diff --git a/osdwin.cc b/osdwin.cc index 83560b9..c55bb11 100644 --- a/osdwin.cc +++ b/osdwin.cc @@ -1,603 +1,603 @@ -/* - Copyright 2004-2005 Chris Tallon - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - - -#include "osdwin.h" -#include "mtd.h" -#include "videowin.h" -#include "surfacewin.h" - -#include "dsallocator.h" -#include "message.h" -#include "command.h" - -#define BACKBUFFER_WIDTH 1280 -#define BACKBUFFER_HEIGHT 720 - - - -typedef HRESULT (__stdcall *FCT_DXVA2CreateDirect3DDeviceManager9)(UINT* pResetToken, IDirect3DDeviceManager9** ppDeviceManager); -typedef HRESULT (__stdcall *FCT_MFCreateVideoSampleFromSurface)(IUnknown* pUnkSurface, IMFSample** ppSample); - -FCT_DXVA2CreateDirect3DDeviceManager9 ptrDXVA2CreateDirect3DDeviceManager9=NULL; -FCT_MFCreateVideoSampleFromSurface ptrMFCreateVideoSampleFromSurface=NULL; - -//This is stuff for rendering the OSD - - -OsdWin::OsdWin() -{ - d3d=NULL; - d3ddevice=NULL; - d3drtsurf=NULL; - swappy=NULL; - swapsurf=NULL; - evrstate=EVR_pres_off; - window=NULL; - - external_driving=false; - dsallocator=NULL; - filter_type=D3DTEXF_FORCE_DWORD; - lastrendertime=timeGetTime(); - event = CreateEvent(NULL,/*FALSE*/TRUE,FALSE,NULL); - d3dmutex = CreateMutex(NULL,FALSE,NULL); - /*EVR stuff*/ - dxvadevicehandle=NULL; - evrsupported=true; - HMODULE hlib=NULL; - hlib=LoadLibrary("dxva2.dll"); - if (!hlib) { - evrsupported=false; - return; - } - ptrDXVA2CreateDirect3DDeviceManager9=(FCT_DXVA2CreateDirect3DDeviceManager9)GetProcAddress(hlib, "DXVA2CreateDirect3DDeviceManager9"); - if (!ptrDXVA2CreateDirect3DDeviceManager9){ - evrsupported=false; - return; - } - - hlib=LoadLibrary("evr.dll"); - if (!hlib) { - evrsupported=false; - return; - } - - ptrMFCreateVideoSampleFromSurface = (FCT_MFCreateVideoSampleFromSurface)GetProcAddress(hlib,"MFCreateVideoSampleFromSurface"); - if (!ptrMFCreateVideoSampleFromSurface){ - evrsupported=false; - return; - } - -} - -OsdWin::~OsdWin() -{ - - if (initted) - { - threadStop(); - shutdown(); - } - CloseHandle(event); - CloseHandle(d3dmutex); -} - -int OsdWin::getFD() -{ - if (!initted) return 0; - return fdOsd; -} - -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(); -} +/* + Copyright 2004-2005 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + + +#include "osdwin.h" +#include "mtd.h" +#include "videowin.h" +#include "surfacewin.h" + +#include "dsallocator.h" +#include "message.h" +#include "command.h" + +#define BACKBUFFER_WIDTH 1280 +#define BACKBUFFER_HEIGHT 720 + + + +typedef HRESULT (__stdcall *FCT_DXVA2CreateDirect3DDeviceManager9)(UINT* pResetToken, IDirect3DDeviceManager9** ppDeviceManager); +typedef HRESULT (__stdcall *FCT_MFCreateVideoSampleFromSurface)(IUnknown* pUnkSurface, IMFSample** ppSample); + +FCT_DXVA2CreateDirect3DDeviceManager9 ptrDXVA2CreateDirect3DDeviceManager9=NULL; +FCT_MFCreateVideoSampleFromSurface ptrMFCreateVideoSampleFromSurface=NULL; + +//This is stuff for rendering the OSD + + +OsdWin::OsdWin() +{ + d3d=NULL; + d3ddevice=NULL; + d3drtsurf=NULL; + swappy=NULL; + swapsurf=NULL; + evrstate=EVR_pres_off; + window=NULL; + + external_driving=false; + dsallocator=NULL; + filter_type=D3DTEXF_FORCE_DWORD; + lastrendertime=timeGetTime(); + event = CreateEvent(NULL,/*FALSE*/TRUE,FALSE,NULL); + d3dmutex = CreateMutex(NULL,FALSE,NULL); + /*EVR stuff*/ + dxvadevicehandle=NULL; + evrsupported=true; + HMODULE hlib=NULL; + hlib=LoadLibrary("dxva2.dll"); + if (!hlib) { + evrsupported=false; + return; + } + ptrDXVA2CreateDirect3DDeviceManager9=(FCT_DXVA2CreateDirect3DDeviceManager9)GetProcAddress(hlib, "DXVA2CreateDirect3DDeviceManager9"); + if (!ptrDXVA2CreateDirect3DDeviceManager9){ + evrsupported=false; + return; + } + + hlib=LoadLibrary("evr.dll"); + if (!hlib) { + evrsupported=false; + return; + } + + ptrMFCreateVideoSampleFromSurface = (FCT_MFCreateVideoSampleFromSurface)GetProcAddress(hlib,"MFCreateVideoSampleFromSurface"); + if (!ptrMFCreateVideoSampleFromSurface){ + evrsupported=false; + return; + } + +} + +OsdWin::~OsdWin() +{ + + if (initted) + { + threadStop(); + shutdown(); + } + CloseHandle(event); + CloseHandle(d3dmutex); +} + +int OsdWin::getFD() +{ + if (!initted) return 0; + return fdOsd; +} + +Surface * OsdWin::createNewSurface(){ + return (Surface*)new SurfaceWin(); +} + +int OsdWin::init(void* device) +{ + if (initted) return 0; + Video* video = Video::getInstance(); + window=*((HWND*)device); + //First Create Direct 3D Object + d3d=Direct3DCreate9(D3D_SDK_VERSION); + if (!d3d) + { + Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 object!"); + return 0; + } + // then create the Device + D3DPRESENT_PARAMETERS d3dparas; + ZeroMemory(&d3dparas,sizeof(d3dparas)); + d3dparas.BackBufferWidth=BACKBUFFER_WIDTH; + d3dparas.BackBufferHeight=BACKBUFFER_HEIGHT; + d3dparas.Windowed=TRUE; + d3dparas.SwapEffect=D3DSWAPEFFECT_COPY; + if (d3d->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,*((HWND*) device), + D3DCREATE_SOFTWARE_VERTEXPROCESSING |D3DCREATE_MULTITHREADED,&d3dparas,&d3ddevice)!=D3D_OK) { + Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 device!"); + return 0; + } + d3ddevice->GetRenderTarget(0,&d3drtsurf); + + /* + if (!InitVertexBuffer()) { + Log::getInstance()->log("OSD", Log::WARN, "Could not create Direct3D9 vertex buf!"); + return 0; + }*/ + /* We have to determine which kind of filtering is supported*/ + D3DCAPS9 caps; + d3ddevice->GetDeviceCaps(&caps); + if ( ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFLINEAR)!=0) + && ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFLINEAR)!=0)) { + if (filter_type==D3DTEXF_FORCE_DWORD) { + filter_type=D3DTEXF_LINEAR; + } + } else { + if (filter_type==D3DTEXF_LINEAR) + { + filter_type=D3DTEXF_POINT; + } + } + + if ( ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFPOINT)!=0) + && ((caps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFPOINT)!=0)) { + if (filter_type==D3DTEXF_FORCE_DWORD) + { + filter_type=D3DTEXF_POINT; + } + } else { + if (filter_type==D3DTEXF_POINT) { + filter_type=D3DTEXF_NONE; + } + } + if (filter_type==D3DTEXF_FORCE_DWORD) { + filter_type=D3DTEXF_NONE; + } + + if (evrsupported) + { + if (ptrDXVA2CreateDirect3DDeviceManager9(&dxvatoken,&d3ddevman)!=S_OK) evrsupported=false; + else { + d3ddevman->ResetDevice(d3ddevice,dxvatoken); + } + } + + + + + //Now we will create the Screen + screen = new SurfaceWin(Surface::SCREEN); + SetEvent(event);//Now all devices are ready + screen->create(video->getScreenWidth(), video->getScreenHeight()); + screen->display(); + initted = 1; // must set this here or create surface won't work + threadStart(); + + return 1; +} + +void OsdWin::LockDevice() +{ + if (!evrsupported) return; + if (!dxvadevicehandle) + { + d3ddevman->OpenDeviceHandle(&dxvadevicehandle); + } + IDirect3DDevice9 *temp; + d3ddevman->LockDevice(dxvadevicehandle,&temp,TRUE); + +} + +void OsdWin::UnlockDevice() +{ + if (!evrsupported) return; + if (!initted) return; + d3ddevman->UnlockDevice(dxvadevicehandle,TRUE); + if (dxvadevicehandle) + { + d3ddevman->CloseDeviceHandle(dxvadevicehandle); + dxvadevicehandle=NULL; + } + +} + +DWORD OsdWin::getFilterCaps() +{ + if (!initted) return NULL; + D3DCAPS9 caps; + d3ddevice->GetDeviceCaps(&caps); + return caps.StretchRectFilterCaps; +} + +LPDIRECT3DVERTEXBUFFER9 OsdWin::InitVertexBuffer(DWORD width, DWORD height) +{ + LPDIRECT3DVERTEXBUFFER9 ret =NULL; + Video* video=Video::getInstance(); + FLOAT texx=((float)video->getScreenWidth())/1024.f; + FLOAT texy=((float)video->getScreenHeight())/1024.f; + D3DCOLOR osdcolor=D3DCOLOR_RGBA(255,255,255,255); + osdvertices[0].c=osdcolor; + osdvertices[0].x=0.f-0.5f; + osdvertices[0].y=0.f-0.5f; + osdvertices[0].z=0.5f; + osdvertices[0].rhw=1.f; + osdvertices[0].u=0.f; + osdvertices[0].v=0.f; + osdvertices[1].c=osdcolor; + osdvertices[1].x=((float)width)-0.5f; + osdvertices[1].y=0.f-0.5f; + osdvertices[1].z=0.5f; + osdvertices[1].u=texx; + osdvertices[1].v=0.f; + osdvertices[1].rhw=1.f; + osdvertices[2].c=osdcolor; + osdvertices[2].x=((float)width)-0.5f; + osdvertices[2].y=((float)height)-0.5f; + osdvertices[2].z=0.5f; + osdvertices[2].rhw=1.f; + osdvertices[2].u=texx; + osdvertices[2].v=texy; + osdvertices[3].c=osdcolor; + osdvertices[3].x=0.f-0.5f; + osdvertices[3].y=((float)height)-0.5f; + osdvertices[3].z=0.5f; + osdvertices[3].rhw=1.f; + osdvertices[3].u=0.f; + osdvertices[3].v=texy; + + if (d3ddevice->CreateVertexBuffer(4*sizeof(OSDVERTEX),0,D3DFVF_OSDVERTEX,D3DPOOL_MANAGED, + &ret,NULL)!=D3D_OK) { + return NULL; + } + void *pvertex=NULL; + if (ret->Lock(0,sizeof(osdvertices),&pvertex,0)!=D3D_OK) { + return NULL; + } + memcpy(pvertex,osdvertices,sizeof(osdvertices)); + ret->Unlock(); + return ret; +} + +int OsdWin::shutdown() +{ + if (!initted) return 0; + initted = 0; + evrsupported=0; + if (d3ddevman) d3ddevman->Release(); + d3drtsurf->Release(); + d3ddevice->Release(); + d3d->Release(); + if (swapsurf) swapsurf->Release(); + if (swappy) swappy->Release(); + + return 1; +} + +void OsdWin::screenShot(const char* fileName) +{ + screen->screenShot(fileName); +} + +void OsdWin::threadMethod() +{ + while (true) + { + DWORD waittime=10; + if (initted){ + if (evrstate==EVR_pres_off || evrstate==EVR_pres_pause) + { + Render(); + } else if (evrstate==EVR_pres_started) + { + LPDIRECT3DSURFACE9 surf; + if (dsallocator) dsallocator->GetNextSurface(&surf,&waittime); + if (surf==NULL) + { + Render(); + } + else + { + RenderDS(surf); + surf->Release(); + if (dsallocator) dsallocator->DiscardSurfaceandgetWait(&waittime); + } + } + } + threadCheckExit(); + if (waittime!=0) Sleep(min(10,waittime)); + //Sleep(1); + } +} + + +void OsdWin::threadPostStopCleanup() +{ + //Doing nothing + //goo; +} + + +// This function is called from the WinMain function in order to get Screen updates +void OsdWin::Render() +{ + if (!initted) return ; + if (external_driving) { + DWORD time1=timeGetTime(); //Updates the Menue + if ((time1-lastrendertime)>200) {//5 fps for OSD updates are enough, avoids tearing + InternalRendering(NULL); + lastrendertime=timeGetTime(); + } else { + //Sleep(5); //Sleep for 5 ms, in order to avoid blocking the other threads + } + } else { + DWORD time1=timeGetTime(); + if ((time1-lastrendertime)>50) {//10 fps for OSD updates are enough, avoids tearing + InternalRendering(NULL); + lastrendertime=timeGetTime(); + } else { + //Sleep(5); + + } + + } +} + +void OsdWin::RenderDS(LPDIRECT3DSURFACE9 present){ + if (!initted) return; + if (external_driving) { + InternalRendering(present); + lastrendertime=timeGetTime(); + } +} + + +void OsdWin::InternalRendering(LPDIRECT3DSURFACE9 present){ + BeginPainting(); + HRESULT losty=d3ddevice->TestCooperativeLevel(); + if (losty==D3DERR_DEVICELOST) { + //Sleep(10); + EndPainting(); + return; //Device Lost + } + if (losty==D3DERR_DEVICENOTRESET){ + EndPainting(); + DoLost(); + return; + } + WaitForSingleObject(event,INFINITE); + + + + + LPDIRECT3DSURFACE9 targetsurf; + if (swappy) + { + targetsurf=swapsurf; + d3ddevice->SetRenderTarget(0,swapsurf);//Stupid VMR manipulates the render target + } + else + { + targetsurf=d3drtsurf; + d3ddevice->SetRenderTarget(0,d3drtsurf);//Stupid VMR manipulates the render target + } + D3DSURFACE_DESC targetdesc; + targetsurf->GetDesc(&targetdesc); + + if (external_driving) { + //Copy video to Backbuffer + if (present!=NULL ) { + VideoWin* video =(VideoWin*) Video::getInstance(); + /*calculating destination rect */ + RECT destrect={0,0,/*video->getScreenWidth()*/ targetdesc.Width, + /*video->getScreenHeight()*/targetdesc.Height}; + UCHAR mode=video->getMode(); + switch (mode) { + case Video::EIGHTH: + destrect.right=destrect.right/2; + destrect.bottom=destrect.bottom/2; + case Video::QUARTER: + destrect.right=destrect.right/2+video->getPosx()*2; + destrect.bottom=destrect.bottom/2+video->getPosy()*2; + destrect.left=video->getPosx()*2; + destrect.top=video->getPosy()*2; + d3ddevice->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0f,0); + break; + }; + D3DSURFACE_DESC surf_desc; + present->GetDesc(&surf_desc);//for chop sides + RECT sourcerect= {0,0,surf_desc.Width,surf_desc.Height}; + if (video->getPseudoTVsize()==Video::ASPECT4X3 + && video->getMode()==Video::NORMAL + && video->getAspectRatio()==Video::ASPECT16X9) { + unsigned int correction=((double) (surf_desc.Width))*4.*9./3./16.; + sourcerect.left=(surf_desc.Width-correction)/2; + sourcerect.right=sourcerect.left+correction; + } + d3ddevice->StretchRect(present,&sourcerect,targetsurf ,&destrect,filter_type); + + } + } else { + VideoWin* video =(VideoWin*) Video::getInstance(); + //Clear Background + if (!video->isVideoOn()) d3ddevice->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0f,0); + } + LPDIRECT3DVERTEXBUFFER9 vb=NULL; + vb=InitVertexBuffer(targetdesc.Width,targetdesc.Height); + + //Drawing the OSD + if (d3ddevice->BeginScene()==D3D_OK) { + d3ddevice->SetStreamSource(0,vb,0,sizeof(OSDVERTEX)); + d3ddevice->SetFVF(D3DFVF_OSDVERTEX); + d3ddevice->SetTexture(0,((SurfaceWin*)screen)->getD3dtexture()); + //d3ddevice->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_MODULATE); + d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP,D3DTOP_MODULATE); + + d3ddevice->SetSamplerState(0,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR); + d3ddevice->SetSamplerState(0,D3DSAMP_MINFILTER,D3DTEXF_LINEAR); + + + d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE); + d3ddevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA); + d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + d3ddevice->SetRenderState(D3DRS_LIGHTING,FALSE); + + + d3ddevice->DrawPrimitive(D3DPT_TRIANGLEFAN,0,2); + d3ddevice->EndScene(); + //Show it to the user! + HRESULT hres; + if (swappy) + { + if (hres=swappy->Present(NULL,NULL,NULL,NULL,0)==D3DERR_DEVICELOST){ + //EndPainting(); + if (!external_driving) DoLost(); + } + } + else + { + if (hres=d3ddevice->Present(NULL,NULL,NULL,NULL)==D3DERR_DEVICELOST){ + //EndPainting(); + if (!external_driving) DoLost(); + } + } + + } + + vb->Release(); + EndPainting(); + + +// if (!external_driving) { +// Sleep(4);//The User can wait for 4 milliseconds to see his changes +// } +} + +bool OsdWin::DoLost(){ + Log::getInstance()->log("OSD", Log::WARN, "Direct3D Device Lost! Reobtaining..."); + ResetEvent(event); + if (external_driving && dsallocator!=NULL) { + dsallocator->LostDevice(d3ddevice,d3d); //Propagate the information through DS + } + //First we free up all resources + Video* video = Video::getInstance(); + ((SurfaceWin*)screen)->ReleaseSurface(); + if (d3drtsurf) d3drtsurf->Release(); + d3drtsurf=NULL; + D3DPRESENT_PARAMETERS d3dparas; + ZeroMemory(&d3dparas,sizeof(d3dparas)); + d3dparas.BackBufferWidth=BACKBUFFER_WIDTH; + d3dparas.BackBufferHeight=BACKBUFFER_HEIGHT; + d3dparas.Windowed=TRUE; + d3dparas.SwapEffect=D3DSWAPEFFECT_COPY; + + if (swapsurf) {swapsurf->Release();swapsurf=NULL;}; + if (swappy) {swappy->Release();swappy=NULL;}; + + if (d3ddevice->Reset(&d3dparas)!=D3D_OK){ + return false; + } + d3ddevice->GetRenderTarget(0,&d3drtsurf); + if (d3ddevman) d3ddevman->ResetDevice(d3ddevice,dxvatoken); + //InitVertexBuffer(); + //Redraw Views, Chris could you add a member function to BoxStack, so that + // I can cause it to completely redraw the Views? + // Otherwise the OSD would be distorted after Device Lost + // FIXME + + SetEvent(event); + + + screen->create(video->getScreenWidth(), video->getScreenHeight()); + screen->display(); + + return true; + +} +LPDIRECT3DDEVICE9 OsdWin::getD3dDev() { + WaitForSingleObject(event,INFINITE);//We will only return if we are initted + return d3ddevice; +} + +LPDIRECT3D9 OsdWin::getD3d() { + WaitForSingleObject(event,INFINITE);//We will only return if we are initted + return d3d; +} + +void OsdWin::BeginPainting() {//We synchronize calls to d3d between different threads + WaitForSingleObject(d3dmutex,INFINITE); + LockDevice(); +} + +void OsdWin::EndPainting() { + UnlockDevice(); + ReleaseMutex(d3dmutex); +} + +void OsdWin::setExternalDriving(DsAllocator* dsall,DWORD width, DWORD height) { + + if (swappy) + { + BeginPainting(); + d3ddevice->StretchRect(swapsurf,NULL,d3drtsurf,NULL,filter_type); + LPDIRECT3DSWAPCHAIN9 temp=swappy; + LPDIRECT3DSURFACE9 tempsurf=swapsurf; + swappy=NULL; + swapsurf=NULL; + EndPainting(); + tempsurf->Release(); + temp->Release(); + } + + if (dsall==NULL) { + external_driving=false; + dsallocator=NULL; + return; + } + WaitForSingleObject(event,INFINITE);//We will only return if we are initted + BeginPainting(); + + if (width>BACKBUFFER_WIDTH || height>BACKBUFFER_HEIGHT) + { + D3DPRESENT_PARAMETERS d3dparas; + ZeroMemory(&d3dparas,sizeof(d3dparas)); + d3dparas.BackBufferWidth=width; + d3dparas.BackBufferHeight=height; + d3dparas.Windowed=TRUE; + d3dparas.SwapEffect=D3DSWAPEFFECT_COPY; + if (d3ddevice->CreateAdditionalSwapChain(&d3dparas,&swappy)!=D3D_OK){ + Log::getInstance()->log("OSD", Log::WARN, "Could not create Swap Chain!"); + //return 0; + } else { + swappy->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&swapsurf); + } + Log::getInstance()->log("OSD", Log::INFO, "Create Additional Swap Chain %d %d!",width,height); + } + + dsallocator=dsall; + external_driving=true; + + EndPainting(); +} + +void OsdWin::Blank() { + WaitForSingleObject(event,INFINITE); + BeginPainting(); + d3ddevice->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0f,0); + EndPainting(); +} diff --git a/osdwin.h b/osdwin.h index 81f64f5..d904853 100644 --- a/osdwin.h +++ b/osdwin.h @@ -1,124 +1,124 @@ -/* - Copyright 2004-2005 Chris Tallon - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#ifndef OSDWIN_H -#define OSDWIN_H - -#include - -#include "osd.h" -#include "defines.h" -#include "log.h" -#include "threadwin.h" -#include -#include -#include - -struct OSDVERTEX -{ - FLOAT x,y,z,rhw; - DWORD c; - FLOAT u,v; -}; - -#define D3DFVF_OSDVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE| D3DFVF_TEX1) - -class DsAllocator; - - - -class OsdWin : public Osd, public ThreadWin -{ - public: - OsdWin(); - ~OsdWin(); - - int init(void* device); - int shutdown(); - - int getFD(); - - void screenShot(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 +/* + Copyright 2004-2005 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef OSDWIN_H +#define OSDWIN_H + +#include + +#include "osd.h" +#include "defines.h" +#include "log.h" +#include "threadwin.h" +#include +#include +#include + +struct OSDVERTEX +{ + FLOAT x,y,z,rhw; + DWORD c; + FLOAT u,v; +}; + +#define D3DFVF_OSDVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE| D3DFVF_TEX1) + +class DsAllocator; + + + +class OsdWin : public Osd, public ThreadWin +{ + public: + OsdWin(); + ~OsdWin(); + + int init(void* device); + int shutdown(); + + int getFD(); + + void screenShot(const char* fileName); + + Surface * createNewSurface(); + + void threadMethod(); + void threadPostStopCleanup(); + + LPDIRECT3DDEVICE9 getD3dDev() ; + LPDIRECT3D9 getD3d() ; + // This function is called from the WinMain function in order to get Screen updates + void Render(); + void RenderDS(LPDIRECT3DSURFACE9 present); + void BeginPainting(); + void EndPainting(); + void setExternalDriving(DsAllocator* dsall,DWORD width, DWORD height); + void Blank(); + DWORD getFilterCaps(); + DWORD getFilterType(){return filter_type;}; + void setFilterType(D3DTEXTUREFILTERTYPE type) {filter_type=type;}; + + + + + + enum EVR_state { + EVR_pres_off=0, + EVR_pres_started, + EVR_pres_pause + }; + + void SetEVRStatus(EVR_state new_state){evrstate=new_state;}; + + IDirect3DDeviceManager9 * getD3dMan() {return d3ddevman;}; + bool IsEvrSupported() {return evrsupported;}; + HWND getWindow() {return window;}; + +private: + void LockDevice(); + void UnlockDevice(); + + LPDIRECT3D9 d3d; + LPDIRECT3DDEVICE9 d3ddevice; +// LPDIRECT3DVERTEXBUFFER9 d3dvb; + LPDIRECT3DSURFACE9 d3drtsurf; + LPDIRECT3DSWAPCHAIN9 swappy; + LPDIRECT3DSURFACE9 swapsurf; + DsAllocator* dsallocator; + // This indicates, that currently a video is played, thus the osd updates are driven by the Directshow Filtersystem + bool external_driving; + HANDLE d3dmutex; + DWORD lastrendertime; + void InternalRendering(LPDIRECT3DSURFACE9 present); + bool DoLost(); + LPDIRECT3DVERTEXBUFFER9 InitVertexBuffer(DWORD width, DWORD height); + OSDVERTEX osdvertices[4]; + HANDLE event; + D3DTEXTUREFILTERTYPE filter_type; + EVR_state evrstate; + bool evrsupported; + HWND window; + + UINT dxvatoken; + IDirect3DDeviceManager9 *d3ddevman; + HANDLE dxvadevicehandle; +}; + +#endif diff --git a/player.cc b/player.cc index ddff93f..c8f9329 100644 --- a/player.cc +++ b/player.cc @@ -1,1421 +1,1421 @@ -/* - 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 +/* + 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 diff --git a/player.h b/player.h index 5c9d7f7..12de87a 100644 --- a/player.h +++ b/player.h @@ -1,237 +1,237 @@ -/* - Copyright 2004-2008 Chris Tallon - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#ifndef PLAYER_H -#define PLAYER_H - -#include -#include -#ifndef WIN32 -#include -#endif -#include - -#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 - -*/ +/* + Copyright 2004-2008 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef PLAYER_H +#define PLAYER_H + +#include +#include +#ifndef WIN32 +#include +#endif +#include + +#include "threadsystem.h" + +#include "callback.h" +#include "defines.h" +#include "vfeed.h" +#include "afeed.h" +#include "tfeed.h" + +#include "teletextdecodervbiebu.h" + +class MessageQueue; +class Audio; +class Video; +class VDR; +class Log; +class Demuxer; +class OSDReceiver; +class DVBSubtitles; +class Channel; + +class Player : public Thread_TYPE, public Callback +{ + public: + Player(MessageQueue* messageQueue, void* messageReceiver, OSDReceiver* osdReceiver); + virtual ~Player(); + + int init(bool p_isPesRecording,double framespersec); + int shutdown(); + void setStartFrame(ULONG frameNum); + void setLengthBytes(ULLONG length); + void setLengthFrames(ULONG length); + void setAudioChannel(int newChannel, int type, 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 + +*/ diff --git a/playerliveradio.cc b/playerliveradio.cc index 0a2fe57..9a4a1bb 100644 --- a/playerliveradio.cc +++ b/playerliveradio.cc @@ -1,504 +1,504 @@ -/* - 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 (jnumAPids && !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 (jnumDPids && !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"); -} - +/* + 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 (jnumAPids && !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 (jnumDPids && !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"); +} + diff --git a/playerlivetv.cc b/playerlivetv.cc index bbd3928..f2fc417 100644 --- a/playerlivetv.cc +++ b/playerlivetv.cc @@ -1,815 +1,815 @@ -/* - 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 (jnumAPids && !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 (jnumDPids && !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"); -} - +/* + 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 (jnumAPids && !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 (jnumDPids && !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"); +} + diff --git a/playerradio.cc b/playerradio.cc index 1b8e8ba..6d5d6b3 100644 --- a/playerradio.cc +++ b/playerradio.cc @@ -1,594 +1,594 @@ -/* - Copyright 2004-2006 Chris Tallon - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#include "playerradio.h" - -#include "log.h" -#include "audio.h" -#include "video.h" -#include "demuxervdr.h" -#include "demuxerts.h" -#include "remote.h" -#include "vdr.h" -#include "message.h" -#include "messagequeue.h" - -// ----------------------------------- Called from outside, one offs or info funcs - -PlayerRadio::PlayerRadio(MessageQueue* tmessageQueue, void* tmessageReceiver) -: afeed(this) -{ - messageQueue = tmessageQueue; - messageReceiver = tmessageReceiver; - audio = Audio::getInstance(); - logger = Log::getInstance(); - vdr = VDR::getInstance(); - initted = false; - lengthBytes = 0; - lengthPackets = 0; - streamPos = 0; - state = S_STOP; - - startPTS = 0; - lengthSeconds = 0; - - threadBuffer = NULL; - - blockSize = 10000; - startupBlockSize = 20000; - - Video::getInstance()->turnVideoOff(); -} - -PlayerRadio::~PlayerRadio() -{ - if (initted) shutdown(); -} - -int PlayerRadio::init(ULLONG tlengthBytes, ULONG tlengthPackets, bool isPesRecording) -{ - if (initted) return 0; -#ifndef WIN32 - pthread_mutex_init(&mutex, NULL); -#else - mutex=CreateMutex(NULL,FALSE,NULL); -#endif - - if (isPesRecording) - demuxer = new DemuxerVDR(); - else - demuxer = new DemuxerTS(); - if (!demuxer) return 0; - - if (!demuxer->init(this, audio, NULL, NULL, 0, 40000, 0)) - { - logger->log("PlayerRadio", Log::ERR, "Demuxer failed to init"); - shutdown(); - return 0; - } - - afeed.init(); - audio->stop(); - - lengthBytes = tlengthBytes; - lengthPackets = tlengthPackets; - - logger->log("PlayerRadio", Log::DEBUG, "PlayerRadio has received length bytes of %llu", lengthBytes); - - UINT thisRead = 0; - int success; - - UCHAR* buffer = vdr->getBlock(0, 10000, &thisRead); - if (!buffer) - { - logger->log("PlayerRadio", Log::ERR, "Failed to get start block"); - shutdown(); - if (!vdr->isConnected()) doConnectionLost(); - return 0; - } - - success = demuxer->findPTS(buffer, thisRead, &startPTS); - if (!success) - { - logger->log("PlayerRadio", Log::ERR, "Failed to get start PTS"); - free(buffer); - shutdown(); - return 0; - } - - free(buffer); - - if (!setLengthSeconds()) - { - logger->log("PlayerRadio", Log::ERR, "Failed to setLengthSeconds"); - shutdown(); - return 0; - } - - initted = true; - return 1; -} - -bool PlayerRadio::setLengthSeconds() -{ - int success; - ULLONG endPTS = 0; - UINT thisRead = 0; - UCHAR* buffer = vdr->getBlock(lengthBytes - 10000, 10000, &thisRead); - if (!buffer) - { - logger->log("PlayerRadio", Log::ERR, "Failed to get end block"); - if (!vdr->isConnected()) doConnectionLost(); - return false; - } - - success = demuxer->findPTS(buffer, thisRead, &endPTS); - free(buffer); - if (!success) - { - logger->log("PlayerRadio", Log::ERR, "Failed to get end PTS"); - return false; - } - - if (startPTS < endPTS) - { - lengthSeconds = (endPTS - startPTS) / 90000; - } - else - { - lengthSeconds = (startPTS - endPTS) / 90000; - } - - return true; -} - -int PlayerRadio::shutdown() -{ - if (!initted) return 0; - switchState(S_STOP); - initted = false; - - delete demuxer; - demuxer = NULL; - -#ifdef WIN32 - CloseHandle(mutex); -#endif - - return 1; -} - - -void PlayerRadio::setStartBytes(ULLONG startBytes) -{ - streamPos = startBytes; -} - -ULONG PlayerRadio::getLengthSeconds() -{ - return lengthSeconds; -} - -ULONG PlayerRadio::getCurrentSeconds() -{ - if (startup) return 0; - - long long currentPTS = demuxer->getAudioPTS(); - currentPTS -= startPTS; - if (currentPTS < 0) currentPTS += 8589934592ULL; - ULONG ret = currentPTS / 90000; - return ret; -} - -// ----------------------------------- Externally called events - -void PlayerRadio::play() -{ - if (!initted) return; - if (state == S_PLAY) return; - lock(); - switchState(S_PLAY); - unLock(); -} - - -void PlayerRadio::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; - } -} - +/* + Copyright 2004-2006 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "playerradio.h" + +#include "log.h" +#include "audio.h" +#include "video.h" +#include "demuxervdr.h" +#include "demuxerts.h" +#include "remote.h" +#include "vdr.h" +#include "message.h" +#include "messagequeue.h" + +// ----------------------------------- Called from outside, one offs or info funcs + +PlayerRadio::PlayerRadio(MessageQueue* tmessageQueue, void* tmessageReceiver) +: afeed(this) +{ + messageQueue = tmessageQueue; + messageReceiver = tmessageReceiver; + audio = Audio::getInstance(); + logger = Log::getInstance(); + vdr = VDR::getInstance(); + initted = false; + lengthBytes = 0; + lengthPackets = 0; + streamPos = 0; + state = S_STOP; + + startPTS = 0; + lengthSeconds = 0; + + threadBuffer = NULL; + + blockSize = 10000; + startupBlockSize = 20000; + + Video::getInstance()->turnVideoOff(); +} + +PlayerRadio::~PlayerRadio() +{ + if (initted) shutdown(); +} + +int PlayerRadio::init(ULLONG tlengthBytes, ULONG tlengthPackets, bool isPesRecording) +{ + if (initted) return 0; +#ifndef WIN32 + pthread_mutex_init(&mutex, NULL); +#else + mutex=CreateMutex(NULL,FALSE,NULL); +#endif + + if (isPesRecording) + demuxer = new DemuxerVDR(); + else + demuxer = new DemuxerTS(); + if (!demuxer) return 0; + + if (!demuxer->init(this, audio, NULL, NULL, 0, 40000, 0)) + { + logger->log("PlayerRadio", Log::ERR, "Demuxer failed to init"); + shutdown(); + return 0; + } + + afeed.init(); + audio->stop(); + + lengthBytes = tlengthBytes; + lengthPackets = tlengthPackets; + + logger->log("PlayerRadio", Log::DEBUG, "PlayerRadio has received length bytes of %llu", lengthBytes); + + UINT thisRead = 0; + int success; + + UCHAR* buffer = vdr->getBlock(0, 10000, &thisRead); + if (!buffer) + { + logger->log("PlayerRadio", Log::ERR, "Failed to get start block"); + shutdown(); + if (!vdr->isConnected()) doConnectionLost(); + return 0; + } + + success = demuxer->findPTS(buffer, thisRead, &startPTS); + if (!success) + { + logger->log("PlayerRadio", Log::ERR, "Failed to get start PTS"); + free(buffer); + shutdown(); + return 0; + } + + free(buffer); + + if (!setLengthSeconds()) + { + logger->log("PlayerRadio", Log::ERR, "Failed to setLengthSeconds"); + shutdown(); + return 0; + } + + initted = true; + return 1; +} + +bool PlayerRadio::setLengthSeconds() +{ + int success; + ULLONG endPTS = 0; + UINT thisRead = 0; + UCHAR* buffer = vdr->getBlock(lengthBytes - 10000, 10000, &thisRead); + if (!buffer) + { + logger->log("PlayerRadio", Log::ERR, "Failed to get end block"); + if (!vdr->isConnected()) doConnectionLost(); + return false; + } + + success = demuxer->findPTS(buffer, thisRead, &endPTS); + free(buffer); + if (!success) + { + logger->log("PlayerRadio", Log::ERR, "Failed to get end PTS"); + return false; + } + + if (startPTS < endPTS) + { + lengthSeconds = (endPTS - startPTS) / 90000; + } + else + { + lengthSeconds = (startPTS - endPTS) / 90000; + } + + return true; +} + +int PlayerRadio::shutdown() +{ + if (!initted) return 0; + switchState(S_STOP); + initted = false; + + delete demuxer; + demuxer = NULL; + +#ifdef WIN32 + CloseHandle(mutex); +#endif + + return 1; +} + + +void PlayerRadio::setStartBytes(ULLONG startBytes) +{ + streamPos = startBytes; +} + +ULONG PlayerRadio::getLengthSeconds() +{ + return lengthSeconds; +} + +ULONG PlayerRadio::getCurrentSeconds() +{ + if (startup) return 0; + + long long currentPTS = demuxer->getAudioPTS(); + currentPTS -= startPTS; + if (currentPTS < 0) currentPTS += 8589934592ULL; + ULONG ret = currentPTS / 90000; + return ret; +} + +// ----------------------------------- Externally called events + +void PlayerRadio::play() +{ + if (!initted) return; + if (state == S_PLAY) return; + lock(); + switchState(S_PLAY); + unLock(); +} + + +void PlayerRadio::playpause() +{ + if (!initted) return; + lock(); + if (state==S_PLAY) { + switchState(S_PAUSE_P); + } else { + switchState(S_PLAY); + } + unLock(); +} + +void PlayerRadio::stop() +{ + if (!initted) return; + if (state == S_STOP) return; + lock(); + logger->log("PlayerRadio", Log::DEBUG, "Stop called lock"); + switchState(S_STOP); + unLock(); +} + +void PlayerRadio::pause() +{ + if (!initted) return; + lock(); + + if (state == S_PAUSE_P) + { + switchState(S_PLAY); + } + else + { + switchState(S_PAUSE_P); + } + + unLock(); +} + +void PlayerRadio::jumpToPercent(double percent) +{ + lock(); + logger->log("PlayerRadio", Log::DEBUG, "JUMP TO %i%%", percent); + ULONG newPacket = (ULONG)(percent * lengthPackets / 100); + switchState(S_JUMP, newPacket); + unLock(); +} + +void PlayerRadio::skipForward(UINT seconds) +{ + lock(); + logger->log("PlayerRadio", Log::DEBUG, "SKIP FORWARD %i SECONDS", seconds); + ULONG currentSeconds = getCurrentSeconds(); + ULONG currentPacket = demuxer->getPacketNum(); + + if (currentSeconds == 0) { unLock(); return; } // div by zero + if (currentPacket == 0) { unLock(); return; } // Current pos from demuxer is not valid + + ULONG newPacket = currentPacket + (currentPacket * seconds / currentSeconds); + if (newPacket > lengthPackets) { switchState(S_PLAY); unLock(); } + else switchState(S_JUMP, newPacket); + unLock(); +} + +void PlayerRadio::skipBackward(UINT seconds) +{ + lock(); + logger->log("PlayerRadio", Log::DEBUG, "SKIP BACKWARD %i SECONDS", seconds); + + ULONG currentSeconds = getCurrentSeconds(); + ULONG currentPacket = demuxer->getPacketNum(); + + if (currentSeconds == 0) { unLock(); return; } // div by zero + if (currentPacket == 0) { unLock(); return; } // Current pos from demuxer is not valid + + ULONG newPacket; + if ((UINT)seconds > currentSeconds) + newPacket = 0; + else + newPacket = currentPacket - (currentPacket * seconds / currentSeconds); + + switchState(S_JUMP, newPacket); + unLock(); +} + +// ----------------------------------- Implementations called events + +void PlayerRadio::switchState(UCHAR toState, ULONG jumpPacket) +{ + if (!initted) return; + + logger->log("PlayerRadio", Log::DEBUG, "Switch state from %u to %u", state, toState); + + switch(state) // current state selector + { + case S_PLAY: // from S_PLAY ----------------------------------- + { + switch(toState) + { + case S_PLAY: // to S_PLAY + { + return; + } + case S_PAUSE_P: // to S_PAUSE_P + { + audio->pause(); + state = S_PAUSE_P; + return; + } + case S_STOP: // to S_STOP + { + afeed.stop(); + threadStop(); + audio->stop(); + audio->unPause(); + demuxer->reset(); + state = S_STOP; + return; + } + case S_JUMP: // to S_JUMP + { + restartAtPacket(jumpPacket); + return; + } + } + } + case S_PAUSE_P: // from S_PAUSE_P ----------------------------------- + { + switch(toState) + { + case S_PLAY: // to S_PLAY + { + audio->unPause(); + state = S_PLAY; + return; + } + case S_PAUSE_P: // to S_PAUSE_P + { + return; + } + case S_STOP: // to S_STOP + { + afeed.stop(); + threadStop(); + audio->stop(); + audio->unPause(); + demuxer->reset(); + audio->systemMuteOff(); + state = S_STOP; + return; + } + case S_JUMP: // to S_JUMP + { + state = S_PLAY; + audio->unPause(); + restartAtPacket(jumpPacket); + return; + } + } + } + case S_STOP: // from S_STOP ----------------------------------- + { + switch(toState) + { + case S_PLAY: // to S_PLAY + { + startup = true; + + audio->reset(); + audio->setStreamType(Audio::MPEG2_PES); + audio->systemMuteOff(); + demuxer->reset(); + + // FIXME use restartAtPacket here? + if (currentPacketNumber > lengthPackets) currentPacketNumber = 0; + demuxer->setPacketNum(currentPacketNumber); + state = S_PLAY; + threadStart(); + logger->log("PlayerRadio", Log::DEBUG, "Immediate play"); + afeed.start(); + audio->play(); + + return; + } + case S_PAUSE_P: // to S_PAUSE_P + { + return; + } + case S_STOP: // to S_STOP + { + return; + } + case S_JUMP: // to S_JUMP + { + return; + } + } + } + // case S_JUMP cannot be selected as a start state because it auto flips to play + } +} + +// ----------------------------------- Internal functions + +void PlayerRadio::lock() +{ +#ifndef WIN32 + pthread_mutex_lock(&mutex); + logger->log("PlayerRadio", Log::DEBUG, "LOCKED"); + +#else + WaitForSingleObject(mutex, INFINITE); +#endif +} + +void PlayerRadio::unLock() +{ +#ifndef WIN32 + logger->log("PlayerRadio", Log::DEBUG, "UNLOCKING"); + pthread_mutex_unlock(&mutex); +#else + ReleaseMutex(mutex); +#endif +} + +void PlayerRadio::restartAtPacket(ULONG newPacket) +{ + afeed.stop(); + threadStop(); + audio->reset(); + audio->setStreamType(Audio::MPEG2_PES); + demuxer->flush(); + currentPacketNumber = newPacket; + demuxer->setPacketNum(newPacket); + afeed.start(); + threadStart(); + audio->play(); + audio->systemMuteOff(); + audio->doMuting(); +} + +void PlayerRadio::doConnectionLost() +{ + logger->log("PlayerRadio", Log::DEBUG, "Connection lost, sending message"); + Message* m = new Message(); + m->to = messageReceiver; + m->from = this; + m->message = Message::PLAYER_EVENT; + m->parameter = PlayerRadio::CONNECTION_LOST; + messageQueue->postMessage(m); +} + +// ----------------------------------- Callback + +void PlayerRadio::call(void* caller) +{ + threadSignalNoLock(); +} + +// ----------------------------------- Feed thread + +void PlayerRadio::threadMethod() +{ + if (state == S_PLAY) threadFeedPlay(); +} + +void PlayerRadio::threadFeedPlay() +{ + ULLONG feedPosition; + UINT thisRead, writeLength, thisWrite, askFor; + time_t lastRescan = time(NULL); + + feedPosition = vdr->positionFromFrameNumber(currentPacketNumber); + if (!vdr->isConnected()) { doConnectionLost(); return; } + logger->log("PlayerRadio", Log::DEBUG, "startFeedPlay: wantedPacket %i goto %llu", currentPacketNumber, feedPosition); + + + while(1) + { + thisRead = 0; + writeLength = 0; + thisWrite = 0; + + threadCheckExit(); + + // If we havn't rescanned for a while.. + if ((lastRescan + 60) < time(NULL)) + { + lengthBytes = vdr->rescanRecording(&lengthPackets); + if (!vdr->isConnected()) { doConnectionLost(); return; } + logger->log("PlayerRadio", Log::DEBUG, "Rescanned and reset length: %llu", lengthBytes); + lastRescan = time(NULL); + + if (!setLengthSeconds()) + { + logger->log("PlayerRadio", Log::ERR, "Failed to setLengthSeconds in thread"); + return; + } + } + + if (feedPosition >= lengthBytes) break; // finished playback + + if (startup) + { + if (startupBlockSize > lengthBytes) + askFor = lengthBytes; // is a very small recording! + else + askFor = startupBlockSize; // normal, but a startup sized block to detect all the audio streams + } + else + { + if ((feedPosition + blockSize) > lengthBytes) // last block of recording + askFor = lengthBytes - feedPosition; + else // normal + askFor = blockSize; + } + + threadBuffer = vdr->getBlock(feedPosition, askFor, &thisRead); + feedPosition += thisRead; + + if (!vdr->isConnected()) + { + doConnectionLost(); + return; + } + + if (!threadBuffer) break; + + if (startup) + { + int a_stream = demuxer->scan(threadBuffer, thisRead); + demuxer->setAudioStream(a_stream); + logger->log("PlayerRadio", Log::DEBUG, "Startup Audio stream chosen %x", a_stream); + startup = false; + } + + threadCheckExit(); + + while(writeLength < thisRead) + { + thisWrite = demuxer->put(threadBuffer + writeLength, thisRead - writeLength); + writeLength += thisWrite; + + if (!thisWrite) + { + // demuxer is full and can't take anymore + threadLock(); + threadWaitForSignal(); + threadUnlock(); + } + + threadCheckExit(); + } + + free(threadBuffer); + threadBuffer = NULL; + + } + + // end of recording + logger->log("PlayerRadio", Log::DEBUG, "Recording playback ends"); + + threadCheckExit(); + + + Message* m = new Message(); // Must be done after this thread finishes, and must break into master mutex + m->to = messageReceiver; + m->from = this; + m->message = Message::PLAYER_EVENT; + m->parameter = PlayerRadio::STOP_PLAYBACK; + logger->log("PlayerRadio", Log::DEBUG, "Posting message to %p...", messageQueue); + messageQueue->postMessage(m); +} + +void PlayerRadio::threadPostStopCleanup() +{ + if (threadBuffer) + { + free(threadBuffer); + threadBuffer = NULL; + } +} + diff --git a/playerradio.h b/playerradio.h index a8fed68..ac370ce 100644 --- a/playerradio.h +++ b/playerradio.h @@ -1,157 +1,157 @@ -/* - Copyright 2004-2006 Chris Tallon - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#ifndef PLAYERRADIO_H -#define PLAYERRADIO_H - -#include -#include -#ifndef WIN32 -#include -#endif -#include - -#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 - -*/ +/* + Copyright 2004-2006 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef PLAYERRADIO_H +#define PLAYERRADIO_H + +#include +#include +#ifndef WIN32 +#include +#endif +#include + +#include "threadsystem.h" + +#include "callback.h" +#include "defines.h" +#include "afeed.h" + +class Log; +class Audio; +class Video; +class Demuxer; +class VDR; +class MessageQueue; + +class PlayerRadio : public Thread_TYPE, public Callback +{ + public: + PlayerRadio(MessageQueue* messageQueue, void* messageReceiver); + virtual ~PlayerRadio(); + + int init(ULLONG lengthBytes, ULONG lengthPackets, bool IsPesRecording); + int shutdown(); + void setStartBytes(ULLONG startBytes); + + void play(); + void stop(); + void pause(); + void playpause(); + void jumpToPercent(double percent); + void skipForward(UINT seconds); + void skipBackward(UINT seconds); + + UCHAR getState() { return state; } + ULONG getCurrentSeconds(); + ULONG getLengthSeconds(); + + void call(void*); // for callback interface + + const static UCHAR S_PLAY = 1; + const static UCHAR S_PAUSE_P = 2; + const static UCHAR S_STOP = 6; + const static UCHAR S_JUMP = 7; + + // Player events + + const static UCHAR CONNECTION_LOST = 1; + const static UCHAR STOP_PLAYBACK = 2; + const static UCHAR STREAM_END = 3; + + protected: + void threadMethod(); + void threadPostStopCleanup(); + + private: + void switchState(UCHAR newState, ULONG jumpPacket=0); + + void threadFeedPlay(); + void threadFeedScan(); + + void doConnectionLost(); + void restartAtPacket(ULONG newPacket); + bool setLengthSeconds(); + + MessageQueue* messageQueue; + void* messageReceiver; + Log* logger; + Audio* audio; + Demuxer* demuxer; + VDR* vdr; + AFeed afeed; + + bool initted; + bool startup; + + ULLONG startPTS; + ULONG lengthSeconds; + +#ifndef WIN32 + pthread_mutex_t mutex; +#else + HANDLE mutex; +#endif + void lock(); + void unLock(); + + ULLONG lengthBytes; + ULLONG streamPos; + ULONG lengthPackets; + ULONG currentPacketNumber; + UINT blockSize; + UINT startupBlockSize; + UCHAR* threadBuffer; + UCHAR state; +}; + +#endif + + +/* + +Possible states: + +Play, Pause, FFwd, FBwd, (Stop), [Jump] + + Possible Working + +Play -> PauseP * * + -> Stop * * + -> Jump * * + +PauseP -> Play * * + -> Stop * * + -> Jump * * + +PauseI -> Play * * + -> PauseP + -> Stop * * + -> Jump * * + +Stop -> Play * * + -> PauseP + -> Jump + +Jump -> Play + -> PauseP + -> Stop + +*/ diff --git a/stream.cc b/stream.cc index abea315..665739b 100644 --- a/stream.cc +++ b/stream.cc @@ -1,206 +1,206 @@ -/* - 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 -} +/* + 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 +} diff --git a/surface.cc b/surface.cc index 40b61b7..1ae5411 100644 --- a/surface.cc +++ b/surface.cc @@ -1,325 +1,325 @@ -/* - Copyright 2004-2005 Chris Tallon - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#include "surface.h" - -#include -#include "osd.h" -#include "log.h" -#include "video.h" - -#include "teletxt/txtfont.h" - -unsigned int interpol_table_fac1[16][22]; -unsigned int interpol_table_fac2[16][22]; -unsigned int interpol_table_fac3[16][22]; -unsigned int interpol_table_fac4[16][22]; -int interpol_lowbit[16]; -int interpol_upbit[16]; -int interpol_lowline[22]; -int interpol_upline[22]; -bool pol_table_inited=false; - -void initpol_tables(){ - int charsizex; - int charsizey; - charsizex=16; - if (Video::getInstance()->getFormat() == Video::PAL) - { - charsizey=22; - } else { - charsizey=18; - } - int ttcharsizex=12; - int ttcharsizey=10; - for (int py=0;pyheight; - - X = 0; - cx = 0; - ULONG rgba=c.rgba(); - startFastDraw(); - for (i=0; icontent[font->offset[c]]; - int w = font->width[c]; - int pixels = 0; - - for (X=0; (X> (32 - X)) & 0x1) - { - drawPixel(x+X+cx, y+Y, rgba,true); - pixels++; - } - } - } - cx += w; - } - endFastDraw(); - return 1; -} - -int Surface::drawTextRJ(const char* text, int x, int y, 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>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); -} +/* + Copyright 2004-2005 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "surface.h" + +#include +#include "osd.h" +#include "log.h" +#include "video.h" + +#include "teletxt/txtfont.h" + +unsigned int interpol_table_fac1[16][22]; +unsigned int interpol_table_fac2[16][22]; +unsigned int interpol_table_fac3[16][22]; +unsigned int interpol_table_fac4[16][22]; +int interpol_lowbit[16]; +int interpol_upbit[16]; +int interpol_lowline[22]; +int interpol_upline[22]; +bool pol_table_inited=false; + +void initpol_tables(){ + int charsizex; + int charsizey; + charsizex=16; + if (Video::getInstance()->getFormat() == Video::PAL) + { + charsizey=22; + } else { + charsizey=18; + } + int ttcharsizex=12; + int ttcharsizey=10; + for (int py=0;pyheight; + + X = 0; + cx = 0; + ULONG rgba=c.rgba(); + startFastDraw(); + for (i=0; icontent[font->offset[c]]; + int w = font->width[c]; + int pixels = 0; + + for (X=0; (X> (32 - X)) & 0x1) + { + drawPixel(x+X+cx, y+Y, rgba,true); + pixels++; + } + } + } + cx += w; + } + endFastDraw(); + return 1; +} + +int Surface::drawTextRJ(const char* text, int x, int y, 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>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); +} diff --git a/surface.h b/surface.h index 83afa52..4c5d4e6 100644 --- a/surface.h +++ b/surface.h @@ -1,110 +1,110 @@ -/* - Copyright 2004-2005 Chris Tallon - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#ifndef SURFACE_H -#define SURFACE_H - -#include -#include "defines.h" -#include "colour.h" - -#include "teletextdecodervbiebu.h" - -// Font stuff - -typedef struct bogl_font { - char *name; /* Font name. */ - int height; /* Height in pixels. */ - int spacing; /* Vertical spacing in pixels. */ - unsigned long *content; /* 32-bit right-padded bitmap array. */ - short *offset; /* 256 offsets into content. */ - unsigned char *width; /* 256 character widths. */ -} osd_font_t; - -//extern osd_font_t font_CaslonRoman_1_25; -//extern osd_font_t font_helvB24; -extern osd_font_t font_helvB18; - -class Bitmap; -class DisplayRegion; - - - -class Surface -{ - friend class Wwss; // classes that need surface access, their usage is forbidden for vector based - friend class WJpegComplex;// implementations of osd +/* + Copyright 2004-2005 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef SURFACE_H +#define SURFACE_H + +#include +#include "defines.h" +#include "colour.h" + +#include "teletextdecodervbiebu.h" + +// Font stuff + +typedef struct bogl_font { + char *name; /* Font name. */ + int height; /* Height in pixels. */ + int spacing; /* Vertical spacing in pixels. */ + unsigned long *content; /* 32-bit right-padded bitmap array. */ + short *offset; /* 256 offsets into content. */ + unsigned char *width; /* 256 character widths. */ +} osd_font_t; + +//extern osd_font_t font_CaslonRoman_1_25; +//extern osd_font_t font_helvB24; +extern osd_font_t font_helvB18; + +class Bitmap; +class 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: - 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 + 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 diff --git a/surfacedirectfb.cc b/surfacedirectfb.cc index baeeb91..7bcf3eb 100644 --- a/surfacedirectfb.cc +++ b/surfacedirectfb.cc @@ -1,304 +1,304 @@ -/* - Copyright 2004-2005 Chris Tallon, 2009 Marten Richter - Portions copyright 2008 Jon Gettler - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#include "surfacedirectfb.h" - -#include "osd.h" -#include "bitmap.h" -#include "log.h" - -#include "osddirectfb.h" - - - -SurfaceDirectFB::SurfaceDirectFB(int id) -: Surface(id) -{ - surface=NULL; -} - -SurfaceDirectFB::~SurfaceDirectFB() -{ - if (surface) surface->Release(surface); - -} - -int SurfaceDirectFB::create(UINT width, UINT height) -{ - int counter=0; - while (!Osd::getInstance()->isInitted() && counter<2000) { - MILLISLEEP(50); //Wait for Grafiksystem initialization - counter++; - } - if (!Osd::getInstance()->isInitted()) return -1; - - IDirectFB*dfb=((OsdDirectFB*)Osd::getInstance())->getDfb(); -printf("ich bin doof");fflush(stdout); - if (screen == this) - { - IDirectFBDisplayLayer *osd_layer=((OsdDirectFB*)Osd::getInstance())->getOsdLayer(); - if (osd_layer->GetSurface(osd_layer,&surface)!=DFB_OK) - { - - return 0; - } - surface->Clear(surface,0x0,0x0,0x0,0xFF); - } else { - DFBSurfaceDescription dsc; - memset(&dsc,0,sizeof(dsc)); - *((int*)&dsc.flags)=DSDESC_CAPS | DSDESC_WIDTH | DSDESC_HEIGHT; - - dsc.width=width; - dsc.height=height; - dsc.caps= DSCAPS_NONE; - if (dfb->CreateSurface(dfb,&dsc,&surface)!=DFB_OK) - { - return 0; - } - } - return 1; -} - -void SurfaceDirectFB::display() -{ -// unsigned long fb_descriptor[2]; -/* - fb_descriptor[0] = surface.sfc.handle; - fb_descriptor[1] = 1; -*/ -// ioctl(fdOsd, GFX_FB_ATTACH, fb_descriptor); -} - - -// ---------------------------------------------------------------------------- - -// Now for the drawing functions - - -int SurfaceDirectFB::fillblt(int x, int y, int width, int height, unsigned int c) -{ - int sw,sh; - unsigned char r,g,b,a; - int nx,ny,nw,nh; - nx=x; - ny=y; - nw=width; - nh=height; - surface->GetSize(surface,&sw,&sh); - - if (nx >sw) nx=sw-1; - if (ny >sh) ny=sh-1; - - if ((nx+nw) >= sw) nw=sw-nx; - if ((ny+nh) >= sh) nh=sh-ny; - - if ((nx<0) || (ny < 0) || (nh <=0) || (nw <=0)) { - return 0; - } - - a= (c &0xff000000)>>24; - r= (c &0x00ff0000)>>16; - g= (c &0x0000ff00)>>8; - b= (c &0x000000ff); - - - surface->SetColor(surface,r,g,b,a); - surface->FillRectangle(surface,nx,ny,nw,nh); - return 0; -} - -void SurfaceDirectFB::drawPixel(int x, int y, unsigned int c, bool fastdraw) -{ - int sw,sh; - unsigned char r,g,b,a; - char *dst=NULL; - int offset=0; - int pitch; - surface->GetSize(surface,&sw,&sh); - if (x>=sw) return; - if (y>=sh) return; - - - - a= (c &0xff000000)>>24; - r= (c &0x00ff0000)>>16; - g= (c &0x0000ff00)>>8; - b= (c &0x000000ff); - - //TODO Fastdraw - if (surface->Lock(surface,DSLF_WRITE,(void**)(void*)&dst,&pitch) == DFB_OK) { - offset= y* pitch+x*4; - dst[offset++]=b; - dst[offset++]=g; - dst[offset++]=r; - dst[offset]=a; - surface->Unlock(surface); - } - -} - -void SurfaceDirectFB::drawPixel(int x, int y, Colour& c, bool fastdraw) -{ - int sw,sh; - char *dst=NULL; - int offset=0; - int pitch; - surface->GetSize(surface,&sw,&sh); - if (x>=sw) return; - if (y>=sh) return; - - //TODO Fastdraw - if (surface->Lock(surface,DSLF_WRITE,(void**)(void*)&dst,&pitch) == DFB_OK) { - offset= y* pitch+x*4; - dst[offset++]=c.blue; - dst[offset++]=c.green; - dst[offset++]=c.red; - dst[offset]=c.alpha; - surface->Unlock(surface); - } - -} - -void SurfaceDirectFB::drawHorzLine(int x1, int x2, int y, unsigned int c) -{ - fillblt(x1, y, x2-x1, 1, c); -} - -void SurfaceDirectFB::drawVertLine(int x, int y1, int y2, unsigned int c) -{ - fillblt(x, y1, 1, y2-y1, c); -} - -void SurfaceDirectFB::drawBitmap(int x, int y, const Bitmap& bm) -{/* - UINT bmw = bm.getWidth(); UINT bmh = bm.getHeight(); - if (bmw == 0 || bmh == 0) return; - if ((x >= (int)surface.sfc.width) || (y >= (int)surface.sfc.height)) return; - int remainder = (surface.sfc.width % 4); - UINT line; - if (remainder == 0) - line = surface.sfc.width; - else - line = surface.sfc.width + (4 - remainder); - const std::vector& bmdata = bm.rawData(); - const std::vector& Y = bm.palette.getYVector(); - const std::vector& Cr = bm.palette.getCrVector(); - const std::vector& Cb = bm.palette.getCbVector(); - const std::vector& A = bm.palette.getAVector(); - UINT b_offset = 0; - UINT s_offset = x + y*line; - UINT plotWidth = bmw; - UINT plotHeight = bmh; - if (x + plotWidth - 1 > surface.sfc.width) - plotWidth = surface.sfc.width - x + 1; - if (y + plotHeight - 1 > surface.sfc.height) - plotHeight = surface.sfc.height - y + 1; - for (UINT j = 0; j < plotHeight; ++j) - { - UINT i = 0; - if (x & 1) // odd x - need to plot first column separately - { - UCHAR index = bmdata[b_offset]; - *(surface.base[0] + s_offset) = Y[index]; - *(surface.base[1] + s_offset - 1) = Cb[index]; - *(surface.base[1] + s_offset) = Cr[index]; - *(surface.base[2] + s_offset) = A[index]; - i = 1; - } - // Now, plot pairs of pixels with averaged chroma values - while (i < plotWidth - 1) - { - UCHAR index1 = bmdata[b_offset + i]; - UCHAR index2 = bmdata[b_offset + i + 1]; - *(surface.base[0] + s_offset + i) = Y[index1]; - *(surface.base[0] + s_offset + i + 1) = Y[index2]; - *(surface.base[1] + s_offset + i) = (Cb[index1] + Cb[index2]) / 2; - *(surface.base[1] + s_offset + i + 1) = (Cr[index1] + Cr[index2]) / 2; - *(surface.base[2] + s_offset + i) = A[index1]; - *(surface.base[2] + s_offset + i + 1) = A[index2]; - i += 2; - } - if (i == plotWidth - 1) // One column left to do - { - UCHAR index = bmdata[b_offset + i]; - *(surface.base[0] + s_offset + i) = Y[index]; - *(surface.base[1] + s_offset + i) = Cb[index]; - *(surface.base[1] + s_offset + i + 1) = Cr[index]; - *(surface.base[2] + s_offset + i) = A[index]; - } - s_offset += line; - b_offset += bmw; - }*/ -} - - /* surface update to screen needs: - source x distance into this surface - source y distance into this surface - width of update - height of update - destination x on screen - destination y on screen - */ -int SurfaceDirectFB::updateToScreen(int sx, int sy, int w, int h, int dx, int dy) // FIXME new, replace others with this FIXME -{ - IDirectFBSurface* screensurf=((SurfaceDirectFB*)screen)->getSurfaceDFB(); - if (!screensurf) return 0; - if (this==screen) return 0; - - DFBRectangle rect; - int sw,sh; - - screensurf->GetSize(screensurf,&sw,&sh); -// screensurf->Clear(screensurf,0x0,0x0,0x0,0xFF); - rect.x = sx; - rect.y=sy; - rect.w=w; - rect.h=h; - - DFBRectangle drect; //TODO make osd HD - drect.x=dx*sw/720; - drect.y=dy*sh/576; - drect.w=w*sw/720; - drect.h=h*sh/576; - -// screensurf->Blit(screensurf,surface,&rect,dx,dy); - screensurf->StretchBlit(screensurf,surface,&rect,&drect); - - return 0;//blt(fdOsd, surface.sfc.handle, sx, sy, w, h, ((SurfaceDirectFB*)screen)->getSurfaceHandle(), dx, dy); -} - -int SurfaceDirectFB::blt(int fd, unsigned long shandle, int sx, int sy, int width, int height, unsigned long dhandle, int dx, int dy) -{//Skip it, noone uses this! - - return 0; -} - -void SurfaceDirectFB::screenShot(const char* fileName) -{ - return; - -} - -void SurfaceDirectFB::readPixel(int x, int y, unsigned char* r, unsigned char* g, unsigned char* b) -{ - -} - +/* + Copyright 2004-2005 Chris Tallon, 2009 Marten Richter + Portions copyright 2008 Jon Gettler + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "surfacedirectfb.h" + +#include "osd.h" +#include "bitmap.h" +#include "log.h" + +#include "osddirectfb.h" + + + +SurfaceDirectFB::SurfaceDirectFB(int id) +: Surface(id) +{ + surface=NULL; +} + +SurfaceDirectFB::~SurfaceDirectFB() +{ + if (surface) surface->Release(surface); + +} + +int SurfaceDirectFB::create(UINT width, UINT height) +{ + int counter=0; + while (!Osd::getInstance()->isInitted() && counter<2000) { + MILLISLEEP(50); //Wait for Grafiksystem initialization + counter++; + } + if (!Osd::getInstance()->isInitted()) return -1; + + IDirectFB*dfb=((OsdDirectFB*)Osd::getInstance())->getDfb(); +printf("ich bin doof");fflush(stdout); + if (screen == this) + { + IDirectFBDisplayLayer *osd_layer=((OsdDirectFB*)Osd::getInstance())->getOsdLayer(); + if (osd_layer->GetSurface(osd_layer,&surface)!=DFB_OK) + { + + return 0; + } + surface->Clear(surface,0x0,0x0,0x0,0xFF); + } else { + DFBSurfaceDescription dsc; + memset(&dsc,0,sizeof(dsc)); + *((int*)&dsc.flags)=DSDESC_CAPS | DSDESC_WIDTH | DSDESC_HEIGHT; + + dsc.width=width; + dsc.height=height; + dsc.caps= DSCAPS_NONE; + if (dfb->CreateSurface(dfb,&dsc,&surface)!=DFB_OK) + { + return 0; + } + } + return 1; +} + +void SurfaceDirectFB::display() +{ +// unsigned long fb_descriptor[2]; +/* + fb_descriptor[0] = surface.sfc.handle; + fb_descriptor[1] = 1; +*/ +// ioctl(fdOsd, GFX_FB_ATTACH, fb_descriptor); +} + + +// ---------------------------------------------------------------------------- + +// Now for the drawing functions + + +int SurfaceDirectFB::fillblt(int x, int y, int width, int height, unsigned int c) +{ + int sw,sh; + unsigned char r,g,b,a; + int nx,ny,nw,nh; + nx=x; + ny=y; + nw=width; + nh=height; + surface->GetSize(surface,&sw,&sh); + + if (nx >sw) nx=sw-1; + if (ny >sh) ny=sh-1; + + if ((nx+nw) >= sw) nw=sw-nx; + if ((ny+nh) >= sh) nh=sh-ny; + + if ((nx<0) || (ny < 0) || (nh <=0) || (nw <=0)) { + return 0; + } + + a= (c &0xff000000)>>24; + r= (c &0x00ff0000)>>16; + g= (c &0x0000ff00)>>8; + b= (c &0x000000ff); + + + surface->SetColor(surface,r,g,b,a); + surface->FillRectangle(surface,nx,ny,nw,nh); + return 0; +} + +void SurfaceDirectFB::drawPixel(int x, int y, unsigned int c, bool fastdraw) +{ + int sw,sh; + unsigned char r,g,b,a; + char *dst=NULL; + int offset=0; + int pitch; + surface->GetSize(surface,&sw,&sh); + if (x>=sw) return; + if (y>=sh) return; + + + + a= (c &0xff000000)>>24; + r= (c &0x00ff0000)>>16; + g= (c &0x0000ff00)>>8; + b= (c &0x000000ff); + + //TODO Fastdraw + if (surface->Lock(surface,DSLF_WRITE,(void**)(void*)&dst,&pitch) == DFB_OK) { + offset= y* pitch+x*4; + dst[offset++]=b; + dst[offset++]=g; + dst[offset++]=r; + dst[offset]=a; + surface->Unlock(surface); + } + +} + +void SurfaceDirectFB::drawPixel(int x, int y, Colour& c, bool fastdraw) +{ + int sw,sh; + char *dst=NULL; + int offset=0; + int pitch; + surface->GetSize(surface,&sw,&sh); + if (x>=sw) return; + if (y>=sh) return; + + //TODO Fastdraw + if (surface->Lock(surface,DSLF_WRITE,(void**)(void*)&dst,&pitch) == DFB_OK) { + offset= y* pitch+x*4; + dst[offset++]=c.blue; + dst[offset++]=c.green; + dst[offset++]=c.red; + dst[offset]=c.alpha; + surface->Unlock(surface); + } + +} + +void SurfaceDirectFB::drawHorzLine(int x1, int x2, int y, unsigned int c) +{ + fillblt(x1, y, x2-x1, 1, c); +} + +void SurfaceDirectFB::drawVertLine(int x, int y1, int y2, unsigned int c) +{ + fillblt(x, y1, 1, y2-y1, c); +} + +void SurfaceDirectFB::drawBitmap(int x, int y, const Bitmap& bm) +{/* + UINT bmw = bm.getWidth(); UINT bmh = bm.getHeight(); + if (bmw == 0 || bmh == 0) return; + if ((x >= (int)surface.sfc.width) || (y >= (int)surface.sfc.height)) return; + int remainder = (surface.sfc.width % 4); + UINT line; + if (remainder == 0) + line = surface.sfc.width; + else + line = surface.sfc.width + (4 - remainder); + const std::vector& bmdata = bm.rawData(); + const std::vector& Y = bm.palette.getYVector(); + const std::vector& Cr = bm.palette.getCrVector(); + const std::vector& Cb = bm.palette.getCbVector(); + const std::vector& A = bm.palette.getAVector(); + UINT b_offset = 0; + UINT s_offset = x + y*line; + UINT plotWidth = bmw; + UINT plotHeight = bmh; + if (x + plotWidth - 1 > surface.sfc.width) + plotWidth = surface.sfc.width - x + 1; + if (y + plotHeight - 1 > surface.sfc.height) + plotHeight = surface.sfc.height - y + 1; + for (UINT j = 0; j < plotHeight; ++j) + { + UINT i = 0; + if (x & 1) // odd x - need to plot first column separately + { + UCHAR index = bmdata[b_offset]; + *(surface.base[0] + s_offset) = Y[index]; + *(surface.base[1] + s_offset - 1) = Cb[index]; + *(surface.base[1] + s_offset) = Cr[index]; + *(surface.base[2] + s_offset) = A[index]; + i = 1; + } + // Now, plot pairs of pixels with averaged chroma values + while (i < plotWidth - 1) + { + UCHAR index1 = bmdata[b_offset + i]; + UCHAR index2 = bmdata[b_offset + i + 1]; + *(surface.base[0] + s_offset + i) = Y[index1]; + *(surface.base[0] + s_offset + i + 1) = Y[index2]; + *(surface.base[1] + s_offset + i) = (Cb[index1] + Cb[index2]) / 2; + *(surface.base[1] + s_offset + i + 1) = (Cr[index1] + Cr[index2]) / 2; + *(surface.base[2] + s_offset + i) = A[index1]; + *(surface.base[2] + s_offset + i + 1) = A[index2]; + i += 2; + } + if (i == plotWidth - 1) // One column left to do + { + UCHAR index = bmdata[b_offset + i]; + *(surface.base[0] + s_offset + i) = Y[index]; + *(surface.base[1] + s_offset + i) = Cb[index]; + *(surface.base[1] + s_offset + i + 1) = Cr[index]; + *(surface.base[2] + s_offset + i) = A[index]; + } + s_offset += line; + b_offset += bmw; + }*/ +} + + /* surface update to screen needs: + source x distance into this surface + source y distance into this surface + width of update + height of update + destination x on screen + destination y on screen + */ +int SurfaceDirectFB::updateToScreen(int sx, int sy, int w, int h, int dx, int dy) // FIXME new, replace others with this FIXME +{ + IDirectFBSurface* screensurf=((SurfaceDirectFB*)screen)->getSurfaceDFB(); + if (!screensurf) return 0; + if (this==screen) return 0; + + DFBRectangle rect; + int sw,sh; + + screensurf->GetSize(screensurf,&sw,&sh); +// screensurf->Clear(screensurf,0x0,0x0,0x0,0xFF); + rect.x = sx; + rect.y=sy; + rect.w=w; + rect.h=h; + + DFBRectangle drect; //TODO make osd HD + drect.x=dx*sw/720; + drect.y=dy*sh/576; + drect.w=w*sw/720; + drect.h=h*sh/576; + +// screensurf->Blit(screensurf,surface,&rect,dx,dy); + screensurf->StretchBlit(screensurf,surface,&rect,&drect); + + return 0;//blt(fdOsd, surface.sfc.handle, sx, sy, w, h, ((SurfaceDirectFB*)screen)->getSurfaceHandle(), dx, dy); +} + +int SurfaceDirectFB::blt(int fd, unsigned long shandle, int sx, int sy, int width, int height, unsigned long dhandle, int dx, int dy) +{//Skip it, noone uses this! + + return 0; +} + +void SurfaceDirectFB::screenShot(const char* fileName) +{ + return; + +} + +void SurfaceDirectFB::readPixel(int x, int y, unsigned char* r, unsigned char* g, unsigned char* b) +{ + +} + diff --git a/surfacedirectfb.h b/surfacedirectfb.h index e1870ea..e9a3fff 100644 --- a/surfacedirectfb.h +++ b/surfacedirectfb.h @@ -1,75 +1,75 @@ -/* - Copyright 2004-2005 Chris Tallon, 2009 Marten Richter - Portions copyright 2004 Jon Gettler - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#ifndef SURFACEDirectFB_H -#define SURFACEDirectFB_H - -#include -#include -#include -#include -#include - -extern "C" -{ - #include -} - -#include "defines.h" -#include "surface.h" - -#include - - - -class SurfaceDirectFB : public Surface -{ - public: - SurfaceDirectFB(int id = 0); - 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 +/* + Copyright 2004-2005 Chris Tallon, 2009 Marten Richter + Portions copyright 2004 Jon Gettler + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef SURFACEDirectFB_H +#define SURFACEDirectFB_H + +#include +#include +#include +#include +#include + +extern "C" +{ + #include +} + +#include "defines.h" +#include "surface.h" + +#include + + + +class SurfaceDirectFB : public Surface +{ + public: + SurfaceDirectFB(int id = 0); + 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 diff --git a/surfacemvp.h b/surfacemvp.h index 12c7c88..a0c4807 100644 --- a/surfacemvp.h +++ b/surfacemvp.h @@ -1,250 +1,250 @@ -/* - 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 -#include -#include -#include -#include - -extern "C" -{ - #include -} - -#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 +/* + 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 +#include +#include +#include +#include + +extern "C" +{ + #include +} + +#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 diff --git a/surfaceopengl.cc b/surfaceopengl.cc index 578fb3d..c1745c0 100644 --- a/surfaceopengl.cc +++ b/surfaceopengl.cc @@ -1,364 +1,364 @@ -/* - 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 - -#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;ylog("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; - -}*/ - +/* + 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 + +#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;ylog("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; + +}*/ + diff --git a/surfaceopengl.h b/surfaceopengl.h index 81d0809..88ba56a 100644 --- a/surfaceopengl.h +++ b/surfaceopengl.h @@ -1,72 +1,72 @@ -/* - 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 - -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 +/* + 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 + +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 diff --git a/surfacevector.cc b/surfacevector.cc index 6203c47..aa2cc7b 100644 --- a/surfacevector.cc +++ b/surfacevector.cc @@ -1,301 +1,301 @@ -/* - 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 -#include - -SurfaceVector::SurfaceVector(OsdVector* vosd) -{ - - osd=vosd; -} - -SurfaceVector::~SurfaceVector() -{ - osd->removeSurface(this); - list::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::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::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(); -} +/* + 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 +#include + +SurfaceVector::SurfaceVector(OsdVector* vosd) +{ + + osd=vosd; +} + +SurfaceVector::~SurfaceVector() +{ + osd->removeSurface(this); + list::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::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::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(); +} diff --git a/surfacevector.h b/surfacevector.h index 51a82a6..76a882f 100644 --- a/surfacevector.h +++ b/surfacevector.h @@ -1,93 +1,93 @@ -/* - 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 - - -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 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 +/* + 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 + + +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 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 diff --git a/surfacewin.cc b/surfacewin.cc index c3477de..e64f563 100644 --- a/surfacewin.cc +++ b/surfacewin.cc @@ -1,404 +1,404 @@ -/* - 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;lineUnlockRect()!=D3D_OK) { - osd->EndPainting(); - return 0; - } - osd->EndPainting(); - } - - return 0; -} - - -void SurfaceWin::startFastDraw(){ - WaitForSingleObject(event,INFINITE); //since this might be called before surface - //allocation we will wait in this case, hopefully without deadlocks - if (!d3dsurface) { - return; //why does this happen - } - OsdWin* osd=((OsdWin*)(Osd::getInstance())); - LPDIRECT3DDEVICE9 d3ddev=osd->getD3dDev(); - if (screen==this) { - //This should not happen! - return ; - - } else { - osd->BeginPainting(); -// D3DLOCKED_RECT lockrect; - RECT rect={0,0,swidth,sheight}; - if (d3dsurface->LockRect(&lockrect,&rect,D3DLOCK_DISCARD)!=D3D_OK) { - osd->EndPainting(); - return ; - } - } -// fastdraw=true; -} -void SurfaceWin::endFastDraw(){ - OsdWin* osd=((OsdWin*)(Osd::getInstance())); - if (d3dsurface->UnlockRect()!=D3D_OK) { - osd->EndPainting(); - return ; - } - osd->EndPainting(); -// fastdraw=false; - } - -void SurfaceWin::drawPixel(int x, int y, Colour & colour, bool fastdraw) { - int c = ( (0xFF000000 ) - | (colour.red << 16) - | (colour.green << 8) - | (colour.blue ) ); - - drawPixel(x, y, c, fastdraw); - } - -void SurfaceWin::drawPixel(int x, int y, unsigned int c, bool fastdraw) -{ - //FixMe: locking for every single Pixel will be painfully slow - OsdWin* osd; - if (!fastdraw) { - WaitForSingleObject(event,INFINITE); //since this might be called before surface - //allocation we will wait in this case, hopefully without deadlocks - if (!d3dsurface) { - return; //why does this happen - } - osd=((OsdWin*)(Osd::getInstance())); - } - if (x>=swidth || y>=sheight) return; //do not draw outside the surface - if (screen==this) { - //This should not happen! - return ; - - } else { - if (!fastdraw) { - osd->BeginPainting(); -// D3DLOCKED_RECT lockrect; - RECT rect={x,y,x+1,y+1}; - if (d3dsurface->LockRect(&lockrect,&rect,D3DLOCK_DISCARD)!=D3D_OK) { - osd->EndPainting(); - return ; - } - unsigned int*row=(unsigned int*)(((char*)lockrect.pBits)); - row[0]=c; - if (d3dsurface->UnlockRect()!=D3D_OK) { - osd->EndPainting(); - return ; - } - osd->EndPainting(); - } else { - unsigned int*row=(unsigned int*)(((char*)lockrect.pBits+lockrect.Pitch*y+4*x)); - row[0]=c; - } - - } - -} - -void SurfaceWin::drawHorzLine(int x1, int x2, int y,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; - -}*/ - +/* + 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;lineUnlockRect()!=D3D_OK) { + osd->EndPainting(); + return 0; + } + osd->EndPainting(); + } + + return 0; +} + + +void SurfaceWin::startFastDraw(){ + WaitForSingleObject(event,INFINITE); //since this might be called before surface + //allocation we will wait in this case, hopefully without deadlocks + if (!d3dsurface) { + return; //why does this happen + } + OsdWin* osd=((OsdWin*)(Osd::getInstance())); + LPDIRECT3DDEVICE9 d3ddev=osd->getD3dDev(); + if (screen==this) { + //This should not happen! + return ; + + } else { + osd->BeginPainting(); +// D3DLOCKED_RECT lockrect; + RECT rect={0,0,swidth,sheight}; + if (d3dsurface->LockRect(&lockrect,&rect,D3DLOCK_DISCARD)!=D3D_OK) { + osd->EndPainting(); + return ; + } + } +// fastdraw=true; +} +void SurfaceWin::endFastDraw(){ + OsdWin* osd=((OsdWin*)(Osd::getInstance())); + if (d3dsurface->UnlockRect()!=D3D_OK) { + osd->EndPainting(); + return ; + } + osd->EndPainting(); +// fastdraw=false; + } + +void SurfaceWin::drawPixel(int x, int y, Colour & colour, bool fastdraw) { + int c = ( (0xFF000000 ) + | (colour.red << 16) + | (colour.green << 8) + | (colour.blue ) ); + + drawPixel(x, y, c, fastdraw); + } + +void SurfaceWin::drawPixel(int x, int y, unsigned int c, bool fastdraw) +{ + //FixMe: locking for every single Pixel will be painfully slow + OsdWin* osd; + if (!fastdraw) { + WaitForSingleObject(event,INFINITE); //since this might be called before surface + //allocation we will wait in this case, hopefully without deadlocks + if (!d3dsurface) { + return; //why does this happen + } + osd=((OsdWin*)(Osd::getInstance())); + } + if (x>=swidth || y>=sheight) return; //do not draw outside the surface + if (screen==this) { + //This should not happen! + return ; + + } else { + if (!fastdraw) { + osd->BeginPainting(); +// D3DLOCKED_RECT lockrect; + RECT rect={x,y,x+1,y+1}; + if (d3dsurface->LockRect(&lockrect,&rect,D3DLOCK_DISCARD)!=D3D_OK) { + osd->EndPainting(); + return ; + } + unsigned int*row=(unsigned int*)(((char*)lockrect.pBits)); + row[0]=c; + if (d3dsurface->UnlockRect()!=D3D_OK) { + osd->EndPainting(); + return ; + } + osd->EndPainting(); + } else { + unsigned int*row=(unsigned int*)(((char*)lockrect.pBits+lockrect.Pitch*y+4*x)); + row[0]=c; + } + + } + +} + +void SurfaceWin::drawHorzLine(int x1, int x2, int y,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; + +}*/ + diff --git a/surfacewin.h b/surfacewin.h index 0692c7a..093b28e 100644 --- a/surfacewin.h +++ b/surfacewin.h @@ -1,66 +1,66 @@ -/* - Copyright 2006 Marten Richter - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#ifndef SURFACEWIN_H -#define SURFACEWIN_H - -#include "defines.h" -#include "surface.h" -#include -#include - -class SurfaceWin : public Surface -{ - public: - SurfaceWin(int id = 0); - ~SurfaceWin(); - - int create(UINT width, UINT height); - void display(); - - void startFastDraw(); - void endFastDraw(); - - int fillblt(int x, int y, int width, int height, 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 +/* + Copyright 2006 Marten Richter + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef SURFACEWIN_H +#define SURFACEWIN_H + +#include "defines.h" +#include "surface.h" +#include +#include + +class SurfaceWin : public Surface +{ + public: + SurfaceWin(int id = 0); + ~SurfaceWin(); + + int create(UINT width, UINT height); + void display(); + + void startFastDraw(); + void endFastDraw(); + + int fillblt(int x, int y, int width, int height, 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 diff --git a/teletextdecodervbiebu.cc b/teletextdecodervbiebu.cc index 1e97f83..4053aee 100644 --- a/teletextdecodervbiebu.cc +++ b/teletextdecodervbiebu.cc @@ -1,837 +1,837 @@ -/* - Copyright 2008 Marten Richter - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - - -/* Portions from vdr osdteletext plugin "txtrender.c": */ -/*************************************************************************** - * * - * txtrender.c - Teletext display abstraction and teletext code * - * renderer * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * Changelog: * - * 2005-03 initial version (c) Udo Richter * - * * - ***************************************************************************/ - -#include "teletextdecodervbiebu.h" -#include "teletxt/tables.h" -#include "message.h" -#include "command.h" -#include "video.h" - -#ifdef WIN32 -#include -#endif - -TeletextDecoderVBIEBU::TeletextDecoderVBIEBU() -{ - selectedpage=0x100; - ourpage=false; - flags=lang=0; - - CleanPage(); - FirstG0CodePage=0; - SecondG0CodePage=0; - dirty=false; - txtview=NULL; - firstlineupdate=0; - gotcha=false; - char digits[]={'1','0','0'}; - setKeyinDigits(digits,false); - isrecording=false; - for (int i=0;i<10;i++) record_pages[i]=-1; - - -} - -TeletextDecoderVBIEBU::~TeletextDecoderVBIEBU() -{ - - -} - -long long TeletextDecoderVBIEBU::SetStartOffset(long long curreftime, bool *rsync) -{ - return 0; -} - -void TeletextDecoderVBIEBU::ResetTimeOffsets() -{ - -} - - - -void TeletextDecoderVBIEBU::ResetDecoder() -{ - gotcha=false; - ourpage=false; - firstlineupdate=0; - selectedpage=0x100; - CleanPage(); - char digits[]={'1','0','0'}; - setKeyinDigits(digits,false); - for (int i=0;i<10;i++) record_pages[i]=-1; - -} - -void TeletextDecoderVBIEBU::setPage(unsigned int newpage) -{ - selectedpage=newpage; - gotcha=false; - ourpage=false; - firstlineupdate=0; - for (int i=0;i<25;i++) { - memset(curpage[i],0,40); - } -} - -void TeletextDecoderVBIEBU::CleanPage() -{ - int x,y; - for (int i=0;i<25;i++) { - memset(curpage[i],0,40); - } - for (y=0;y<25;y++) { - for (x=0;x<40;x++) { - cTeletextChar c; - c.SetFGColor(ttcWhite); - c.SetBGColor(ttcBlack); - c.SetCharset(CHARSET_LATIN_G0); - c.SetChar(' '); - if (flags&0x60) { - c.SetBoxedOut(true); - } - setChar(x,y,c); - } - } - -} -void TeletextDecoderVBIEBU::setKeyinDigits(char digits[3],bool inKeying) -{ - int x; - inkeying=inKeying; - for (x=0;x<3;x++) { - cTeletextChar c; - c.SetFGColor(ttcWhite); - c.SetBGColor(ttcBlack); - c.SetCharset(CHARSET_LATIN_G0); - c.SetChar(digits[x]); - if (flags&0x60) { - c.SetBoxedOut(true); - } - setChar(x+3,0,c); - keyindigits[x]=digits[x]; - } -} - -void TeletextDecoderVBIEBU::PrepareMediaSample(const MediaPacketList& mplist, UINT samplepos) -{ - mediapacket = mplist.front(); -} - -static ULLONG 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++; - - - } - -} +/* + Copyright 2008 Marten Richter + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + + +/* Portions from vdr osdteletext plugin "txtrender.c": */ +/*************************************************************************** + * * + * txtrender.c - Teletext display abstraction and teletext code * + * renderer * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * Changelog: * + * 2005-03 initial version (c) Udo Richter * + * * + ***************************************************************************/ + +#include "teletextdecodervbiebu.h" +#include "teletxt/tables.h" +#include "message.h" +#include "command.h" +#include "video.h" + +#ifdef WIN32 +#include +#endif + +TeletextDecoderVBIEBU::TeletextDecoderVBIEBU() +{ + selectedpage=0x100; + ourpage=false; + flags=lang=0; + + CleanPage(); + FirstG0CodePage=0; + SecondG0CodePage=0; + dirty=false; + txtview=NULL; + firstlineupdate=0; + gotcha=false; + char digits[]={'1','0','0'}; + setKeyinDigits(digits,false); + isrecording=false; + for (int i=0;i<10;i++) record_pages[i]=-1; + + +} + +TeletextDecoderVBIEBU::~TeletextDecoderVBIEBU() +{ + + +} + +long long TeletextDecoderVBIEBU::SetStartOffset(long long curreftime, bool *rsync) +{ + return 0; +} + +void TeletextDecoderVBIEBU::ResetTimeOffsets() +{ + +} + + + +void TeletextDecoderVBIEBU::ResetDecoder() +{ + gotcha=false; + ourpage=false; + firstlineupdate=0; + selectedpage=0x100; + CleanPage(); + char digits[]={'1','0','0'}; + setKeyinDigits(digits,false); + for (int i=0;i<10;i++) record_pages[i]=-1; + +} + +void TeletextDecoderVBIEBU::setPage(unsigned int newpage) +{ + selectedpage=newpage; + gotcha=false; + ourpage=false; + firstlineupdate=0; + for (int i=0;i<25;i++) { + memset(curpage[i],0,40); + } +} + +void TeletextDecoderVBIEBU::CleanPage() +{ + int x,y; + for (int i=0;i<25;i++) { + memset(curpage[i],0,40); + } + for (y=0;y<25;y++) { + for (x=0;x<40;x++) { + cTeletextChar c; + c.SetFGColor(ttcWhite); + c.SetBGColor(ttcBlack); + c.SetCharset(CHARSET_LATIN_G0); + c.SetChar(' '); + if (flags&0x60) { + c.SetBoxedOut(true); + } + setChar(x,y,c); + } + } + +} +void TeletextDecoderVBIEBU::setKeyinDigits(char digits[3],bool inKeying) +{ + int x; + inkeying=inKeying; + for (x=0;x<3;x++) { + cTeletextChar c; + c.SetFGColor(ttcWhite); + c.SetBGColor(ttcBlack); + c.SetCharset(CHARSET_LATIN_G0); + c.SetChar(digits[x]); + if (flags&0x60) { + c.SetBoxedOut(true); + } + setChar(x+3,0,c); + keyindigits[x]=digits[x]; + } +} + +void TeletextDecoderVBIEBU::PrepareMediaSample(const MediaPacketList& mplist, UINT samplepos) +{ + mediapacket = mplist.front(); +} + +static ULLONG TxtPTSDifference(ULLONG pts1, ULLONG pts2) +{ + // Assume pts1, pts2 < 2^33; calculate pts1 - pts2 + if (pts1 > pts2) + return pts1 - pts2; + else + return -pts1 + pts2; +} + +UINT TeletextDecoderVBIEBU::DeliverMediaSample(UCHAR* buffer, UINT *samplepos) +{ + if (mediapacket.type != MPTYPE_TELETEXT) + { + *samplepos= 0; + return 1; //Skip it! + } + unsigned int headerstrip; + unsigned char txtdata[43]; + headerstrip=buffer[mediapacket.pos_buffer+8]+9; + //headerstrip+=4; //substream id + unsigned int datapos=0; + unsigned int datatype=buffer[mediapacket.pos_buffer+headerstrip+0]; + //Chris should we use here the pts data from mediapacket ? + // in this case the data fields should also be added in mediamvp + if (mediapacket.synched) + { // An entry exists in the work list + ULLONG nowPTS = Video::getInstance()->getCurrentTimestamp(); + + ULLONG ptsdifference=TxtPTSDifference(mediapacket.pts, nowPTS); + + if (ptsdifference >= (120LL*90000LL)) { + *samplepos=0; + return 1;//bad data skip it + } + if (nowPTS < (mediapacket.pts-4000) ) { + *samplepos=0; + return 0; + } + } + + if (( datatype>=0x10 && datatype <=0x1F) || + ( datatype>=0x99 && datatype <=0x9B)) { + //EBU VBI DATA + datapos++; + while (datapos< (mediapacket.length-headerstrip)) { + unsigned int unit_id=buffer[mediapacket.pos_buffer+headerstrip+datapos]; + datapos++; + unsigned int unit_length=buffer[mediapacket.pos_buffer+headerstrip+datapos]; + datapos++; + switch (unit_id) { + case 0x02: + case 0x03: {//Teletext with and without subtitles + //call teletext decoder + unsigned int field=buffer[mediapacket.pos_buffer+headerstrip+datapos]; + + if ((datapos+44)< (mediapacket.length-headerstrip)) { + for (int i=0;i<42;i++) { + txtdata[i]=invtab[buffer[mediapacket.pos_buffer+headerstrip+datapos+2+i]]; + } + DecodeTeletext(txtdata,field); + } + }break; + case 0xC0: //inverted Teletext + //call teletext decoder + break; + + case 0xC3: //VPS + //what to do with this, in the moment we do not need it + break; + case 0xC4: //WSS + //what to do with this, in the moment we do not need it + break; + case 0xC5: //CC + //what to do with this, in the moment we do not need it + break; + case 0xC6: //monochrome 4:2:2 samples, what is that? + //what to do with this, in the moment we do not need it + break; + default: + // case 0x00,0x01,0x04..0x7f,0x80..0xbf,0xc1,0xc2,0xc7..0xfe,0xff: //discard + //discard + break; + }; + datapos+=unit_length; + // while (buffer[mediapacket.pos_buffer+headerstrip+datapos]==0xFF &&(datapos< (mediapacket.length-headerstrip))) { + // datapos++; //stuffing bytes + // } + + + } + *samplepos=0; + return 1; + + + } else { + *samplepos= 0; + return 1; //Skip it! and discard data + } + + return 0; + +} +// This part is inspired by the vdr-plugin-osdteletext of Udo Richter and Marcel Wiesweg +void TeletextDecoderVBIEBU::DecodeTeletext(const UCHAR* buffer, unsigned int field) //needs to be exactly 42 byte long!! +{ + UCHAR hdrbuf[5]; + for (int i=0;i<5;i++) hdrbuf[i]=(unhamtab[buffer[2*i]]&0xF) | ((unhamtab[buffer[2*i+1]]&0xF)<< 4); + int header=hdrbuf[0]; + int magazin=header & 0x7; + int line = (header>>3) & 0x1f; + if (magazin==0) magazin=8; + if (line==0) + { + if (ourpage) { + gotcha=true; + inkeying=false; + RenderTeletextCode(false); + } else { + RenderTeletextCode(true); + } + int pagenumber=hdrbuf[1]; + int pagemagazin=magazin<<8 | pagenumber; + int pagesubnumber=(hdrbuf[2]) || ((hdrbuf[3]<<8) & 0x3f7f); + + if (pagemagazin == selectedpage) ourpage=true; + else ourpage=false; + if (isrecording) { + for (int i=0;i<10;i++) { + if (pagemagazin==record_pages[i]) break; + if (record_pages[i]==-1) { + record_pages[i]=pagemagazin; + break; + } + } + } + if (hdrbuf[3] &0x80) { //This is a subtitle + for (int i=0;i<10;i++) { + if (pagemagazin==record_pages[i]) break; + if (record_pages[i]==-1) { + record_pages[i]=pagemagazin; + break; + } + } + } + + + if (ourpage) { + lang=((hdrbuf[4]>>5) & 0x07); + flags=hdrbuf[2] & 0x80; + flags|=(hdrbuf[3]&0x40)|((hdrbuf[3]>>2)&0x20); //?????? + flags|=((hdrbuf[4]<<4)&0x10)|((hdrbuf[4]<<2)&0x08)|(hdrbuf[4]&0x04)|((hdrbuf[4]>>1)&0x02)|((hdrbuf[4]>>4)&0x01); + for (int i=0;i<25;i++) { + memset(curpage[i],0,40); + } + } + + memcpy(curpage[line],buffer+2,40); + } else if (ourpage && (line<=25)) { + memcpy(curpage[line],buffer+2,40); + + } +} + + +/* from osdteletext plugin: (slightly adapted for vomp)*/ +// Font tables + +// teletext uses 7-bit numbers to identify a font set. +// There are three font sets involved: +// Primary G0, Secondary G0, and G2 font set. + +// Font tables are organized in blocks of 8 fonts: + +enumCharsets FontBlockG0_0000[8] = { + CHARSET_LATIN_G0_EN, + CHARSET_LATIN_G0_DE, + CHARSET_LATIN_G0_SV_FI, + CHARSET_LATIN_G0_IT, + CHARSET_LATIN_G0_FR, + CHARSET_LATIN_G0_PT_ES, + CHARSET_LATIN_G0_CZ_SK, + CHARSET_LATIN_G0 +}; + +enumCharsets FontBlockG2Latin[8]={ + CHARSET_LATIN_G2, + CHARSET_LATIN_G2, + CHARSET_LATIN_G2, + CHARSET_LATIN_G2, + CHARSET_LATIN_G2, + CHARSET_LATIN_G2, + CHARSET_LATIN_G2, + CHARSET_LATIN_G2 +}; + +enumCharsets FontBlockG0_0001[8] = { + CHARSET_LATIN_G0_PL, + CHARSET_LATIN_G0_DE, + CHARSET_LATIN_G0_SV_FI, + CHARSET_LATIN_G0_IT, + CHARSET_LATIN_G0_FR, + CHARSET_LATIN_G0, + CHARSET_LATIN_G0_CZ_SK, + CHARSET_LATIN_G0 +}; + +enumCharsets FontBlockG0_0010[8] = { + CHARSET_LATIN_G0_EN, + CHARSET_LATIN_G0_DE, + CHARSET_LATIN_G0_SV_FI, + CHARSET_LATIN_G0_IT, + CHARSET_LATIN_G0_FR, + CHARSET_LATIN_G0_PT_ES, + CHARSET_LATIN_G0_TR, + CHARSET_LATIN_G0 +}; + + +enumCharsets FontBlockG0_0011[8] = { + CHARSET_LATIN_G0, + CHARSET_LATIN_G0, + CHARSET_LATIN_G0, + CHARSET_LATIN_G0, + CHARSET_LATIN_G0, + CHARSET_LATIN_G0_SR_HR_SL, + CHARSET_LATIN_G0, + CHARSET_LATIN_G0_RO +}; + +enumCharsets FontBlockG0_0100[8] = { + CHARSET_CYRILLIC_G0_SR_HR, + CHARSET_LATIN_G0_DE, + CHARSET_LATIN_G0_EE, + CHARSET_LATIN_G0_LV_LT, + CHARSET_CYRILLIC_G0_RU_BG, + CHARSET_CYRILLIC_G0_UK, + CHARSET_LATIN_G0_CZ_SK, + CHARSET_INVALID +}; + +enumCharsets FontBlockG2_0100[8] = { + CHARSET_CYRILLIC_G2, + CHARSET_LATIN_G2, + CHARSET_LATIN_G2, + CHARSET_LATIN_G2, + CHARSET_CYRILLIC_G2, + CHARSET_CYRILLIC_G2, + CHARSET_LATIN_G2, + CHARSET_INVALID +}; + +enumCharsets FontBlockG0_0110[8] = { + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_LATIN_G0_TR, + CHARSET_GREEK_G0 +}; + +enumCharsets FontBlockG2_0110[8] = { + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_LATIN_G2, + CHARSET_GREEK_G2 +}; + +enumCharsets FontBlockG0_1000[8] = { + CHARSET_LATIN_G0_EN, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_LATIN_G0_FR, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_ARABIC_G0 +}; + +enumCharsets FontBlockG2_1000[8] = { + CHARSET_ARABIC_G2, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_ARABIC_G2, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_ARABIC_G2 +}; + +enumCharsets FontBlockG0_1010[8] = { + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_HEBREW_G0, + CHARSET_INVALID, + CHARSET_ARABIC_G0, +}; + +enumCharsets FontBlockG2_1010[8] = { + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_ARABIC_G2, + CHARSET_INVALID, + CHARSET_ARABIC_G2, +}; + +enumCharsets FontBlockInvalid[8] = { + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID, + CHARSET_INVALID +}; + + + +// The actual font table definition: +// Split the 7-bit number into upper 4 and lower 3 bits, +// use upper 4 bits for outer array, +// use lower 3 bits for inner array + +struct structFontBlock { + enumCharsets *G0Block; + enumCharsets *G2Block; +}; + +structFontBlock FontTable[16] = { + { FontBlockG0_0000, FontBlockG2Latin }, // 0000 block + { FontBlockG0_0001, FontBlockG2Latin }, // 0001 block + { FontBlockG0_0010, FontBlockG2Latin }, // 0010 block + { FontBlockG0_0011, FontBlockG2Latin }, // 0011 block + { FontBlockG0_0100, FontBlockG2_0100 }, // 0100 block + { FontBlockInvalid, FontBlockInvalid }, // 0101 block + { FontBlockG0_0110, FontBlockG2_0110 }, // 0110 block + { FontBlockInvalid, FontBlockInvalid }, // 0111 block + { FontBlockG0_1000, FontBlockG2_1000 }, // 1000 block + { FontBlockInvalid, FontBlockInvalid }, // 1001 block + { FontBlockG0_1010, FontBlockG2_1010 }, // 1010 block + { FontBlockInvalid, FontBlockInvalid }, // 1011 block + { FontBlockInvalid, FontBlockInvalid }, // 1100 block + { FontBlockInvalid, FontBlockInvalid }, // 1101 block + { FontBlockInvalid, FontBlockInvalid }, // 1110 block + { FontBlockInvalid, FontBlockInvalid } // 1111 block +}; + +inline enumCharsets GetG0Charset(int codepage) { + return FontTable[codepage>>3].G0Block[codepage&7]; +} +inline enumCharsets GetG2Charset(int codepage) { + return FontTable[codepage>>3].G2Block[codepage&7]; +} + +enum enumSizeMode { + // Possible size modifications of characters + sizeNormal, + sizeDoubleWidth, + sizeDoubleHeight, + sizeDoubleSize +}; + +void TeletextDecoderVBIEBU::RenderTeletextCode(bool renderfirstlineonly) { + int x,y; + bool EmptyNextLine=false; + // Skip one line, in case double height chars were/will be used + + // Get code pages: + int LocalG0CodePage=(FirstG0CodePage & 0x78) + | ((lang & 0x04)>>2) | (lang & 0x02) | ((lang & 0x01)<<2); + enumCharsets FirstG0=GetG0Charset(LocalG0CodePage); + enumCharsets SecondG0=GetG0Charset(SecondG0CodePage); + // Reserved for later use: + // enumCharsets FirstG2=GetG2Charset(LocalG0CodePage); + + for (y=0;y<24;(EmptyNextLine?y+=2:y++)) { + // Start of line: Set start of line defaults + + // Hold Mosaics mode: Remember last mosaic char/charset + // for next spacing code + bool HoldMosaics=false; + unsigned char HoldMosaicChar=' '; + enumCharsets HoldMosaicCharset=FirstG0; + + enumSizeMode Size=sizeNormal; + // Font size modification + bool SecondCharset=false; + // Use primary or secondary G0 charset + bool GraphicCharset=false; + // Graphics charset used? + bool SeparateGraphics=false; + // Use separated vs. contiguous graphics charset + bool NoNextChar=false; + // Skip display of next char, for double-width + EmptyNextLine=false; + // Skip next line, for double-height + + cTeletextChar c; + // auto.initialized to everything off + c.SetFGColor(ttcWhite); + c.SetBGColor(ttcBlack); + c.SetCharset(FirstG0); + + if (y==0 && (flags&0x10)) { + if (!inkeying ) c.SetBoxedOut(true); + + } + if (flags&0x60) { + if (!(inkeying && y==0) ) c.SetBoxedOut(true); + + } + if (y==0) { + + for (x=0;x<8;x++) { + cTeletextChar c2=c; + + if (x>=3 && x<6){ + c2.SetChar(keyindigits[x-3]); + + } + else + c2.SetChar(' '); + setChar(x,0,c2); + } + } + + // Pre-scan for double-height and double-size codes + for (x=0;x<40;x++) { + if (y==0 && x<8) x=8; + if ((curpage[y][x] & 0x7f)==0x0D || (curpage[y][x] & 0x7f)==0x0F) + EmptyNextLine=true; + } + + // Move through line + for (x=0;x<40;x++) { + unsigned char ttc=curpage[y][x] & 0x7f; + // skip parity check + + if (y==0 && x<8) continue; + if (y==0 && x<31 && renderfirstlineonly && gotcha && !inkeying) continue; + // no displayable data here... + +/* // Debug only: Output line data and spacing codes + if (y==6) { + if (ttc<0x20) + printf("%s ",names[ttc]); + else + printf("%02x ",ttc); + if (x==39) printf("\n"); + } +*/ + + // Handle all 'Set-At' spacing codes + switch (ttc) { + case 0x09: // Steady + c.SetBlink(false); + break; + case 0x0C: // Normal Size + if (Size!=sizeNormal) { + Size=sizeNormal; + HoldMosaicChar=' '; + HoldMosaicCharset=FirstG0; + } + break; + case 0x18: // Conceal + c.SetConceal(true); + break; + case 0x19: // Contiguous Mosaic Graphics + SeparateGraphics=false; + if (GraphicCharset) + c.SetCharset(CHARSET_GRAPHICS_G1); + break; + case 0x1A: // Separated Mosaic Graphics + SeparateGraphics=true; + if (GraphicCharset) + c.SetCharset(CHARSET_GRAPHICS_G1_SEP); + break; + case 0x1C: // Black Background + c.SetBGColor(ttcBlack); + break; + case 0x1D: // New Background + c.SetBGColor(c.GetFGColor()); + break; + case 0x1E: // Hold Mosaic + HoldMosaics=true; + break; + } + + // temporary copy of character data: + cTeletextChar c2=c; + // c2 will be text character or space character or hold mosaic + // c2 may also have temporary flags or charsets + + if (ttc<0x20) { + // Spacing code, display space or hold mosaic + if (HoldMosaics) { + c2.SetChar(HoldMosaicChar); + c2.SetCharset(HoldMosaicCharset); + } else { + c2.SetChar(' '); + } + } else { + // Character code + c2.SetChar(ttc); + if (GraphicCharset) { + if (ttc&0x20) { + // real graphics code, remember for HoldMosaics + HoldMosaicChar=ttc; + HoldMosaicCharset=c.GetCharset(); + } else { + // invalid code, pass-through to G0 + c2.SetCharset(SecondCharset?SecondG0:FirstG0); + } + } + } + + // Handle double-height and double-width extremes + if (y>=23) { + if (Size==sizeDoubleHeight) Size=sizeNormal; + if (Size==sizeDoubleSize) Size=sizeDoubleWidth; + } + if (x>=38) { + if (Size==sizeDoubleWidth) Size=sizeNormal; + if (Size==sizeDoubleSize) Size=sizeDoubleHeight; + } + + // Now set character code + + if (NoNextChar) { + // Suppress this char due to double width last char + NoNextChar=false; + } else { + switch (Size) { + case sizeNormal: + // Normal sized + setChar(x,y,c2); + if (EmptyNextLine && y<23) { + // Clean up next line + setChar(x,y+1,c2.ToChar(' ').ToCharset(FirstG0)); + } + break; + case sizeDoubleWidth: + // Double width + setChar(x,y,c2.ToDblWidth(dblw_Left)); + setChar(x+1,y,c2.ToDblWidth(dblw_Right)); + if (EmptyNextLine && y<23) { + // Clean up next line + setChar(x ,y+1,c2.ToChar(' ').ToCharset(FirstG0)); + setChar(x+1,y+1,c2.ToChar(' ').ToCharset(FirstG0)); + } + NoNextChar=true; + break; + case sizeDoubleHeight: + // Double height + setChar(x,y,c2.ToDblHeight(dblh_Top)); + setChar(x,y+1,c2.ToDblHeight(dblh_Bottom)); + break; + case sizeDoubleSize: + // Double Size + setChar(x , y,c2.ToDblHeight(dblh_Top ).ToDblWidth(dblw_Left )); + setChar(x+1, y,c2.ToDblHeight(dblh_Top ).ToDblWidth(dblw_Right)); + setChar(x ,y+1,c2.ToDblHeight(dblh_Bottom).ToDblWidth(dblw_Left )); + setChar(x+1,y+1,c2.ToDblHeight(dblh_Bottom).ToDblWidth(dblw_Right)); + NoNextChar=true; + break; + } + } + + // Handle all 'Set-After' spacing codes + if (ttc>=0x00 && ttc<=0x07) { // Set FG color + if (GraphicCharset) { + // Actual switch from graphics charset + HoldMosaicChar=' '; + HoldMosaicCharset=FirstG0; + } + c.SetFGColor((enumTeletextColor)ttc); + c.SetCharset(SecondCharset?SecondG0:FirstG0); + GraphicCharset=false; + c.SetConceal(false); + } else if (ttc==0x08) { + c.SetBlink(true); + } else if (ttc==0x0A) { + c.SetBoxedOut(true); + } else if (ttc==0x0B) { + // Start Box + c.SetBoxedOut(false); + } else if (ttc==0x0D) { + if (Size!=sizeDoubleHeight) { + Size=sizeDoubleHeight; + HoldMosaicChar=' '; + HoldMosaicCharset=FirstG0; + } + } else if (ttc==0x0E) { + if (Size!=sizeDoubleWidth) { + Size=sizeDoubleWidth; + HoldMosaicChar=' '; + HoldMosaicCharset=FirstG0; + } + } else if (ttc==0x0E) { + if (Size!=sizeDoubleSize) { + Size=sizeDoubleSize; + HoldMosaicChar=' '; + HoldMosaicCharset=FirstG0; + } + } else if (ttc>=0x10 && ttc<=0x17) { + if (!GraphicCharset) { + // Actual switch to graphics charset + HoldMosaicChar=' '; + HoldMosaicCharset=FirstG0; + } + c.SetFGColor((enumTeletextColor)(ttc-0x10)); + c.SetCharset(SeparateGraphics?CHARSET_GRAPHICS_G1_SEP:CHARSET_GRAPHICS_G1); + GraphicCharset=true; + c.SetConceal(false); + } else if (ttc==0x1B) { + SecondCharset=!SecondCharset; + if (!GraphicCharset) c.SetCharset(SecondCharset?SecondG0:FirstG0); + } else if (ttc==0x1F) { + HoldMosaics=false; + } + + } // end for x + if (renderfirstlineonly) break; + } // end for y + + for (x=0;x<40;x++) { + // Clean out last line + cTeletextChar c; + c.SetFGColor(ttcWhite); + c.SetBGColor(ttcBlack); + c.SetCharset(FirstG0); + c.SetChar(' '); + if (flags&0x60) { + c.SetBoxedOut(true); + } + setChar(x,24,c); + } + for (y=0;y<25;y++) { + for (x=0;x<40;x++) { + if (isDirty(x,y)) { + dirty=true; + break; + } + if (dirty) break; + } + if (dirty) break; + } + + if (dirty && txtview!=NULL ) { + + if ( !renderfirstlineonly) { + Message* m= new Message(); + m->message = Message::TELETEXTUPDATE; + m->to = txtview; + m->from = this; + m->parameter = 0; + Command::getInstance()->postMessageFromOuterSpace(m); + } else if (firstlineupdate==10) { + Message* m= new Message(); + m->message = Message::TELETEXTUPDATEFIRSTLINE; + m->to = txtview; + m->from = this; + m->parameter = 0; + Command::getInstance()->postMessageFromOuterSpace(m); + firstlineupdate=0; + } else firstlineupdate++; + + + } + +} diff --git a/tfeed.cc b/tfeed.cc index 040fe09..0b88678 100644 --- a/tfeed.cc +++ b/tfeed.cc @@ -1,63 +1,63 @@ -/* - 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. -*/ - -#include "tfeed.h" -#include "log.h" #include "demuxer.h" #include "callback.h" - -TFeed::TFeed(Callback* tcb) : cb(*tcb) { teletextEnabled = 1; } - -int TFeed::init() { return 1; } - -int TFeed::shutdown() { - // FIXME return 1; } - -void TFeed::disable() -{ teletextEnabled = 0; -} - -void TFeed::enable() -{ teletextEnabled = 1; -} - -int TFeed::start() -{ - teletextEnabled = 1; - return threadStart(); -} - -void TFeed::stop() -{ - threadCancel(); -} - -void TFeed::threadMethod() -{ - bool tlen; - while(1) { - threadCheckExit(); - tlen = Demuxer::getInstance()->writeTeletext(); - if (tlen) - { - cb.call(this); -// Log::getInstance()->log("Tfeed", Log::DEBUG, "written"); - } - else - { - //MILLISLEEP(100); - MILLISLEEP(20); //Performance Issue Marten - } - } +/* + 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. +*/ + +#include "tfeed.h" +#include "log.h" #include "demuxer.h" #include "callback.h" + +TFeed::TFeed(Callback* tcb) : cb(*tcb) { teletextEnabled = 1; } + +int TFeed::init() { return 1; } + +int TFeed::shutdown() { + // FIXME return 1; } + +void TFeed::disable() +{ teletextEnabled = 0; +} + +void TFeed::enable() +{ teletextEnabled = 1; +} + +int TFeed::start() +{ + teletextEnabled = 1; + return threadStart(); +} + +void TFeed::stop() +{ + threadCancel(); +} + +void TFeed::threadMethod() +{ + bool tlen; + while(1) { + threadCheckExit(); + tlen = Demuxer::getInstance()->writeTeletext(); + if (tlen) + { + cb.call(this); +// Log::getInstance()->log("Tfeed", Log::DEBUG, "written"); + } + else + { + //MILLISLEEP(100); + MILLISLEEP(20); //Performance Issue Marten + } + } } \ No newline at end of file diff --git a/tfeed.h b/tfeed.h index 1c153f1..e713bb0 100644 --- a/tfeed.h +++ b/tfeed.h @@ -1,21 +1,21 @@ -/* 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. -*/ -#ifndef TFEED_H #define TFEED_H -#include #include -#include "threadsystem.h" -class Callback; -class TFeed: public Thread_TYPE { public: TFeed(Callback* tcb); int init(); int shutdown(); int start(); void stop(); void enable(); void disable(); private: void threadMethod(); void threadPostStopCleanup() { }; int teletextEnabled; Callback& cb; }; - -#endif +/* 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. +*/ +#ifndef TFEED_H #define TFEED_H +#include #include +#include "threadsystem.h" +class Callback; +class TFeed: public Thread_TYPE { public: TFeed(Callback* tcb); int init(); int shutdown(); int start(); void stop(); void enable(); void disable(); private: void threadMethod(); void threadPostStopCleanup() { }; int teletextEnabled; Callback& cb; }; + +#endif diff --git a/threadsystem.h b/threadsystem.h index cddc15f..4d51203 100644 --- a/threadsystem.h +++ b/threadsystem.h @@ -1,29 +1,29 @@ -/* - 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 +/* + 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 diff --git a/vcolourtuner.cc b/vcolourtuner.cc index 1e828a4..bab815a 100644 --- a/vcolourtuner.cc +++ b/vcolourtuner.cc @@ -1,294 +1,294 @@ -/* - 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" +/* + 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 - -#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;rowdrawPixel(col,row,c); - } -} - - -void VColourTuner::draw() -{ - //do not call base classes draw to avoid drawing the picture... - Log::getInstance()->log("VColourTuner::draw",Log::DEBUG,"dp %s, rf=%d, gf=%d, bf=%d", - (drawPicture?"true":"false"),vrfactor,vgfactor,vbfactor); - char valbuf[20]; - int x=20; - int y=20; - int bw=50; - int bh=50; - int picx=picture.getX(); - 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 -} + +#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;rowdrawPixel(col,row,c); + } +} + + +void VColourTuner::draw() +{ + //do not call base classes draw to avoid drawing the picture... + Log::getInstance()->log("VColourTuner::draw",Log::DEBUG,"dp %s, rf=%d, gf=%d, bf=%d", + (drawPicture?"true":"false"),vrfactor,vgfactor,vbfactor); + char valbuf[20]; + int x=20; + int y=20; + int bw=50; + int bh=50; + int picx=picture.getX(); + 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 +} diff --git a/vdr.cc b/vdr.cc index 1798c3b..33ed8ec 100644 --- a/vdr.cc +++ b/vdr.cc @@ -1,1531 +1,1531 @@ -/* - 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& 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; -} +/* + 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& 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; +} diff --git a/vdr.h b/vdr.h index 5a5172d..b0ba3f6 100644 --- a/vdr.h +++ b/vdr.h @@ -1,292 +1,292 @@ -/* - Copyright 2004-2005 Chris Tallon - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - - -// FIXME - This and the protocol are overly complicated now. Sorry. -// I'll clean it up in a couple of releases time... - - -#ifndef VDR_H -#define VDR_H - -#include -#include -#include -#include - -#include "threadsystem.h" - -#include "defines.h" -#include "rectimer.h" -#include "mark.h" -#include "mediaprovider.h" -#include "eventdispatcher.h" -#include "i18n.h" -#include "log.h" - -class TCP; -class Log; -class RecInfo; -class Event; -class Channel; -class VDR_RequestPacket; -class VDR_ResponsePacket; -class SerializeBuffer; - -using namespace std; - -typedef vector EventList; -typedef vector ChannelList; -typedef vector RecTimerList; - -struct VDRServer -{ - char* ip; - char* name; -}; - -struct RecTimerSorter // : public binary_function -{ - bool operator() (const RecTimer* a, const RecTimer* b) - { - return a->startTime < b->startTime; - } -}; - -struct ServerSorter -{ - bool operator() (const VDRServer& a, const VDRServer& b) - { - if (strcmp(b.name, a.name) > 0) return true; - return false; - } -}; - -class RecMan; - -class StreamReceiver -{ - public: - virtual void streamReceive(ULONG, void*, ULONG)=0; -}; - -class VDR_PacketReceiver : public EDReceiver // implementation in vdr.cc -{ - public: - virtual bool call(void* userTag); - - friend class VDR; - protected: -// ULONG requestTime; - ULONG receiverChannel; - - // If receiverChannel == 1: - ULONG requestSerialNumber; // set by RequestResponse, used in ed_cb_find - VDR_ResponsePacket* save_vresp; // set by ed_cb_call, used in RequestResponse - - // If receiverChannel == 2: - ULONG streamID; - StreamReceiver* streamReceiver; -}; - -class VDR : public Thread_TYPE, public EventDispatcher, public MediaProvider, public ExternLogger -{ - - public: - const static ULONG VIDEO = 1; - const static ULONG RADIO = 2; - - const static ULONG CHANNEL_REQUEST_RESPONSE = 1; - const static ULONG CHANNEL_STREAM = 2; - const static ULONG CHANNEL_KEEPALIVE = 3; - const static ULONG CHANNEL_NETLOG = 4; - - VDR(); - ~VDR(); - static VDR* getInstance(); - - int init(int port); - int shutdown(); - - void findServers(vector& servers); - void cancelFindingServer(); - void setServerIP(char*); - void setReceiveWindow(size_t size); - int connect(); - void disconnect(); - bool isConnected() { return connected; } - ULONG getChannelNumberWidth() { return channelNumberWidth; } - - // protocol functions - // for the following, if result == false then the connection has died - // doLogin - // getRecordingList - // getChannelsList - // getChannelSchedule - // getRecTimersList - // isConnected can be called after the following to determine if still ok - // deleteRecording - // streamRecording - // positionFromFrameNumber - // streamChannel - // getBlock - // stopStreaming - // configLoad - // configSave - // setEventTimer - - int doLogin(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 - -*/ - +/* + Copyright 2004-2005 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + + +// FIXME - This and the protocol are overly complicated now. Sorry. +// I'll clean it up in a couple of releases time... + + +#ifndef VDR_H +#define VDR_H + +#include +#include +#include +#include + +#include "threadsystem.h" + +#include "defines.h" +#include "rectimer.h" +#include "mark.h" +#include "mediaprovider.h" +#include "eventdispatcher.h" +#include "i18n.h" +#include "log.h" + +class TCP; +class Log; +class RecInfo; +class Event; +class Channel; +class VDR_RequestPacket; +class VDR_ResponsePacket; +class SerializeBuffer; + +using namespace std; + +typedef vector EventList; +typedef vector ChannelList; +typedef vector RecTimerList; + +struct VDRServer +{ + char* ip; + char* name; +}; + +struct RecTimerSorter // : public binary_function +{ + bool operator() (const RecTimer* a, const RecTimer* b) + { + return a->startTime < b->startTime; + } +}; + +struct ServerSorter +{ + bool operator() (const VDRServer& a, const VDRServer& b) + { + if (strcmp(b.name, a.name) > 0) return true; + return false; + } +}; + +class RecMan; + +class StreamReceiver +{ + public: + virtual void streamReceive(ULONG, void*, ULONG)=0; +}; + +class VDR_PacketReceiver : public EDReceiver // implementation in vdr.cc +{ + public: + virtual bool call(void* userTag); + + friend class VDR; + protected: +// ULONG requestTime; + ULONG receiverChannel; + + // If receiverChannel == 1: + ULONG requestSerialNumber; // set by RequestResponse, used in ed_cb_find + VDR_ResponsePacket* save_vresp; // set by ed_cb_call, used in RequestResponse + + // If receiverChannel == 2: + ULONG streamID; + StreamReceiver* streamReceiver; +}; + +class VDR : public Thread_TYPE, public EventDispatcher, public MediaProvider, public ExternLogger +{ + + public: + const static ULONG VIDEO = 1; + const static ULONG RADIO = 2; + + const static ULONG CHANNEL_REQUEST_RESPONSE = 1; + const static ULONG CHANNEL_STREAM = 2; + const static ULONG CHANNEL_KEEPALIVE = 3; + const static ULONG CHANNEL_NETLOG = 4; + + VDR(); + ~VDR(); + static VDR* getInstance(); + + int init(int port); + int shutdown(); + + void findServers(vector& servers); + void cancelFindingServer(); + void setServerIP(char*); + void setReceiveWindow(size_t size); + int connect(); + void disconnect(); + bool isConnected() { return connected; } + ULONG getChannelNumberWidth() { return channelNumberWidth; } + + // protocol functions + // for the following, if result == false then the connection has died + // doLogin + // getRecordingList + // getChannelsList + // getChannelSchedule + // getRecTimersList + // isConnected can be called after the following to determine if still ok + // deleteRecording + // streamRecording + // positionFromFrameNumber + // streamChannel + // getBlock + // stopStreaming + // configLoad + // configSave + // setEventTimer + + int doLogin(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 + +*/ + diff --git a/vdrcommand.h b/vdrcommand.h index 486f004..7f8d450 100644 --- a/vdrcommand.h +++ b/vdrcommand.h @@ -1,164 +1,164 @@ -/* - Copyright 2004-2005 Chris Tallon, Andreas Vogel - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#ifndef VDRCOMMAND_H -#define VDRCOMMAND_H - -#include "defines.h" -#include "serialize.h" -#include "media.h" - -/** - * data holder for VDR commands - * it's only important to add serializable objects - * in the same order on both sides - */ - -//until we really have response - commands we simply take -//the request+this flag for responses -//not really necessary but for checks it's better to have a command ID at least in some responses -const static ULONG VDR_RESPONSE_FLAG =0x1000000; - -//as this header is only included by vdr.cc the constants are this way private -//but can easily be used on the server side as well - -const static ULONG VDR_LOGIN = 1; -const static ULONG VDR_GETRECORDINGLIST = 2; -const static ULONG VDR_DELETERECORDING = 3; -const static ULONG VDR_GETCHANNELLIST = 5; -const static ULONG VDR_STREAMCHANNEL = 6; -const static ULONG VDR_GETBLOCK = 7; -const static ULONG VDR_STOPSTREAMING = 8; -const static ULONG VDR_STREAMRECORDING = 9; -const static ULONG VDR_GETCHANNELSCHEDULE = 10; -const static ULONG VDR_CONFIGSAVE = 11; -const static ULONG VDR_CONFIGLOAD = 12; -const static ULONG VDR_RESCANRECORDING = 13; // FIXME obselete -const static ULONG VDR_GETTIMERS = 14; -const static ULONG VDR_SETTIMER = 15; -const static ULONG VDR_POSFROMFRAME = 16; -const static ULONG VDR_FRAMEFROMPOS = 17; -const static ULONG VDR_MOVERECORDING = 18; -const static ULONG VDR_GETNEXTIFRAME = 19; -const static ULONG VDR_GETRECINFO = 20; -const static ULONG VDR_GETMARKS = 21; -const static ULONG VDR_GETCHANNELPIDS = 22; -const static ULONG VDR_DELETETIMER = 23; -const static ULONG VDR_GETLANGUAGELIST = 33; -const static ULONG VDR_GETLANGUAGECONTENT = 34; -const static ULONG VDR_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 +/* + Copyright 2004-2005 Chris Tallon, Andreas Vogel + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef VDRCOMMAND_H +#define VDRCOMMAND_H + +#include "defines.h" +#include "serialize.h" +#include "media.h" + +/** + * data holder for VDR commands + * it's only important to add serializable objects + * in the same order on both sides + */ + +//until we really have response - commands we simply take +//the request+this flag for responses +//not really necessary but for checks it's better to have a command ID at least in some responses +const static ULONG VDR_RESPONSE_FLAG =0x1000000; + +//as this header is only included by vdr.cc the constants are this way private +//but can easily be used on the server side as well + +const static ULONG VDR_LOGIN = 1; +const static ULONG VDR_GETRECORDINGLIST = 2; +const static ULONG VDR_DELETERECORDING = 3; +const static ULONG VDR_GETCHANNELLIST = 5; +const static ULONG VDR_STREAMCHANNEL = 6; +const static ULONG VDR_GETBLOCK = 7; +const static ULONG VDR_STOPSTREAMING = 8; +const static ULONG VDR_STREAMRECORDING = 9; +const static ULONG VDR_GETCHANNELSCHEDULE = 10; +const static ULONG VDR_CONFIGSAVE = 11; +const static ULONG VDR_CONFIGLOAD = 12; +const static ULONG VDR_RESCANRECORDING = 13; // FIXME obselete +const static ULONG VDR_GETTIMERS = 14; +const static ULONG VDR_SETTIMER = 15; +const static ULONG VDR_POSFROMFRAME = 16; +const static ULONG VDR_FRAMEFROMPOS = 17; +const static ULONG VDR_MOVERECORDING = 18; +const static ULONG VDR_GETNEXTIFRAME = 19; +const static ULONG VDR_GETRECINFO = 20; +const static ULONG VDR_GETMARKS = 21; +const static ULONG VDR_GETCHANNELPIDS = 22; +const static ULONG VDR_DELETETIMER = 23; +const static ULONG VDR_GETLANGUAGELIST = 33; +const static ULONG VDR_GETLANGUAGECONTENT = 34; +const static ULONG VDR_SETCHARSET = 37; +const static ULONG VDR_GETMEDIALIST = 30; +const static ULONG VDR_OPENMEDIA = 31; +const static ULONG VDR_GETMEDIABLOCK = 32; +const static ULONG VDR_GETMEDIAINFO = 35; +const static ULONG VDR_CLOSECHANNEL = 36; + +class VDR_Command : public SerializableList { + public: + VDR_Command(const ULONG cmd) { + command=cmd; + addParam(&command); + } + virtual ~VDR_Command(){} + ULONG command; +}; + +class VDR_GetMediaListRequest : public VDR_Command { + public: + VDR_GetMediaListRequest(MediaURI *root) :VDR_Command(VDR_GETMEDIALIST) { + addParam(root); + } +}; + +class VDR_GetMediaListResponse : public VDR_Command { + public: + VDR_GetMediaListResponse(ULONG *flags,MediaList *m) : VDR_Command(VDR_GETMEDIALIST|VDR_RESPONSE_FLAG){ + addParam(flags); + addParam(m); + } +}; + +class VDR_OpenMediumRequest : public VDR_Command { + public: + VDR_OpenMediumRequest(ULONG *channel,MediaURI *u,ULONG *xsize, ULONG *ysize) : + VDR_Command(VDR_OPENMEDIA) { + addParam(channel); + addParam(u); + addParam(xsize); + addParam(ysize); + } +}; +class VDR_OpenMediumResponse : public VDR_Command { + public: + VDR_OpenMediumResponse(ULONG *flags,ULLONG *size) : + VDR_Command(VDR_OPENMEDIA|VDR_RESPONSE_FLAG) { + addParam(flags); + addParam(size); + } +}; +class VDR_GetMediaBlockRequest : public VDR_Command { + public: + VDR_GetMediaBlockRequest(ULONG * channel, ULLONG *pos, ULONG *max): + VDR_Command(VDR_GETMEDIABLOCK) { + addParam(channel); + addParam(pos); + addParam(max); + } +}; + +//no response class for GetMediaBlock + + +class VDR_CloseMediaChannelRequest : public VDR_Command { + public: + VDR_CloseMediaChannelRequest(ULONG * channel): + VDR_Command(VDR_CLOSECHANNEL) { + addParam(channel); + } +}; + +class VDR_CloseMediaChannelResponse : public VDR_Command { + public: + VDR_CloseMediaChannelResponse(ULONG * flags): + VDR_Command(VDR_CLOSECHANNEL|VDR_RESPONSE_FLAG) { + addParam(flags); + } +}; + +class VDR_GetMediaInfoRequest : public VDR_Command { + public: + VDR_GetMediaInfoRequest(ULONG * channel): + VDR_Command(VDR_GETMEDIAINFO) { + addParam(channel); + } +}; +class VDR_GetMediaInfoResponse : public VDR_Command { + public: + VDR_GetMediaInfoResponse(ULONG * flags,MediaInfo *info): + VDR_Command(VDR_GETMEDIAINFO|VDR_RESPONSE_FLAG) { + addParam(flags); + addParam(info); + } +}; + + + + +#endif diff --git a/vdrresponsepacket.cc b/vdrresponsepacket.cc index 8a14fd2..37b2be5 100644 --- a/vdrresponsepacket.cc +++ b/vdrresponsepacket.cc @@ -1,150 +1,150 @@ -/* - Copyright 2007 Chris Tallon - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#include "vdrresponsepacket.h" - -#include "vdr.h" -#include "tcp.h" - -VDR_ResponsePacket::VDR_ResponsePacket() -{ - userDataLength = 0; - packetPos = 0; - userData = NULL; - ownBlock = true; - - channelID = 0; - - requestID = 0; - streamID = 0; - - flag = 0; -} - -VDR_ResponsePacket::~VDR_ResponsePacket() -{ - if (!ownBlock) return; // don't free if it's a getblock - - if (userData) free(userData); -} - -void VDR_ResponsePacket::setResponse(ULONG trequestID, UCHAR* tuserData, ULONG tuserDataLength) -{ - channelID = VDR::CHANNEL_REQUEST_RESPONSE; - requestID = trequestID; - userData = tuserData; - userDataLength = tuserDataLength; -} - -void VDR_ResponsePacket::setStream(ULONG tstreamID, ULONG tflag, UCHAR* tuserData, ULONG tuserDataLength) -{ - channelID = VDR::CHANNEL_STREAM; - streamID = tstreamID; - flag = tflag; - userData = tuserData; - userDataLength = tuserDataLength; -} - -bool VDR_ResponsePacket::end() -{ - return (packetPos >= userDataLength); -} - -void VDR_ResponsePacket::dumpUD() -{ - TCP::dump(userData, userDataLength); -} - -int VDR_ResponsePacket::serverError() -{ - if ((packetPos == 0) && (userDataLength == 4) && !ntohl(*(ULONG*)userData)) return 1; - else return 0; -} - -char* VDR_ResponsePacket::extractString() -{ - if (serverError()) return NULL; - - int length = strlen((char*)&userData[packetPos]); - if ((packetPos + length) > userDataLength) return NULL; - char* str = new char[length + 1]; - strcpy(str, (char*)&userData[packetPos]); - packetPos += length + 1; - return str; -} - -UCHAR VDR_ResponsePacket::extractUCHAR() -{ - if ((packetPos + sizeof(UCHAR)) > userDataLength) return 0; - UCHAR uc = userData[packetPos]; - packetPos += sizeof(UCHAR); - return uc; -} - -ULONG VDR_ResponsePacket::extractULONG() -{ - if ((packetPos + sizeof(ULONG)) > userDataLength) return 0; - ULONG ul = 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; -} - +/* + Copyright 2007 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "vdrresponsepacket.h" + +#include "vdr.h" +#include "tcp.h" + +VDR_ResponsePacket::VDR_ResponsePacket() +{ + userDataLength = 0; + packetPos = 0; + userData = NULL; + ownBlock = true; + + channelID = 0; + + requestID = 0; + streamID = 0; + + flag = 0; +} + +VDR_ResponsePacket::~VDR_ResponsePacket() +{ + if (!ownBlock) return; // don't free if it's a getblock + + if (userData) free(userData); +} + +void VDR_ResponsePacket::setResponse(ULONG trequestID, UCHAR* tuserData, ULONG tuserDataLength) +{ + channelID = VDR::CHANNEL_REQUEST_RESPONSE; + requestID = trequestID; + userData = tuserData; + userDataLength = tuserDataLength; +} + +void VDR_ResponsePacket::setStream(ULONG tstreamID, ULONG tflag, UCHAR* tuserData, ULONG tuserDataLength) +{ + channelID = VDR::CHANNEL_STREAM; + streamID = tstreamID; + flag = tflag; + userData = tuserData; + userDataLength = tuserDataLength; +} + +bool VDR_ResponsePacket::end() +{ + return (packetPos >= userDataLength); +} + +void VDR_ResponsePacket::dumpUD() +{ + TCP::dump(userData, userDataLength); +} + +int VDR_ResponsePacket::serverError() +{ + if ((packetPos == 0) && (userDataLength == 4) && !ntohl(*(ULONG*)userData)) return 1; + else return 0; +} + +char* VDR_ResponsePacket::extractString() +{ + if (serverError()) return NULL; + + int length = strlen((char*)&userData[packetPos]); + if ((packetPos + length) > userDataLength) return NULL; + char* str = new char[length + 1]; + strcpy(str, (char*)&userData[packetPos]); + packetPos += length + 1; + return str; +} + +UCHAR VDR_ResponsePacket::extractUCHAR() +{ + if ((packetPos + sizeof(UCHAR)) > userDataLength) return 0; + UCHAR uc = userData[packetPos]; + packetPos += sizeof(UCHAR); + return uc; +} + +ULONG VDR_ResponsePacket::extractULONG() +{ + if ((packetPos + sizeof(ULONG)) > userDataLength) return 0; + ULONG ul = userData[packetPos++]<<24; + ul|= userData[packetPos++]<<16; + ul|= userData[packetPos++]<<8; + ul|= userData[packetPos++]; + //packetPos += sizeof(ULONG); + return ul; +} + +ULLONG VDR_ResponsePacket::extractULLONG() +{ + if ((packetPos + sizeof(ULLONG)) > userDataLength) return 0; + ULLONG ull= ((ULLONG)userData[packetPos++])<<56; + ull|= ((ULLONG)userData[packetPos++])<<48; + ull|= ((ULLONG)userData[packetPos++])<<40; + ull|= ((ULLONG)userData[packetPos++])<<32; + ull|= ((ULLONG)userData[packetPos++])<<24; + ull|= ((ULLONG)userData[packetPos++])<<16; + ull|= ((ULLONG)userData[packetPos++])<<8; + ull|= ((ULLONG)userData[packetPos++]); + return ull; +} + +double VDR_ResponsePacket::extractdouble() +{ + if ((packetPos + sizeof(ULLONG)) > userDataLength) return 0; + ULLONG ull = extractULLONG(); + double d; + memcpy(&d,&ull,sizeof(double)); + return d; +} + +long VDR_ResponsePacket::extractLONG() +{ + if ((packetPos + sizeof(long)) > userDataLength) return 0; + long l = userData[packetPos++]<<24; + l|= userData[packetPos++]<<16; + l|= userData[packetPos++]<<8; + l|= userData[packetPos++]; + return l; +} + +UCHAR* VDR_ResponsePacket::getUserData() +{ + ownBlock = false; + return userData; +} + diff --git a/vepg.cc b/vepg.cc index c60f1cf..72edcae 100644 --- a/vepg.cc +++ b/vepg.cc @@ -1,828 +1,828 @@ -/* - 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 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); - } - } - } -} - +/* + 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 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); + } + } + } +} + diff --git a/vfeed.cc b/vfeed.cc index b0fefab..1f0af70 100644 --- a/vfeed.cc +++ b/vfeed.cc @@ -1,84 +1,84 @@ -/* - Copyright 2004-2005 Chris Tallon - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#include "vfeed.h" - -#include "log.h" -#include "demuxer.h" -#include "callback.h" - -VFeed::VFeed(Callback* tcb) -: cb(*tcb) -{ -} - -int VFeed::init() -{ - return 1; -} - -int VFeed::shutdown() -{ - // FIXME - return 1; -} - -int VFeed::start() -{ - return threadStart(); -} - -void VFeed::stop() -{ - 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); - } - } -} - +/* + 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); + } + } +} + diff --git a/video.cc b/video.cc index 864c1a0..135d756 100644 --- a/video.cc +++ b/video.cc @@ -1,98 +1,98 @@ -/* - Copyright 2004-2005 Chris Tallon - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ -//portions from vdr by Klaus Schmiding HSMF code - -#include "video.h" - -Video* Video::instance = NULL; - -Video::Video() -{ - if (instance) return; - instance = this; - initted = 0; - - fdVideo = 0; - - format = 0; - connection = 0; - aspectRatio = 0; - mode = 0; - tvsize = 0; - - screenWidth = 0; - screenHeight = 0; -} - -Video::~Video() -{ - instance = NULL; -} - -Video* Video::getInstance() -{ - return instance; -} -/* -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; -} -*/ +/* + Copyright 2004-2005 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +//portions from vdr by Klaus Schmiding HSMF code + +#include "video.h" + +Video* Video::instance = NULL; + +Video::Video() +{ + if (instance) return; + instance = this; + initted = 0; + + fdVideo = 0; + + format = 0; + connection = 0; + aspectRatio = 0; + mode = 0; + tvsize = 0; + + screenWidth = 0; + screenHeight = 0; +} + +Video::~Video() +{ + instance = NULL; +} + +Video* Video::getInstance() +{ + return instance; +} +/* +void Video::setInstance(Video* inst) +{ + instance=inst; +}*/ + +/* +hmsf Video::framesToHMSF(ULONG frames, double fps) +{ + hmsf ret; + / * from vdr * + double Seconds; + ret.frames= int(modf((frames + 0.5) / fps, &Seconds) * fps + 1); + int s = int(Seconds); + ret.seconds=s; + ret.minutes = s / 60 % 60; + ret.hours = s / 3600; + +/ * if (format == NTSC) + { + ret.hours = frames / 108000; + frames %= 108000; + ret.minutes = frames / 1800; + frames %= 1800; + ret.seconds = frames / 30; + ret.frames = frames % 30; + } + else + { + ret.hours = frames / 90000; + frames %= 90000; + ret.minutes = frames / 1500; + frames %= 1500; + ret.seconds = frames / 25; + ret.frames = frames % 25; + }* / + return ret; +}*/ + +/* +UINT Video::getFPS() +{ + if (format == NTSC) return 30; + else return 25; +} +*/ diff --git a/video.h b/video.h index 9c9611f..bacd8ea 100644 --- a/video.h +++ b/video.h @@ -1,179 +1,179 @@ -/* - Copyright 2004-2005 Chris Tallon - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#ifndef VIDEO_H -#define VIDEO_H - -#include -#include "defines.h" -#include "draintarget.h" -#include "abstractoption.h" - -typedef struct _hmsf -{ - UINT hours; - UINT minutes; - UINT seconds; - UINT frames; -} hmsf; - -class Video: public DrainTarget, public AbstractOption -{ - public: - Video(); - virtual ~Video(); - static Video* getInstance(); - //static void setInstance(Video*); - - virtual int init(UCHAR format)=0; - virtual int shutdown()=0; - virtual int setFormat(UCHAR format)=0; - virtual 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 +/* + Copyright 2004-2005 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef VIDEO_H +#define VIDEO_H + +#include +#include "defines.h" +#include "draintarget.h" +#include "abstractoption.h" + +typedef struct _hmsf +{ + UINT hours; + UINT minutes; + UINT seconds; + UINT frames; +} hmsf; + +class Video: public DrainTarget, public AbstractOption +{ + public: + Video(); + virtual ~Video(); + static Video* getInstance(); + //static void setInstance(Video*); + + virtual int init(UCHAR format)=0; + virtual int shutdown()=0; + virtual int setFormat(UCHAR format)=0; + virtual 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 diff --git a/videomvp.cc b/videomvp.cc index 1c6313b..7f12942 100644 --- a/videomvp.cc +++ b/videomvp.cc @@ -1,406 +1,406 @@ -/* - Copyright 2004-2005 Chris Tallon - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#include "videomvp.h" - -// temp -#include "log.h" - -VideoMVP::VideoMVP() -{ - if (instance) return; -} - -VideoMVP::~VideoMVP() -{ - instance = NULL; -} - -int VideoMVP::init(UCHAR tformat) -{ - if (initted) return 0; - initted = 1; - - if ((fdVideo = open("/dev/vdec_dev", O_WRONLY)) < 0) return 0; - - if (!setFormat(tformat)) { shutdown(); return 0; } - if (!setConnection(COMPOSITERGB)) { shutdown(); return 0; } - if (!setAspectRatio(ASPECT4X3)) { shutdown(); return 0; } - if (!setMode(NORMAL)) { shutdown(); return 0; } - if (!setSource()) { shutdown(); return 0; } - if (!attachFrameBuffer()) { shutdown(); return 0; } - - setTVsize(ASPECT4X3); - - if (format == PAL) setLetterboxBorder("38"); - else setLetterboxBorder("31"); - - stop(); - - - return 1; -} - -void VideoMVP::setLetterboxBorder(char* border) -{ - FILE* fdlbox = fopen("/proc/lbox_border", "w"); - if (!fdlbox) return; - fputs(border, fdlbox); - fclose(fdlbox); -} - -int VideoMVP::setTVsize(UCHAR ttvsize) -{ - tvsize = ttvsize; - - // Override the aspect ratio usage, temporarily use to set the video chip mode - if (!setAspectRatio(tvsize)) { shutdown(); return 0; } - close(fdVideo); - if ((fdVideo = open("/dev/vdec_dev", O_WRONLY)) < 0) return 0; - if (!setSource()) { shutdown(); return 0; } - if (!attachFrameBuffer()) { shutdown(); return 0; } - - // Reopening the fd causes the scart aspect line to go back to 4:3 - // Set this again to the same as the tv screen size - if (!setAspectRatio(tvsize)) { shutdown(); return 0; } - - // mode == LETTERBOX is invalid if the TV is widescreen - if (tvsize == ASPECT16X9) setMode(NORMAL); - - return 1; -} - -int VideoMVP::setDefaultAspect() -{ - return setAspectRatio(tvsize); -} - -int VideoMVP::shutdown() -{ - if (!initted) return 0; - initted = 0; - close(fdVideo); - return 1; -} - -int VideoMVP::checkSCART() -{ - // Returns 3 for SCART Composite out - // Returns 3 for SCART S-Video out - // Returns 2 for SCART RGB out - // Returns 3 for SCART not plugged in - - // So, as you can have RGB and composite out simultaneously, - // and it can't detect S-Video, what is the point of this? - - int scart; - if (ioctl(fdVideo, AV_CHK_SCART, &scart) != 0) return -10; - - return scart; -} - -int VideoMVP::setFormat(UCHAR tformat) -{ - if (!initted) return 0; - if ((tformat != PAL) && (tformat != NTSC)) return 0; - format = tformat; - - if (ioctl(fdVideo, AV_SET_VID_DISP_FMT, format) != 0) return 0; - - if (format == NTSC) - { - screenWidth = 720; - screenHeight = 480; - } - if (format == PAL) - { - screenWidth = 720; - screenHeight = 576; - } - - return 1; -} - -int VideoMVP::setConnection(UCHAR tconnection) -{ - if (!initted) return 0; - if ((tconnection != COMPOSITERGB) && (tconnection != SVIDEO)) return 0; - connection = tconnection; - - if (ioctl(fdVideo, AV_SET_VID_OUTPUT, connection) != 0) return 0; - return 1; -} - -int VideoMVP::setAspectRatio(UCHAR taspectRatio) -{ - if (!initted) return 0; - if ((taspectRatio != ASPECT4X3) && (taspectRatio != ASPECT16X9)) return 0; - aspectRatio = taspectRatio; - - Log::getInstance()->log("Video", Log::DEBUG, "Setting aspect to %i", aspectRatio); - - if (ioctl(fdVideo, AV_SET_VID_RATIO, aspectRatio) != 0) return 0; - return 1; -} - -int VideoMVP::setMode(UCHAR tmode) -{ - if (!initted) return 0; - - if ((tmode == LETTERBOX) && (tvsize == ASPECT16X9)) return 0; // invalid mode - - if ((tmode != NORMAL) && (tmode != LETTERBOX) && (tmode != UNKNOWN2) && (tmode != QUARTER) && (tmode != EIGHTH) - && (tmode != ZOOM) && (tmode != UNKNOWN6)) return 0; - mode = tmode; - - if (ioctl(fdVideo, AV_SET_VID_MODE, mode) != 0) return 0; - return 1; -} - -int VideoMVP::signalOff() -{ - if (ioctl(fdVideo, AV_SET_VID_DENC, 0) != 0) return 0; - return 1; -} - -int VideoMVP::signalOn() -{ - if (ioctl(fdVideo, AV_SET_VID_DENC, 1) != 0) return 0; - return 1; -} - -int VideoMVP::setSource() -{ - if (!initted) return 0; - - // What does this do... - if (ioctl(fdVideo, AV_SET_VID_SRC, 1) != 0) return 0; - return 1; -} - -int VideoMVP::setPosition(int x, int y) -{ - if (!initted) return 0; - -// vid_pos_regs_t pos_d; -// pos_d.x = x; -// pos_d.y = y; - - vid_pos_regs_t pos_d; - - memset(&pos_d, 0, sizeof(pos_d)); - - pos_d.dest.y = y; - pos_d.dest.x = x; -/* -typedef struct { - int w; - int h; - int scale; - int x1; - int y; - int x; - int y2; - int x3; - int y3; - int x4; - int y4; -} vid_pos_regs_t; -*/ - -/* - pos_d.w = 100; - pos_d.h = 30; - pos_d.scale = 2; - pos_d.x1 = 0; - pos_d.y = 100; // Top left X - pos_d.x = 50; // Top left Y - pos_d.y2 = 30; - pos_d.x3 = 60; - pos_d.y3 = 90; - pos_d.x4 = 120; - pos_d.y4 = 150; -*/ - - if (ioctl(fdVideo, AV_SET_VID_POSITION, &pos_d) != 0) return 0; - return 1; -} - -int VideoMVP::sync() -{ - if (!initted) return 0; - - if (ioctl(fdVideo, AV_SET_VID_SYNC, 2) != 0) return 0; - return 1; -} - -int VideoMVP::play() -{ - if (!initted) return 0; - - if (ioctl(fdVideo, AV_SET_VID_PLAY, 0) != 0) return 0; - return 1; -} - -int VideoMVP::stop() -{ - if (!initted) return 0; - - if (ioctl(fdVideo, AV_SET_VID_STOP, 0) != 0) return 0; - return 1; -} - -int VideoMVP::reset() -{ - if (!initted) return 0; - - if (ioctl(fdVideo, AV_SET_VID_RESET, 0x11) != 0) return 0; - return 1; -} - -int VideoMVP::pause() -{ - if (!initted) return 0; - - if (ioctl(fdVideo, AV_SET_VID_PAUSE, 0) != 0) return 0; - return 1; -} - -int VideoMVP::unPause() // FIXME get rid - same as play!! -{ - if (!initted) return 0; - if (ioctl(fdVideo, AV_SET_VID_PLAY, 0) != 0) return 0; - return 1; -} - -int VideoMVP::fastForward() -{ - if (!initted) return 0; - - if (ioctl(fdVideo, AV_SET_VID_FFWD, 1) != 0) return 0; - return 1; -} - -int VideoMVP::unFastForward() -{ - if (!initted) return 0; - -// if (ioctl(fdVideo, AV_SET_VID_RESET, 0x11) != 0) return 0; // don't need this. - - if (ioctl(fdVideo, AV_SET_VID_PLAY, 0) != 0) return 0; - return 1; -} - -int VideoMVP::attachFrameBuffer() -{ - if (!initted) return 0; - - if (ioctl(fdVideo, AV_SET_VID_FB, 0) != 0) return 0; - return 1; -} - -int VideoMVP::blank(void) -{ - if (ioctl(fdVideo, AV_SET_VID_FB, 1) != 0) return 0; - if (ioctl(fdVideo, AV_SET_VID_FB, 0) != 0) return 0; - return 1; -} - -ULLONG VideoMVP::getCurrentTimestamp() -{ - sync_data_t timestamps; - if (ioctl(fdVideo, AV_GET_VID_TIMESTAMPS, ×tamps) == 0) - { - // FIXME are these the right way around? - - timestamps.stc = (timestamps.stc >> 31 ) | (timestamps.stc & 1); - timestamps.pts = (timestamps.pts >> 31 ) | (timestamps.pts & 1); - - return timestamps.stc; - } - else - { - return 0; - } -} - -ULONG VideoMVP::timecodeToFrameNumber(ULLONG timecode) -{ - if (format == PAL) return (ULONG)(((double)timecode / (double)90000) * (double)25); - else return (ULONG)(((double)timecode / (double)90000) * (double)30); -} - -#ifdef DEV -int VideoMVP::test() -{ - return 0; - -// ULLONG stc = 0; -// return ioctl(fdVideo, AV_SET_VID_STC, &stc); -/* - // reset(); - return 1; -*/ -} - -int VideoMVP::test2() -{ - return 0; -} -#endif - -void VideoMVP::PrepareMediaSample(const MediaPacketList& mplist,UINT samplepos) -{ - MediaPacketList::const_iterator iter = mplist.begin(); - deliver_start = iter->pos_buffer + samplepos; - mediapacket_len[0] = deliver_length = iter->length; - deliver_count = 1; - while (++iter != mplist.end() && - iter->pos_buffer == deliver_start + deliver_length) - { - deliver_length += iter->length; - mediapacket_len[deliver_count] = iter->length; - ++deliver_count; - if (deliver_length >= WRITE_LENGTH || - deliver_count == WRITE_PACKETS) break; - } -} - -UINT VideoMVP::DeliverMediaSample(UCHAR* buffer, UINT* samplepos) -{ - int written = ::write(fdVideo, buffer + deliver_start, deliver_length); - if (written == (int)deliver_length) { *samplepos = 0; return deliver_count;} - if (written <= 0) return 0; - // Handle a partial write. Is this possible? Should we bother? - UINT i = 0; - while ((written -= mediapacket_len[i]) >= 0) i++; - *samplepos = mediapacket_len[i] + written; - return i; -} - -void VideoMVP::ResetTimeOffsets() -{ -} - -bool VideoMVP::displayIFrame(const UCHAR* buffer, UINT length) -{ - write(fdVideo, buffer, length); - return true; -} +/* + Copyright 2004-2005 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "videomvp.h" + +// temp +#include "log.h" + +VideoMVP::VideoMVP() +{ + if (instance) return; +} + +VideoMVP::~VideoMVP() +{ + instance = NULL; +} + +int VideoMVP::init(UCHAR tformat) +{ + if (initted) return 0; + initted = 1; + + if ((fdVideo = open("/dev/vdec_dev", O_WRONLY)) < 0) return 0; + + if (!setFormat(tformat)) { shutdown(); return 0; } + if (!setConnection(COMPOSITERGB)) { shutdown(); return 0; } + if (!setAspectRatio(ASPECT4X3)) { shutdown(); return 0; } + if (!setMode(NORMAL)) { shutdown(); return 0; } + if (!setSource()) { shutdown(); return 0; } + if (!attachFrameBuffer()) { shutdown(); return 0; } + + setTVsize(ASPECT4X3); + + if (format == PAL) setLetterboxBorder("38"); + else setLetterboxBorder("31"); + + stop(); + + + return 1; +} + +void VideoMVP::setLetterboxBorder(char* border) +{ + FILE* fdlbox = fopen("/proc/lbox_border", "w"); + if (!fdlbox) return; + fputs(border, fdlbox); + fclose(fdlbox); +} + +int VideoMVP::setTVsize(UCHAR ttvsize) +{ + tvsize = ttvsize; + + // Override the aspect ratio usage, temporarily use to set the video chip mode + if (!setAspectRatio(tvsize)) { shutdown(); return 0; } + close(fdVideo); + if ((fdVideo = open("/dev/vdec_dev", O_WRONLY)) < 0) return 0; + if (!setSource()) { shutdown(); return 0; } + if (!attachFrameBuffer()) { shutdown(); return 0; } + + // Reopening the fd causes the scart aspect line to go back to 4:3 + // Set this again to the same as the tv screen size + if (!setAspectRatio(tvsize)) { shutdown(); return 0; } + + // mode == LETTERBOX is invalid if the TV is widescreen + if (tvsize == ASPECT16X9) setMode(NORMAL); + + return 1; +} + +int VideoMVP::setDefaultAspect() +{ + return setAspectRatio(tvsize); +} + +int VideoMVP::shutdown() +{ + if (!initted) return 0; + initted = 0; + close(fdVideo); + return 1; +} + +int VideoMVP::checkSCART() +{ + // Returns 3 for SCART Composite out + // Returns 3 for SCART S-Video out + // Returns 2 for SCART RGB out + // Returns 3 for SCART not plugged in + + // So, as you can have RGB and composite out simultaneously, + // and it can't detect S-Video, what is the point of this? + + int scart; + if (ioctl(fdVideo, AV_CHK_SCART, &scart) != 0) return -10; + + return scart; +} + +int VideoMVP::setFormat(UCHAR tformat) +{ + if (!initted) return 0; + if ((tformat != PAL) && (tformat != NTSC)) return 0; + format = tformat; + + if (ioctl(fdVideo, AV_SET_VID_DISP_FMT, format) != 0) return 0; + + if (format == NTSC) + { + screenWidth = 720; + screenHeight = 480; + } + if (format == PAL) + { + screenWidth = 720; + screenHeight = 576; + } + + return 1; +} + +int VideoMVP::setConnection(UCHAR tconnection) +{ + if (!initted) return 0; + if ((tconnection != COMPOSITERGB) && (tconnection != SVIDEO)) return 0; + connection = tconnection; + + if (ioctl(fdVideo, AV_SET_VID_OUTPUT, connection) != 0) return 0; + return 1; +} + +int VideoMVP::setAspectRatio(UCHAR taspectRatio) +{ + if (!initted) return 0; + if ((taspectRatio != ASPECT4X3) && (taspectRatio != ASPECT16X9)) return 0; + aspectRatio = taspectRatio; + + Log::getInstance()->log("Video", Log::DEBUG, "Setting aspect to %i", aspectRatio); + + if (ioctl(fdVideo, AV_SET_VID_RATIO, aspectRatio) != 0) return 0; + return 1; +} + +int VideoMVP::setMode(UCHAR tmode) +{ + if (!initted) return 0; + + if ((tmode == LETTERBOX) && (tvsize == ASPECT16X9)) return 0; // invalid mode + + if ((tmode != NORMAL) && (tmode != LETTERBOX) && (tmode != UNKNOWN2) && (tmode != QUARTER) && (tmode != EIGHTH) + && (tmode != ZOOM) && (tmode != UNKNOWN6)) return 0; + mode = tmode; + + if (ioctl(fdVideo, AV_SET_VID_MODE, mode) != 0) return 0; + return 1; +} + +int VideoMVP::signalOff() +{ + if (ioctl(fdVideo, AV_SET_VID_DENC, 0) != 0) return 0; + return 1; +} + +int VideoMVP::signalOn() +{ + if (ioctl(fdVideo, AV_SET_VID_DENC, 1) != 0) return 0; + return 1; +} + +int VideoMVP::setSource() +{ + if (!initted) return 0; + + // What does this do... + if (ioctl(fdVideo, AV_SET_VID_SRC, 1) != 0) return 0; + return 1; +} + +int VideoMVP::setPosition(int x, int y) +{ + if (!initted) return 0; + +// vid_pos_regs_t pos_d; +// pos_d.x = x; +// pos_d.y = y; + + vid_pos_regs_t pos_d; + + memset(&pos_d, 0, sizeof(pos_d)); + + pos_d.dest.y = y; + pos_d.dest.x = x; +/* +typedef struct { + int w; + int h; + int scale; + int x1; + int y; + int x; + int y2; + int x3; + int y3; + int x4; + int y4; +} vid_pos_regs_t; +*/ + +/* + pos_d.w = 100; + pos_d.h = 30; + pos_d.scale = 2; + pos_d.x1 = 0; + pos_d.y = 100; // Top left X + pos_d.x = 50; // Top left Y + pos_d.y2 = 30; + pos_d.x3 = 60; + pos_d.y3 = 90; + pos_d.x4 = 120; + pos_d.y4 = 150; +*/ + + if (ioctl(fdVideo, AV_SET_VID_POSITION, &pos_d) != 0) return 0; + return 1; +} + +int VideoMVP::sync() +{ + if (!initted) return 0; + + if (ioctl(fdVideo, AV_SET_VID_SYNC, 2) != 0) return 0; + return 1; +} + +int VideoMVP::play() +{ + if (!initted) return 0; + + if (ioctl(fdVideo, AV_SET_VID_PLAY, 0) != 0) return 0; + return 1; +} + +int VideoMVP::stop() +{ + if (!initted) return 0; + + if (ioctl(fdVideo, AV_SET_VID_STOP, 0) != 0) return 0; + return 1; +} + +int VideoMVP::reset() +{ + if (!initted) return 0; + + if (ioctl(fdVideo, AV_SET_VID_RESET, 0x11) != 0) return 0; + return 1; +} + +int VideoMVP::pause() +{ + if (!initted) return 0; + + if (ioctl(fdVideo, AV_SET_VID_PAUSE, 0) != 0) return 0; + return 1; +} + +int VideoMVP::unPause() // FIXME get rid - same as play!! +{ + if (!initted) return 0; + if (ioctl(fdVideo, AV_SET_VID_PLAY, 0) != 0) return 0; + return 1; +} + +int VideoMVP::fastForward() +{ + if (!initted) return 0; + + if (ioctl(fdVideo, AV_SET_VID_FFWD, 1) != 0) return 0; + return 1; +} + +int VideoMVP::unFastForward() +{ + if (!initted) return 0; + +// if (ioctl(fdVideo, AV_SET_VID_RESET, 0x11) != 0) return 0; // don't need this. + + if (ioctl(fdVideo, AV_SET_VID_PLAY, 0) != 0) return 0; + return 1; +} + +int VideoMVP::attachFrameBuffer() +{ + if (!initted) return 0; + + if (ioctl(fdVideo, AV_SET_VID_FB, 0) != 0) return 0; + return 1; +} + +int VideoMVP::blank(void) +{ + if (ioctl(fdVideo, AV_SET_VID_FB, 1) != 0) return 0; + if (ioctl(fdVideo, AV_SET_VID_FB, 0) != 0) return 0; + return 1; +} + +ULLONG VideoMVP::getCurrentTimestamp() +{ + sync_data_t timestamps; + if (ioctl(fdVideo, AV_GET_VID_TIMESTAMPS, ×tamps) == 0) + { + // FIXME are these the right way around? + + timestamps.stc = (timestamps.stc >> 31 ) | (timestamps.stc & 1); + timestamps.pts = (timestamps.pts >> 31 ) | (timestamps.pts & 1); + + return timestamps.stc; + } + else + { + return 0; + } +} + +ULONG VideoMVP::timecodeToFrameNumber(ULLONG timecode) +{ + if (format == PAL) return (ULONG)(((double)timecode / (double)90000) * (double)25); + else return (ULONG)(((double)timecode / (double)90000) * (double)30); +} + +#ifdef DEV +int VideoMVP::test() +{ + return 0; + +// ULLONG stc = 0; +// return ioctl(fdVideo, AV_SET_VID_STC, &stc); +/* + // reset(); + return 1; +*/ +} + +int VideoMVP::test2() +{ + return 0; +} +#endif + +void VideoMVP::PrepareMediaSample(const MediaPacketList& mplist,UINT samplepos) +{ + MediaPacketList::const_iterator iter = mplist.begin(); + deliver_start = iter->pos_buffer + samplepos; + mediapacket_len[0] = deliver_length = iter->length; + deliver_count = 1; + while (++iter != mplist.end() && + iter->pos_buffer == deliver_start + deliver_length) + { + deliver_length += iter->length; + mediapacket_len[deliver_count] = iter->length; + ++deliver_count; + if (deliver_length >= WRITE_LENGTH || + deliver_count == WRITE_PACKETS) break; + } +} + +UINT VideoMVP::DeliverMediaSample(UCHAR* buffer, UINT* samplepos) +{ + int written = ::write(fdVideo, buffer + deliver_start, deliver_length); + if (written == (int)deliver_length) { *samplepos = 0; return deliver_count;} + if (written <= 0) return 0; + // Handle a partial write. Is this possible? Should we bother? + UINT i = 0; + while ((written -= mediapacket_len[i]) >= 0) i++; + *samplepos = mediapacket_len[i] + written; + return i; +} + +void VideoMVP::ResetTimeOffsets() +{ +} + +bool VideoMVP::displayIFrame(const UCHAR* buffer, UINT length) +{ + write(fdVideo, buffer, length); + return true; +} diff --git a/videomvp.h b/videomvp.h index aabad63..c1b07c8 100644 --- a/videomvp.h +++ b/videomvp.h @@ -1,159 +1,159 @@ -/* - Copyright 2004-2005 Chris Tallon - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -// Thanks to Jon Gettler and BtB of the MVPMC project for all the hardware information - - -#ifndef VIDEOMVP_H -#define VIDEOMVP_H - -// FIXME - check why so many things include unistd - -#include -#include -#include -#include -#include - -#include - -#include "defines.h" -#include "video.h" - -typedef struct -{ - int nleft; - int state; -} vid_state_regs_t; - -typedef struct -{ - uint64_t stc; - uint64_t pts; -} sync_data_t; - -typedef struct -{ - int y; - int x; - int w; - int h; -} vid_rect_t; - -typedef struct -{ - vid_rect_t src; - vid_rect_t dest; -} vid_pos_regs_t; - -struct vid_regs -{ - UCHAR dummy[44]; -}; - -#define AV_SET_VID_STOP _IOW('v', 21, int) -#define AV_SET_VID_PLAY _IOW('v', 22, int) -#define AV_SET_VID_FREEZE _IOW('v', 23, int) -#define AV_SET_VID_RESUME _IOW('v', 24, int) -#define AV_SET_VID_SRC _IOW('v', 25, int) -#define AV_SET_VID_FB _IOW('v', 26, int) -#define AV_GET_VID_STATE _IOR('v', 27, vid_state_regs_t*) -#define AV_SET_VID_PAUSE _IOW('v', 28, int) -#define AV_SET_VID_FFWD _IOW('v', 29, int) -#define AV_SET_VID_SLOMO _IOW('v', 30, int) -#define AV_SET_VID_BLANK _IOW('v', 32, int) -#define AV_SET_VID_POSITION _IOW('v', 36, vid_pos_regs_t*) -#define AV_SET_VID_SCALE_ON _IOW('v', 37, int) -#define AV_SET_VID_SCALE_OFF _IOW('v', 38, int) -#define AV_GET_VID_TIMESTAMPS _IOR('v', 39, sync_data_t*) -#define AV_SET_VID_STC _IOW('v', 40, uint64_t*) -#define AV_SET_VID_RATIO _IOW('v', 41, int) -#define AV_SET_VID_SYNC _IOW('v', 42, int) -#define AV_SET_VID_DISABLE_SYNC _IOW('v', 43, int) -#define AV_SET_VID_DISP_FMT _IOW('v', 45, int) -#define AV_SET_VID_RESET _IOW('v', 51, int) -#define AV_SET_VID_OUTPUT _IOW('v', 57, int) -#define AV_SET_VID_MODE _IOW('v', 58, int) -#define AV_GET_VID_REGS _IOR('v', 61, struct vid_regs*) -#define AV_SET_VID_COLORBAR _IOW('v', 62, int) -#define AV_SET_VID_DENC _IOW('v', 63, int) -#define AV_CHK_SCART _IOW('v', 64, int) - -#define WRITE_LENGTH (32*1024) // Consume up to 32K at a time from Stream -#define WRITE_PACKETS 16 // But no more than 16 packets - -class VideoMVP : public Video -{ - public: - VideoMVP(); - 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 +/* + Copyright 2004-2005 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +// Thanks to Jon Gettler and BtB of the MVPMC project for all the hardware information + + +#ifndef VIDEOMVP_H +#define VIDEOMVP_H + +// FIXME - check why so many things include unistd + +#include +#include +#include +#include +#include + +#include + +#include "defines.h" +#include "video.h" + +typedef struct +{ + int nleft; + int state; +} vid_state_regs_t; + +typedef struct +{ + uint64_t stc; + uint64_t pts; +} sync_data_t; + +typedef struct +{ + int y; + int x; + int w; + int h; +} vid_rect_t; + +typedef struct +{ + vid_rect_t src; + vid_rect_t dest; +} vid_pos_regs_t; + +struct vid_regs +{ + UCHAR dummy[44]; +}; + +#define AV_SET_VID_STOP _IOW('v', 21, int) +#define AV_SET_VID_PLAY _IOW('v', 22, int) +#define AV_SET_VID_FREEZE _IOW('v', 23, int) +#define AV_SET_VID_RESUME _IOW('v', 24, int) +#define AV_SET_VID_SRC _IOW('v', 25, int) +#define AV_SET_VID_FB _IOW('v', 26, int) +#define AV_GET_VID_STATE _IOR('v', 27, vid_state_regs_t*) +#define AV_SET_VID_PAUSE _IOW('v', 28, int) +#define AV_SET_VID_FFWD _IOW('v', 29, int) +#define AV_SET_VID_SLOMO _IOW('v', 30, int) +#define AV_SET_VID_BLANK _IOW('v', 32, int) +#define AV_SET_VID_POSITION _IOW('v', 36, vid_pos_regs_t*) +#define AV_SET_VID_SCALE_ON _IOW('v', 37, int) +#define AV_SET_VID_SCALE_OFF _IOW('v', 38, int) +#define AV_GET_VID_TIMESTAMPS _IOR('v', 39, sync_data_t*) +#define AV_SET_VID_STC _IOW('v', 40, uint64_t*) +#define AV_SET_VID_RATIO _IOW('v', 41, int) +#define AV_SET_VID_SYNC _IOW('v', 42, int) +#define AV_SET_VID_DISABLE_SYNC _IOW('v', 43, int) +#define AV_SET_VID_DISP_FMT _IOW('v', 45, int) +#define AV_SET_VID_RESET _IOW('v', 51, int) +#define AV_SET_VID_OUTPUT _IOW('v', 57, int) +#define AV_SET_VID_MODE _IOW('v', 58, int) +#define AV_GET_VID_REGS _IOR('v', 61, struct vid_regs*) +#define AV_SET_VID_COLORBAR _IOW('v', 62, int) +#define AV_SET_VID_DENC _IOW('v', 63, int) +#define AV_CHK_SCART _IOW('v', 64, int) + +#define WRITE_LENGTH (32*1024) // Consume up to 32K at a time from Stream +#define WRITE_PACKETS 16 // But no more than 16 packets + +class VideoMVP : public Video +{ + public: + VideoMVP(); + 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 diff --git a/videoomx.h b/videoomx.h index 3371aad..ea3c247 100644 --- a/videoomx.h +++ b/videoomx.h @@ -1,290 +1,290 @@ -/* - 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 -#include -#include -#include -#include - -#include - -#include -#include - -#include "defines.h" -#include "video.h" -#include "threadsystem.h" - -#define OMX_SKIP64BIT - -#include -#include -#include -#include - - -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; +/* + 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 +#include +#include +#include +#include + +#include + +#include +#include + +#include "defines.h" +#include "video.h" +#include "threadsystem.h" + +#define OMX_SKIP64BIT + +#include +#include +#include +#include + + +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; - 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 input_bufs_omx_all; - list 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 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 mediapackets; -}; - -#endif + 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 input_bufs_omx_all; + list 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 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 mediapackets; +}; + +#endif diff --git a/videowin.cc b/videowin.cc index 8979151..425ff00 100644 --- a/videowin.cc +++ b/videowin.cc @@ -1,2066 +1,2066 @@ -/* - Copyright 2004-2005 Chris Tallon - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - - - -#include "videowin.h" -#include "log.h" -#include "dssourcefilter.h" -#include "dsallocator.h" -#include "vdr.h" -#include "osdwin.h" -#include "audiowin.h" -#include "wwinvideofilter.h" -#include "wwinvideoh264filter.h" -#include "wtabbar.h" -#include "woptionpane.h" -#include "i18n.h" -#include "demuxer.h" - -#include -#include - -void AdjustWindow(); - - - -VideoWin::VideoWin() -{ - dsinited=false; - dsgraphbuilder=NULL; - dsmediacontrol=NULL; - dsrenderer=NULL; - dsrefclock=NULL; - dsmediafilter=NULL; - dsbasicaudio=NULL; - sourcefilter=NULL; - allocatorvmr=NULL; - cr_time=0; - lastaudiomode=MPTYPE_MPEG_AUDIO; - //lastaudiomode=MPTYPE_AC3; - dsvmrsurfnotify=NULL; - filtermutex=CreateMutex(NULL,FALSE,NULL); - offsetnotset=true; - offsetvideonotset=true; - offsetaudionotset=true; - startoffset=0; - lastrefaudiotime=0; - lastrefvideotime=0; - lastreftimeRT=0; - lastreftimePTS=0; - firstsynched=false; - cur_audio_media_sample=NULL; - cur_video_media_sample=NULL; - videoon=true; - audioon=true; - audiovolume=0; - pseudotvsize=0; - videoposx=0; - videoposy=0; - aud_type=Audio::MPEG2_PES; - iframemode=false;//We are not in Iframe mode at begining - vmrdeinterlacing=2;//Best - videofilterselected=-1; - videoH264filterselected=-1; - OSVERSIONINFO verinfo; - verinfo.dwOSVersionInfoSize=sizeof(verinfo); - GetVersionEx(&verinfo); - - if (verinfo.dwMajorVersion>=6) { - currentpresenter=EVR; - } else { - currentpresenter=VMR9; - } - videoH264dtsfix=false; - videompeg2dtsfix=false; - - - - -} - -VideoWin::~VideoWin() -{ - CleanupDS(); - CloseHandle(filtermutex); - unsigned int i; - for (i=0;ilog("VideoWin", Log::ERR , "Unable to create FilterMapper!"); - return; - } - /* Wishlist, what Mediatypes do we want */ - GUID mtypesin[]={MEDIATYPE_Video,MEDIASUBTYPE_MPEG2_VIDEO}; - IEnumMoniker *myenum; - result = filtmap->EnumMatchingFilters(&myenum,0,TRUE,MERIT_DO_NOT_USE+1, - TRUE,1,mtypesin,NULL,NULL,FALSE,TRUE,0,NULL,NULL,NULL); - if (result != S_OK) - { - filtmap->Release(); - Log::getInstance()->log("VideoWin", Log::ERR , "Unable to enum Filters!"); - return; - } - ULONG gethowmany; - IMoniker * moni; - while(myenum->Next(1,&moni,&gethowmany)==S_OK) - { - VideoFilterDesc desc; - ZeroMemory(&desc,sizeof(desc)); - - LPOLESTR string; - moni->GetDisplayName(0,0,&string); - desc.displayname=new char[wcslen(string)+1]; - wcstombs(desc.displayname,string,wcslen(string)+1); - CoTaskMemFree(string); - IPropertyBag *bag; - if (moni->BindToStorage(0,0,IID_IPropertyBag,(void**)&bag) == S_OK) - { - VARIANT vari; - VariantInit(&vari); - result = bag->Read(L"FriendlyName",&vari,NULL); - if (result == S_OK) - { - desc.friendlyname=new char[wcslen(vari.bstrVal)+1]; - wcstombs(desc.friendlyname,vari.bstrVal,wcslen(vari.bstrVal)+1); - } - VariantClear(&vari); - bag->Release(); - - } - - - videofilterlist.push_back(desc); - - - - moni->Release(); - // bctx->Release(); - } - int i; - videofilterselected=-1; - - - - myenum->Release(); - - - - filtmap->Release(); -} - -void VideoWin::initH264FilterDatabase() -{ - /* This method should determine all availiable DirectShow Filters */ - IFilterMapper2* filtmap=NULL; - HRESULT result; - result = CoCreateInstance(CLSID_FilterMapper2,NULL,CLSCTX_INPROC, - IID_IFilterMapper2,(void**)&filtmap); - if (result != S_OK) - { - Log::getInstance()->log("VideoWin", Log::ERR , "Unable to create FilterMapper!"); - return; - } - /* Wishlist, what Mediatypes do we want */ - GUID mtypesin[]={MEDIATYPE_Video,MEDIASUBTYPE_H264}; - IEnumMoniker *myenum; - result = filtmap->EnumMatchingFilters(&myenum,0,TRUE,MERIT_DO_NOT_USE+1, - TRUE,1,mtypesin,NULL,NULL,FALSE,TRUE,0,NULL,NULL,NULL); - if (result != S_OK) - { - filtmap->Release(); - Log::getInstance()->log("VideoWin", Log::ERR , "Unable to enum Filters!"); - return; - } - ULONG gethowmany; - IMoniker * moni; - while(myenum->Next(1,&moni,&gethowmany)==S_OK) - { - VideoFilterDesc desc; - ZeroMemory(&desc,sizeof(desc)); - - LPOLESTR string; - moni->GetDisplayName(0,0,&string); - desc.displayname=new char[wcslen(string)+1]; - wcstombs(desc.displayname,string,wcslen(string)+1); - CoTaskMemFree(string); - IPropertyBag *bag; - if (moni->BindToStorage(0,0,IID_IPropertyBag,(void**)&bag) == S_OK) - { - VARIANT vari; - VariantInit(&vari); - result = bag->Read(L"FriendlyName",&vari,NULL); - if (result == S_OK) - { - desc.friendlyname=new char[wcslen(vari.bstrVal)+1]; - wcstombs(desc.friendlyname,vari.bstrVal,wcslen(vari.bstrVal)+1); - } - VariantClear(&vari); - bag->Release(); - - } - - - videoH264filterlist.push_back(desc); - - - - moni->Release(); - // bctx->Release(); - } - int i; - videoH264filterselected=-1; - - - - myenum->Release(); - - - - filtmap->Release(); -} - -bool VideoWin::loadOptionsfromServer(VDR* vdr) -{ - char *name=vdr->configLoad("DirectShow","VideoFilter"); - - if (name != NULL) - { - for (int i = 0;i configLoad("DirectShow","VideoH264Filter"); - - if (name != NULL) - { - for (int i = 0;i configLoad("DirectShow","VMR9DeinterlacingMode"); - if (name != NULL) - { - if (STRCASECMP(name,"NoMix")==0) { - vmrdeinterlacing=0; - } else if (STRCASECMP(name,"None")==0) { - vmrdeinterlacing=1; - } else if (STRCASECMP(name,"Best")==0) { - vmrdeinterlacing=2; - } else if (STRCASECMP(name,"Bob")==0) { - vmrdeinterlacing=3; - } else if (STRCASECMP(name,"Weave")==0) { - vmrdeinterlacing=4; - } - } - - name=vdr->configLoad("DirectShow", "VideoPresenter"); - if (name!=NULL) { - if (STRCASECMP(name,"VMR9")==0) { - currentpresenter=VMR9; - } else if (STRCASECMP(name,"EVR")==0) { - currentpresenter=EVR; - } - } - if (!((OsdWin*)Osd::getInstance())->IsEvrSupported()) { - currentpresenter=VMR9; - } - - name=vdr->configLoad("DirectShow","videoH264dtsfix"); - if (name!=NULL) { - if (STRCASECMP(name,"YES")==0) { - videoH264dtsfix=true; - } else { - videoH264dtsfix=false; - } - } - name=vdr->configLoad("DirectShow","videompeg2dtsfix"); - if (name!=NULL) { - if (STRCASECMP(name,"YES")==0) { - videompeg2dtsfix=true; - } else { - videompeg2dtsfix=false; - } - } - - name=vdr->configLoad("DirectGraphics", "StretchFiltering"); - if (name!=NULL) { - if (STRCASECMP(name,"None")==0) { - ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_NONE); - } else if (STRCASECMP(name,"Point")==0) { - ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_POINT); - } else if (STRCASECMP(name,"Linear")==0) { - ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_LINEAR); - } - } - - - - - return true; - -} - -bool VideoWin::handleOptionChanges(Option* option) -{ - if( Video::handleOptionChanges(option)) return true; - switch(option->id) { - case 1: { - if (STRCASECMP(option->options[option->userSetChoice],"None")==0) { - ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_NONE); - } else if (STRCASECMP(option->options[option->userSetChoice],"Point")==0) { - ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_POINT); - } else if (STRCASECMP(option->options[option->userSetChoice],"Linear")==0) { - ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_LINEAR); - } - return true; - } break; - case 2: { - if (STRCASECMP(option->options[option->userSetChoice],"NoMix")==0) { - vmrdeinterlacing=0; - } else if (STRCASECMP(option->options[option->userSetChoice],"None")==0) { - vmrdeinterlacing=1; - } else if (STRCASECMP(option->options[option->userSetChoice],"Best")==0) { - vmrdeinterlacing=2; - } else if (STRCASECMP(option->options[option->userSetChoice],"Bob")==0) { - vmrdeinterlacing=3; - } else if (STRCASECMP(option->options[option->userSetChoice],"Weave")==0) { - vmrdeinterlacing=4; - } - }break; - case 3: { - if (STRCASECMP(option->options[option->userSetChoice],"VMR9")==0) { - currentpresenter=VMR9; - } else if (STRCASECMP(option->options[option->userSetChoice],"EVR")==0) { - currentpresenter=EVR; - } - }break; - case 4: { - if (STRCASECMP(option->options[option->userSetChoice],"Yes")==0) { - videoH264dtsfix=true; - } else { - videoH264dtsfix=false; - } - }break; - case 5: { - if (STRCASECMP(option->options[option->userSetChoice],"Yes")==0) { - videompeg2dtsfix=true; - } else { - videompeg2dtsfix=false; - } - }break; - }; - return false; - -} - -bool VideoWin::saveOptionstoServer() -{ - if (videofilterselected!=-1) { - VDR::getInstance()->configSave("DirectShow", - "VideoFilter",videofilterlist[videofilterselected].displayname); - VDR::getInstance()->configSave("DirectShow", - "VideoH264Filter",videoH264filterlist[videoH264filterselected].displayname); - } - return true; -} - -/*Option(UINT id, const char* displayText, const char* configSection, const char* configKey, UINT optionType, - UINT numChoices, UINT defaultChoice, UINT startInt, - const char * const * options, const char * const * optionkeys = NULL, AbstractOption* handler=NULL);*/ - -bool VideoWin::addOptionsToPanes(int panenumber,Options *options,WOptionPane* pane) -{ - if (!Video::addOptionsToPanes(panenumber,options,pane)) return false; - - - Option* option; - if (panenumber == 2) - { - DWORD scalingcaps=((OsdWin*)Osd::getInstance())->getFilterCaps(); - char **scalingopts=new char *[3]; - int i=0; - scalingopts[i]=new char[strlen("None")+1]; - strcpy(scalingopts[i],"None"); - i++; - if ((scalingcaps & D3DPTFILTERCAPS_MINFPOINT)!=0 - && (scalingcaps & D3DPTFILTERCAPS_MAGFPOINT)!=0) { - scalingopts[i]=new char[strlen("Point")+1]; - strcpy(scalingopts[i],"Point"); - i++; - } - if ((scalingcaps & D3DPTFILTERCAPS_MINFLINEAR)!=0 - && (scalingcaps & D3DPTFILTERCAPS_MAGFLINEAR)!=0) { - scalingopts[i]=new char[strlen("Linear")+1]; - strcpy(scalingopts[i],"Linear"); - i++; - } - option = new Option(1 ,tr("Video Stretching Filter"), "DirectGraphics", "StretchFiltering", Option::TYPE_TEXT, i, (i-1), 0, scalingopts,NULL,true, this); - options->push_back(option); - pane->addOptionLine(option); - static const char* deintopts[]={"NoMix","None","Best","Bob","Weave"}; - option = new Option(2,tr("VMR9 Deinterlacing Mode"), "DirectShow","VMR9DeinterlacingMode",Option::TYPE_TEXT,5,2,0,deintopts,NULL,false,this); - options->push_back(option); - pane->addOptionLine(option); - - if (((OsdWin*)Osd::getInstance())->IsEvrSupported()) - { - static const char* presenteropts[]={"EVR","VMR9"}; - option = new Option(3,tr("Video Presenter Filter"),"DirectShow", "VideoPresenter",Option::TYPE_TEXT,2, - (currentpresenter==EVR)?0:1,0,presenteropts,NULL,false,this); - } else { - static const char* presenteropts[]={"VMR9"}; - option = new Option(3,tr("Video Presenter Filter"),"DirectShow", "VideoPresenter",Option::TYPE_TEXT,1,0,0,presenteropts,NULL,false,this); - } - options->push_back(option); - pane->addOptionLine(option); - - static const char* yesnoopts[]={"Yes","No"}; - option = new Option(4,tr("Video H264 fix dts time"), "DirectShow","videoH264dtsfix",Option::TYPE_TEXT,2,1,0,yesnoopts,NULL,false,this); - options->push_back(option); - pane->addOptionLine(option); - - option = new Option(5,tr("Video Mpeg2 fix dts time"), "DirectShow","videompeg2dtsfix",Option::TYPE_TEXT,2,1,0,yesnoopts,NULL,false,this); - options->push_back(option); - pane->addOptionLine(option); - - - - } - - return true; -} - -IBaseFilter *VideoWin::getVideoFilter() -{ - IBaseFilter *curfilter= NULL; - if (videofilterselected == -1) - { - int i; - for (i = 0;i BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) - { - IAMDecoderCaps* desccaps=NULL; - if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK) - { - DWORD caps; - desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps); - if (caps == DECODER_CAP_SUPPORTED) - { - videofilterlist[i].vmr9tested = true; - videofilterlist[i].vmr9 = true; - videofilterselected = i; - } - else - { - videofilterlist[i].vmr9tested = true; - videofilterlist[i].vmr9 = false; - - curfilter->Release(); - curfilter=NULL; - } - } - desccaps->Release(); - } - moni->Release(); - } - delete [] name; - bindctx->Release(); - } - if (videofilterlist[i].vmr9) break; - - } - if (curfilter != NULL) - { - VDR *vdr=VDR::getInstance(); - if (vdr != NULL) - { - vdr->configSave("DirectShow","VideoFilter", - videofilterlist[videofilterselected].displayname); - } - return curfilter; - } - } - else - { - IBindCtx *bindctx=NULL; - if (CreateBindCtx(0,&bindctx)!=S_OK) return NULL; - IMoniker * moni=NULL; - LPCOLESTR name=new WCHAR[strlen(videofilterlist[videofilterselected].displayname)+1]; - mbstowcs((wchar_t*)name,videofilterlist[videofilterselected].displayname, - strlen(videofilterlist[videofilterselected].displayname)+1); - ULONG eater; - if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK) - { - if (moni->BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) - { - IAMDecoderCaps* desccaps=NULL; - if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK) - { - DWORD caps; - desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps); - if (caps == DECODER_CAP_SUPPORTED) - { - videofilterlist[videofilterselected].vmr9tested = true; - videofilterlist[videofilterselected].vmr9 = true; - } - else - { - videofilterlist[videofilterselected].vmr9tested = true; - videofilterlist[videofilterselected].vmr9 = false; - Log::getInstance()->log("VideoWin", Log::WARN ,"Filter does not support VMR9, but is selected, manual selection!"); - } - } - moni->Release(); - delete [] name; - bindctx->Release(); - return curfilter; - } - moni->Release(); - } - bindctx->Release(); - delete [] name; - return NULL; - } - return NULL; - -} - -IBaseFilter *VideoWin::getVideoH264Filter() -{ - IBaseFilter *curfilter= NULL; - if (videoH264filterselected == -1) - { - int i; - for (i = 0;i log("VideoWin", Log::DEBUG ,"Creating filter: %s",videoH264filterlist[i].friendlyname); - - - - if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK) - { - if (moni->BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) - { - IAMDecoderCaps* desccaps=NULL; - if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK) - { - DWORD caps; - desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps); - if (caps == DECODER_CAP_SUPPORTED) - { - videoH264filterlist[i].vmr9tested = true; - videoH264filterlist[i].vmr9 = true; - videoH264filterselected = i; - } - else - { - videoH264filterlist[i].vmr9tested = true; - videoH264filterlist[i].vmr9 = false; - - curfilter->Release(); - curfilter=NULL; - } - } - desccaps->Release(); - } - moni->Release(); - } - delete [] name; - bindctx->Release(); - } - if (videoH264filterlist[i].vmr9) break; - - } - if (curfilter != NULL) - { - VDR *vdr=VDR::getInstance(); - if (vdr != NULL) - { - vdr->configSave("DirectShow","VideoH264Filter", - videoH264filterlist[videoH264filterselected].displayname); - } - return curfilter; - } - } - else - { - IBindCtx *bindctx=NULL; - if (CreateBindCtx(0,&bindctx)!=S_OK) return NULL; - IMoniker * moni=NULL; - LPCOLESTR name=new WCHAR[strlen(videoH264filterlist[videoH264filterselected].displayname)+1]; - mbstowcs((wchar_t*)name,videoH264filterlist[videoH264filterselected].displayname, - strlen(videoH264filterlist[videoH264filterselected].displayname)+1); - ULONG eater; - Log::getInstance()->log("VideoWin", Log::DEBUG ,"Creating filter: %s",videoH264filterlist[videoH264filterselected].friendlyname); - if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK) - { - if (moni->BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) - { - IAMDecoderCaps* desccaps=NULL; - if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK) - { - DWORD caps; - desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps); - if (caps == DECODER_CAP_SUPPORTED) - { - videoH264filterlist[videoH264filterselected].vmr9tested = true; - videoH264filterlist[videoH264filterselected].vmr9 = true; - } - else - { - videoH264filterlist[videoH264filterselected].vmr9tested = true; - videoH264filterlist[videoH264filterselected].vmr9 = false; - Log::getInstance()->log("VideoWin", Log::WARN ,"Filter does not support VMR9, but is selected, manual selection!"); - } - } - moni->Release(); - delete [] name; - bindctx->Release(); - return curfilter; - } - moni->Release(); - } - bindctx->Release(); - delete [] name; - return NULL; - } - return NULL; - -} - - -#ifdef DS_DEBUG // This stuff would not included in vomp due to lincemse restrcitions -#include "dshelper.h" -#endif - -#define DO_VIDEO - -int VideoWin::play() -{ - if (!initted) return 0; - return 1; -} - -bool VideoWin::addOptionPagesToWTB(WTabBar *wtb) -{ - Boxx *box=new WWinVideoFilter(); - wtb->addTab(tr("Video Filter"), box); - box=new WWinVideoH264Filter(); - wtb->addTab(tr("H264 Filter"), box); - return true; -} - -const VideoFilterDescList *VideoWin::getVideoFilterList(int &selected) -{ - selected=videofilterselected; - return &videofilterlist; -} - -const VideoFilterDescList *VideoWin::getVideoH264FilterList(int &selected) -{ - selected=videoH264filterselected; - return &videoH264filterlist; -} - -bool VideoWin::selectVideoFilter(int filter) -{ - IBindCtx *bindctx=NULL; - if (CreateBindCtx(0,&bindctx)!=S_OK) return NULL; - IMoniker * moni=NULL; - LPCOLESTR name=new WCHAR[strlen(videofilterlist[filter].displayname)+1]; - mbstowcs((wchar_t*)name,videofilterlist[filter].displayname, - strlen(videofilterlist[filter].displayname)+1); - ULONG eater; - bool success=false; - if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK) - { - IBaseFilter* curfilter=NULL; - if (moni->BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) - { - IAMDecoderCaps* desccaps=NULL; - if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK) - { - DWORD caps; - HRESULT hres=desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps); - if (caps == DECODER_CAP_SUPPORTED) - { - videofilterlist[filter].vmr9tested = true; - videofilterlist[filter].vmr9 = true; - success=true; - } - else - { - videofilterlist[filter].vmr9tested = true; - videofilterlist[filter].vmr9 = false; - success=false; - } - desccaps->Release(); - } else { - videofilterlist[filter].vmr9tested = true; - videofilterlist[filter].vmr9 = false; - success=false; - } - - curfilter->Release(); - - } - moni->Release(); - } - bindctx->Release(); - delete [] name; - if (success || true) - { - videofilterselected=filter; - return true; - } - else - { - return false; - } -} - -bool VideoWin::selectVideoH264Filter(int filter) -{ - IBindCtx *bindctx=NULL; - if (CreateBindCtx(0,&bindctx)!=S_OK) return NULL; - IMoniker * moni=NULL; - LPCOLESTR name=new WCHAR[strlen(videoH264filterlist[filter].displayname)+1]; - mbstowcs((wchar_t*)name,videoH264filterlist[filter].displayname, - strlen(videoH264filterlist[filter].displayname)+1); - ULONG eater; - bool success=false; - if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK) - { - IBaseFilter* curfilter=NULL; - if (moni->BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) - { - IAMDecoderCaps* desccaps=NULL; - if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK) - { - DWORD caps; - HRESULT hres=desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps); - if (caps == DECODER_CAP_SUPPORTED) - { - videoH264filterlist[filter].vmr9tested = true; - videoH264filterlist[filter].vmr9 = true; - success=true; - } - else - { - videoH264filterlist[filter].vmr9tested = true; - videoH264filterlist[filter].vmr9 = false; - success=false; - } - desccaps->Release(); - } else { - videoH264filterlist[filter].vmr9tested = true; - videoH264filterlist[filter].vmr9 = false; - success=false; - } - - curfilter->Release(); - - } - moni->Release(); - } - bindctx->Release(); - delete [] name; - if (success || true) - { - videoH264filterselected=filter; - return true; - } - else - { - return false; - } -} - -int VideoWin::dsInitVideoFilter() -{ - #ifdef DO_VIDEO - HRESULT hres; - if (videoon) { - //We alloc the vmr9 as next step - if (currentpresenter==VMR9) - { - Log::getInstance()->log("VideoWin", Log::INFO ,"VMR9 Videopresenter selected!"); - if (hres=CoCreateInstance(CLSID_VideoMixingRenderer9,0, - CLSCTX_INPROC_SERVER,IID_IBaseFilter,(void**) &dsrenderer)!=S_OK) - { - Log::getInstance()->log("VideoWin", Log::WARN ,"Failed creating VMR9 renderer!"); - ReleaseMutex(filtermutex); - CleanupDS(); - } - /*VMR 9 stuff**/ - if (hres=dsgraphbuilder->AddFilter(dsrenderer,L"VMR9")!=S_OK) - { - ReleaseMutex(filtermutex); - CleanupDS(); - Log::getInstance()->log("VideoWin", Log::WARN ,"Failed adding VMR9 renderer!"); - return 0; - } - IVMRFilterConfig9* vmrfilconfig; - if (dsrenderer->QueryInterface(IID_IVMRFilterConfig9,(void**)&vmrfilconfig)!=S_OK) - { - ReleaseMutex(filtermutex); - CleanupDS(); - Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting VMR9 Filterconfig interface!"); - return 0; - } - if (vmrdeinterlacing!=0) vmrfilconfig->SetNumberOfStreams(1);//Enter Mixing Mode - vmrfilconfig->SetRenderingMode(VMR9Mode_Renderless); - vmrfilconfig->Release(); - if (dsrenderer->QueryInterface(IID_IVMRSurfaceAllocatorNotify9, - (void**)& dsvmrsurfnotify) != S_OK) - { - ReleaseMutex(filtermutex); - CleanupDS(); - Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting VMR9 Surface Allocator interface!"); - return 0; - } - allocatorvmr=new DsAllocator(); - dsvmrsurfnotify->AdviseSurfaceAllocator(NULL,allocatorvmr); - allocatorvmr->AdviseNotify(dsvmrsurfnotify); - - IVMRDeinterlaceControl9* deintctrl; - if (dsrenderer->QueryInterface(IID_IVMRDeinterlaceControl9,(void**)&deintctrl)!=S_OK) - { - ReleaseMutex(filtermutex); - CleanupDS(); - Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting VMR9 Deinterlace control!"); - return 0; - } - /*turnoff*/ - switch (vmrdeinterlacing) - { - case 1: //No Deinterlasing - deintctrl->SetDeinterlaceMode(0xFFFFFFFF,(LPGUID)&GUID_NULL);//Turn Off - break; - case 2: //Best - deintctrl->SetDeinterlacePrefs(DeinterlacePref_NextBest);//Choose Next Best - break; - case 3: //Bob - deintctrl->SetDeinterlacePrefs(DeinterlacePref_BOB);//Choose NBob - break; - case 4: //Weave - deintctrl->SetDeinterlacePrefs(DeinterlacePref_Weave);//Choose Weave - break; - }; - deintctrl->Release(); - /*VMR 9 stuff end */ - } - else if (currentpresenter==EVR) - { - Log::getInstance()->log("VideoWin", Log::INFO ,"EVR Videopresenter selected!"); - if (hres=CoCreateInstance(CLSID_EnhancedVideoRenderer,0, - CLSCTX_INPROC_SERVER,IID_IBaseFilter,(void**) &dsrenderer)!=S_OK) - { - Log::getInstance()->log("VideoWin", Log::WARN ,"Failed creating EVR renderer!"); - ReleaseMutex(filtermutex); - CleanupDS(); - } - /*EVR stuff**/ - if (hres=dsgraphbuilder->AddFilter(dsrenderer,L"EVR")!=S_OK) - { - ReleaseMutex(filtermutex); - CleanupDS(); - Log::getInstance()->log("VideoWin", Log::WARN ,"Failed adding EVR renderer!"); - return 0; - } - - - IMFGetService *evr_services; - if (dsrenderer->QueryInterface(IID_IMFGetService,(void**)&evr_services)!=S_OK) - { - ReleaseMutex(filtermutex); - CleanupDS(); - Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting EVR IMFGetServices interface!"); - return 0; - } - - IMFVideoDisplayControl* mfvideodisplaycontrol; - if (evr_services->GetService(MR_VIDEO_RENDER_SERVICE,IID_IMFVideoDisplayControl,(void**)&mfvideodisplaycontrol)!=S_OK) - { - ReleaseMutex(filtermutex); - CleanupDS(); - Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting EVR IMFVideoDisplayControl interface!"); - return 0; - } - - evr_services->Release(); - mfvideodisplaycontrol->SetVideoWindow(((OsdWin*) Osd::getInstance())->getWindow()); - //RECT client; - //GetClientRect(((OsdWin*) Osd::getInstance())->getWindow(), &client); - //mfvideodisplaycontrol->SetVideoPosition(NULL,&client); - - mfvideodisplaycontrol->Release(); - - - /// if (vmrdeinterlacing!=0) vmrfilconfig->SetNumberOfStreams(1);//Enter Mixing Mode //always the case for evr! - - IMFVideoRenderer *mfvideorenderer; - if (dsrenderer->QueryInterface(IID_IMFVideoRenderer,(void**)&mfvideorenderer)!=S_OK) - { - ReleaseMutex(filtermutex); - CleanupDS(); - Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting EVR IMFVideoRenderer interface!"); - return 0; - } - - allocatorvmr=new DsAllocator(); - HRESULT hres=mfvideorenderer->InitializeRenderer(NULL,allocatorvmr); - - mfvideorenderer->Release(); - //How should I do this in EVR? - /* IVMRDeinterlaceControl9* deintctrl; - if (dsrenderer->QueryInterface(IID_IVMRDeinterlaceControl9,(void**)&deintctrl)!=S_OK) - { - ReleaseMutex(filtermutex); - CleanupDS(); - Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting VMR9 Deinterlace control!"); - return 0; - } - /*turnoff* - switch (vmrdeinterlacing) - { - case 1: //No Deinterlasing - deintctrl->SetDeinterlaceMode(0xFFFFFFFF,(LPGUID)&GUID_NULL);//Turn Off - break; - case 2: //Best - deintctrl->SetDeinterlacePrefs(DeinterlacePref_NextBest);//Choose Next Best - break; - case 3: //Bob - deintctrl->SetDeinterlacePrefs(DeinterlacePref_BOB);//Choose NBob - break; - case 4: //Weave - deintctrl->SetDeinterlacePrefs(DeinterlacePref_Weave);//Choose Weave - break; - }; - deintctrl->Release();*/ - /*EVR stuff end */ - } else { - Log::getInstance()->log("VideoWin", Log::ERR ,"No videopresenter selected! Please post on the forum!"); - return -1; - } - IFilterGraph2*fg2=NULL; - if (dsgraphbuilder->QueryInterface(IID_IFilterGraph2,(void**)&fg2)!= S_OK) - { - Log::getInstance()->log("VideoWin", Log::WARN , "Failed querying for FilterGraph2 Interface!"); - ReleaseMutex(filtermutex); - CleanupDS(); - return 0; - } -/*#ifndef NEW_DS_MECHANISMENS - - if (hres=fg2->RenderEx((IPin*)sourcefilter->GetVideoPin()/*video*, - AM_RENDEREX_RENDERTOEXISTINGRENDERERS,NULL) != S_OK) - { - Log::getInstance()->log("VideoWin", Log::WARN , "Failed rendering Video!"); - fg2->Release(); - ReleaseMutex(filtermutex); - CleanupDS(); - return 0; - } - -#else*/ - IBaseFilter*videofilter; - if (h264) - { - Log::getInstance()->log("VideoWin", Log::DEBUG ,"Entering h264 playback..."); - videofilter=getVideoH264Filter(); - } - else - { - Log::getInstance()->log("VideoWin", Log::DEBUG ,"Entering MPEG2 playback..."); - videofilter=getVideoFilter(); - } - if (hres=dsgraphbuilder->AddFilter(videofilter,NULL) != S_OK) - { - Log::getInstance()->log("VideoWin", Log::WARN , "Failed adding Video Filter!"); - ReleaseMutex(filtermutex); - CleanupDS(); - return 0; - } - IEnumPins *pinenum=NULL; - bool error=false; - - mptype_video_detail vid_details; - Demuxer* demux=Demuxer::getInstance(); - vid_details.width=demux->getHorizontalSize(); - vid_details.height=demux->getVerticalSize(); - - if (h264) - { - if (vid_details.width!=0 && vid_details.height!=0) - { - sourcefilter->GetVideoPin()->SetPinMode(MPTYPE_VIDEO_H264,&vid_details); - } - else - { - sourcefilter->GetVideoPin()->SetPinMode(MPTYPE_VIDEO_H264,NULL); - } - - } - else - { - if (vid_details.width!=0 && vid_details.height!=0) - { - sourcefilter->GetVideoPin()->SetPinMode(MPTYPE_VIDEO_MPEG2,&vid_details); - } - else - { - sourcefilter->GetVideoPin()->SetPinMode(MPTYPE_VIDEO_MPEG2,NULL); - } - } - if (videofilter->EnumPins(&pinenum) == S_OK) - { - IPin *current=NULL; - ULONG fetch=0; - bool firststep=false; - - while (pinenum->Next(1,¤t,&fetch)==S_OK) - { - PIN_DIRECTION dir; - if (current->QueryDirection(&dir)==S_OK) - { - if (dir == PINDIR_INPUT) - { - if (sourcefilter->GetVideoPin()->Connect(current,NULL)==S_OK) - { - current->Release(); - firststep=true; - break; - } - } - } - current->Release(); - } - if (firststep==false) - { - Log::getInstance()->log("VideoWin", Log::WARN , "Video Filter has no suitable input!"); - videofilter->Release(); - ReleaseMutex(filtermutex); - CleanupDS(); - return 0; - } - bool secondstep=false; - pinenum->Reset(); - while (pinenum->Next(1,¤t,&fetch)==S_OK) - { - PIN_DIRECTION dir; - if (current->QueryDirection(&dir)==S_OK) - { - if (dir == PINDIR_OUTPUT) - { - - if (fg2->RenderEx((IPin*)current/*video*/, - AM_RENDEREX_RENDERTOEXISTINGRENDERERS,NULL) ==S_OK) - { - current->Release(); - secondstep=true; - break; - } - } - } - current->Release(); - } - if (secondstep==false) - { - Log::getInstance()->log("VideoWin", Log::WARN , "Video Filter has no suitable output!"); - videofilter->Release(); - ReleaseMutex(filtermutex); - CleanupDS(); - return 0; - } - - videofilter->Release(); - pinenum->Release(); - - } - - - - fg2->Release(); - return 1; - } -#endif - return 1; -} - -int VideoWin::setAudioStreamType(UCHAR type) -{ - aud_type=type; - if (!initted) return 0; - return 1; -} - -int VideoWin::dsplay() -{ - if (!initted) return 0; - CleanupDS(); - - //Build filter graph - HRESULT hres; - //So this is the real code, this prevents the feeder from calling noexisting objects! - WaitForSingleObject(filtermutex,INFINITE); - if (hres=CoCreateInstance(CLSID_FilterGraph,NULL,CLSCTX_INPROC_SERVER, - IID_IGraphBuilder,(void**)&dsgraphbuilder) != S_OK) - { - ReleaseMutex(filtermutex); - return 0; - } - #ifdef DS_DEBUG - AddToRot(dsgraphbuilder,&graphidentifier); - #endif - - firstsynched=false; - if (aud_type==Audio::MP3) { - lastaudiomode=MPTYPE_MPEG_AUDIO_LAYER3; - } else { - lastaudiomode=MPTYPE_MPEG_AUDIO; - } - //lastaudiomode=MPTYPE_AC3; - sourcefilter=new DsSourceFilter(); //Creating our Source filter for pushing Data - // to DirectShow - if (hres=dsgraphbuilder->AddFilter(sourcefilter,L"Vomp Win Source Filter") != S_OK) - { - Log::getInstance()->log("VideoWin", Log::WARN , "Failed adding Vomp Source Filter!"); - ReleaseMutex(filtermutex); - CleanupDS(); - return 0; - } - sourcefilter->GetAudioPin()->SetPinMode(lastaudiomode); - /*if (hres=dsgraphbuilder->Render((IPin*)sourcefilter->GetAudioPin()/*audio*)!=S_OK) - { - Log::getInstance()->log("VideoWin", Log::WARN , "Failed rendering audio!"); - ReleaseMutex(filtermutex); - CleanupDS(); - return 0; - }*/ - if (((AudioWin*)Audio::getInstance())->dsInitAudioFilter(dsgraphbuilder)==0) - { - Log::getInstance()->log("VideoWin", Log::WARN , "Failed rendering audio!"); - ReleaseMutex(filtermutex); - CleanupDS(); - return 0; - } - - - if (dsInitVideoFilter()==0) - { - return 0; - } - - if (hres=CoCreateInstance(CLSID_SystemClock,NULL,CLSCTX_INPROC_SERVER, - IID_IReferenceClock,(void**)&dsrefclock)!=S_OK) - { - return 0; - } - dsgraphbuilder->QueryInterface(IID_IMediaFilter,(void **) &dsmediafilter); - HRESULT hresdeb = dsmediafilter->SetSyncSource(dsrefclock); - - dsgraphbuilder->QueryInterface(IID_IMediaControl,(void **) &dsmediacontrol); - dsgraphbuilder->QueryInterface(IID_IBasicAudio,(void **) &dsbasicaudio); - if (dsbasicaudio) - dsbasicaudio->put_Volume(audiovolume); - dsinited=true; - //MILLISLEEP(100); - - hresdeb=dsmediacontrol->Run(); - iframemode=false;//exit iframe mode - ReleaseMutex(filtermutex); - return 1; -} - -int VideoWin::EnterIframePlayback() -{ - if (!initted) return 0; - CleanupDS(); - //So this is the real code, this prevents the feeder from calling noexisting objects! - WaitForSingleObject(filtermutex,INFINITE); - iframemode=true;//enter iframe mode - //Build filter graph - HRESULT hres; - if (hres=CoCreateInstance(CLSID_FilterGraph,NULL,CLSCTX_INPROC_SERVER, - IID_IGraphBuilder,(void**)&dsgraphbuilder)!=S_OK) { - ReleaseMutex(filtermutex); - return 0; - } -#ifdef DS_DEBUG - AddToRot(dsgraphbuilder,&graphidentifier); -#endif - - //firstsynched=false; - sourcefilter=new DsSourceFilter(); //Creating our Source filter for pushing Data - // to DirectShow - if (hres=dsgraphbuilder->AddFilter(sourcefilter,L"Vomp Win Source Filter")!=S_OK) { - Log::getInstance()->log("VideoWin", Log::WARN , "Failed adding Vomp Source Filter!"); - ReleaseMutex(filtermutex); - CleanupDS(); - return 0; - } -#ifdef DO_VIDEO - if (videoon) { - dsInitVideoFilter(); - } -#endif -/* if (hres=CoCreateInstance(CLSID_SystemClock,NULL,CLSCTX_INPROC_SERVER, - IID_IReferenceClock,(void**)&dsrefclock)!=S_OK) { - return 0; - }*/ - - dsgraphbuilder->QueryInterface(IID_IMediaFilter,(void **) &dsmediafilter); - dsmediafilter->SetSyncSource(/*dsrefclock*/NULL); //Run as fast as you can! - - dsgraphbuilder->QueryInterface(IID_IMediaControl,(void **) &dsmediacontrol); - dsgraphbuilder->QueryInterface(IID_IBasicAudio,(void **) &dsbasicaudio); - dsinited=true; - - - dsmediacontrol->Run(); - ReleaseMutex(filtermutex); - return 1; - -} - -int VideoWin::dsstop() -{ - if (!initted) return 0; - - CleanupDS(); - - - return 1; -} - -int VideoWin::stop() -{ - if (!initted) return 0; - - - return 1; -} - -int VideoWin::reset() -{ - if (!initted) return 0; - - - return 1; -} - -int VideoWin::dsreset() -{ - if (!initted) return 0; - videoposx=0; - videoposy=0; - iframemode=false;//exit iframe mode - CleanupDS(); - - return 1; -} - -int VideoWin::dspause() -{ - if (!initted) return 0; - WaitForSingleObject(filtermutex,INFINITE); - if (dsmediacontrol) dsmediacontrol->Pause(); - ReleaseMutex(filtermutex); - return 1; -} - -int VideoWin::pause() -{ - if (!initted) return 0; - - return 1; -} - -int VideoWin::unPause() // FIXME get rid - same as play!! -{//No on windows this is not the same, I don't get rid of! - if (!initted) return 0; - return 1; -} - -int VideoWin::dsunPause() // FIXME get rid - same as play!! -{//No on windows this is not the same, I don't get rid of! - if (!initted) return 0; - WaitForSingleObject(filtermutex,INFINITE); - if (dsmediacontrol) dsmediacontrol->Run(); - ReleaseMutex(filtermutex); - - return 1; -} - -int VideoWin::fastForward() -{ - if (!initted) return 0; - - return 1; -} - -int VideoWin::unFastForward() -{ - if (!initted) return 0; - - return 1; -} - -int VideoWin::attachFrameBuffer() -{ - if (!initted) return 0; - return 1; -} - -int VideoWin::blank(void) -{ - ((OsdWin*)Osd::getInstance())->Blank(); - return 1; -} - -ULLONG VideoWin::getCurrentTimestamp() -{ - REFERENCE_TIME startoffset; - REFERENCE_TIME ncr_time; - if (iframemode) return 0; //Not in iframe mode! - if (!dsrefclock || !sourcefilter) return 0; - FILTER_STATE state; - sourcefilter->GetState(10,&state); - - if (state==State_Running) dsrefclock->GetTime(&cr_time); - ncr_time=cr_time; - startoffset=sourcefilter->getStartOffset(); - if (startoffset==0) return 0; - ncr_time-=startoffset; - ncr_time-=lastreftimeRT; - /* ULLONG result=frameNumberToTimecode( - VDR::getInstance()->frameNumberFromPosition(lastreftimeBYTE));*/ - long long result=lastreftimePTS; - result+=(ULLONG)(ncr_time/10000LL*90LL); - if (result<0) result=(1LL << 33)-result; - return result; - -} - - -/* //to beremoved -ULONG VideoWin::timecodeToFrameNumber(ULLONG timecode) -{ - if (format == PAL) return (ULONG)(((double)timecode / (double)90000) * (double)25); - else return (ULONG)(((double)timecode / (double)90000) * (double)30); -} - -ULLONG VideoWin::frameNumberToTimecode(ULONG framenumber) -{ - if (format == PAL) return (ULLONG)(((double)framenumber * (double)90000) / (double)25); - else return (ULLONG)(((double)framenumber * (double)90000) / (double)30); -} -*/ -void VideoWin::CleanupDS() -{ - WaitForSingleObject(filtermutex,INFINITE); - dsinited=false; - if (dsmediacontrol)dsmediacontrol->Stop(); - if (cur_audio_media_sample) { - cur_audio_media_sample->Release(); - cur_audio_media_sample=NULL; - } - if (cur_video_media_sample) { - cur_video_media_sample->Release(); - cur_video_media_sample=NULL; - } - if (dsbasicaudio) { - dsbasicaudio->Release(); - dsbasicaudio=NULL; - } - if (dsvmrsurfnotify) { - dsvmrsurfnotify->Release(); - dsvmrsurfnotify=NULL; - } - if (dsrenderer) { - dsrenderer->Release(); - dsrenderer=NULL; - } - - if (allocatorvmr) { - allocatorvmr->Release(); - allocatorvmr=NULL; - } - - if (dsrefclock) { - dsrefclock->Release(); - dsrefclock=NULL; - } - if (dsmediafilter) { - dsmediafilter->Release(); - dsmediafilter=NULL; - } - - - - if (dsmediacontrol) { - dsmediacontrol->Stop(); - dsmediacontrol->Release(); - dsmediacontrol=NULL; - } - if (dsgraphbuilder){ -#ifdef DS_DEBUG - RemoveFromRot(graphidentifier); -#endif - dsgraphbuilder->Release(); - dsgraphbuilder=NULL; - - sourcefilter=NULL; //The Graph Builder destroys our SourceFilter - } - ReleaseMutex(filtermutex); - -} - -void VideoWin::PrepareMediaSample(const MediaPacketList& mplist, - UINT samplepos) -{ - mediapacket = mplist.front(); -} - -UINT VideoWin::DeliverMediaSample(UCHAR* buffer, UINT *samplepos) -{ - DeliverMediaPacket(mediapacket, buffer, samplepos); - if (*samplepos == mediapacket.length) { - *samplepos = 0; - return 1; - } - else return 0; -} - -UINT VideoWin::DeliverMediaPacket(MediaPacket packet, - const UCHAR* buffer, - UINT *samplepos) -{ - - /*First Check, if we have an audio sample*/ - if (!isdsinited()) return 0; - if (packet.type == MPTYPE_VIDEO_H264) - { - h264=true; - } - else - { - h264=false; - } - -#ifdef DO_VIDEO - if (!videoon) { - *samplepos+=packet.length; - MILLISLEEP(0); //yet not implemented//bad idea - return packet.length; - } - /*First Check, if we have an audio sample*/ - if (iframemode) { - //samplepos=0; - MILLISLEEP(10); - return 0; //Not in iframe mode! - } - IMediaSample* ms=NULL; - REFERENCE_TIME reftime1=0; - REFERENCE_TIME reftime2=0; - - UINT headerstrip=0; - if (packet.disconti) { - firstsynched=false; - DeliverVideoMediaSample(); - } - - - - /*Inspect PES-Header */ - - if (*samplepos==0) {//stripheader - headerstrip=buffer[packet.pos_buffer+8]+9/*is this right*/; - *samplepos+=headerstrip; - if ( packet.synched ) { - DeliverVideoMediaSample();//write out old data - /* if (packet.presentation_time<0) { //Preroll? - *samplepos=packet.length;//if we have not processed at least one - return packet.length;//synched packet ignore it! - }*/ - - reftime1=packet.presentation_time; - reftime2=reftime1+1; - firstsynched=true; - } else { - if (!firstsynched) {// - *samplepos=packet.length;//if we have not processed at least one - return packet.length;//synched packet ignore it! - } - } - } - BYTE *ms_buf; - UINT ms_length; - UINT ms_pos; - UINT haveToCopy; - - if (!getCurrentVideoMediaSample(&ms) || ms==NULL) {// get the current sample - //samplepos=0; - //MessageBox(0,"da isser","hei",0); - //MILLISLEEP(1); - return 0; - } - ms_pos=ms->GetActualDataLength(); - ms_length=ms->GetSize(); - haveToCopy=min(ms_length-ms_pos,packet.length-*samplepos); - if ((ms_length-ms_pos)<1 ) { - DeliverVideoMediaSample(); //we are full! - if (!getCurrentVideoMediaSample(&ms) || ms==NULL) {// get the current sample - //samplepos=0; - //MessageBox(0,"da isser","hei",0); - //MILLISLEEP(10); - return 0; - } - ms_pos=ms->GetActualDataLength(); - ms_length=ms->GetSize(); - haveToCopy=min(ms_length-ms_pos,packet.length-*samplepos); - } - ms->GetPointer(&ms_buf); - - - if (ms_pos==0) {//will only be changed on first packet - if (packet.disconti) { - ms->SetDiscontinuity(TRUE); - } else { - ms->SetDiscontinuity(FALSE); - } - if (packet.synched) { - ms->SetSyncPoint(TRUE); - ms->SetTime(&reftime1,&reftime2); - //Log::getInstance()->log("VideoWin", Log::DEBUG , "Setted videotime to %lld %lld",reftime1,reftime2); - //Log::getInstance()->log("VideoWin", Log::DEBUG , "Packet pts %lld dts %lld",packet.pts,packet.dts); - //ms->SetTime(NULL,NULL); - ms->SetMediaTime(NULL, NULL); - if (reftime1<0) ms->SetPreroll(TRUE); - else ms->SetPreroll(FALSE); - /*Timecode handling*/ - lastreftimeRT=reftime1; - lastreftimePTS=packet.pts; - - } - else - { - ms->SetSyncPoint(FALSE); - ms->SetTime(NULL,NULL); - ms->SetMediaTime(NULL, NULL); - ms->SetPreroll(FALSE); - - // ms->SetSyncPoint(TRUE); - } - } - - - - memcpy(ms_buf+ms_pos,buffer+packet.pos_buffer+*samplepos,haveToCopy); - ms->SetActualDataLength(haveToCopy+ms_pos); - - *samplepos+=haveToCopy; - - return haveToCopy+headerstrip; - -#else - - *samplepos+=packet.length; - MILLISLEEP(0); //yet not implemented//bad idea - return packet.length; -#endif -} - -int VideoWin::getCurrentAudioMediaSample(IMediaSample** ms) -{ - //WaitForSingleObject(filtermutex,INFINITE); - if (!sourcefilter){ - // ReleaseMutex(filtermutex); - return 0; - } - if (cur_audio_media_sample) { - *ms=cur_audio_media_sample;//already open - return 1; - } - if (!sourcefilter->getCurrentAudioMediaSample(ms)) { - // ReleaseMutex(filtermutex); - } - if (*ms) (*ms)->SetActualDataLength(0); - cur_audio_media_sample=*ms; - //Don't release the mutex before deliver - return 1; -} - -int VideoWin::getCurrentVideoMediaSample(IMediaSample** ms) -{ - //WaitForSingleObject(filtermutex,INFINITE); - if (!sourcefilter){ - // ReleaseMutex(filtermutex); - return 0; - } - if (cur_video_media_sample) { - *ms=cur_video_media_sample;//already open - return 1; - } - if (!sourcefilter->getCurrentVideoMediaSample(ms)) { - // ReleaseMutex(filtermutex); - } - if (*ms) (*ms)->SetActualDataLength(0); - - cur_video_media_sample=*ms; - //Don't release the mutex before deliver - return 1; -} - -int VideoWin::DeliverAudioMediaSample(){ - if (cur_audio_media_sample) { - sourcefilter->DeliverAudioMediaSample(cur_audio_media_sample); - cur_audio_media_sample=NULL; - } - //ReleaseMutex(filtermutex); - return 1; -} - -int VideoWin::DeliverVideoMediaSample(){ - if (cur_video_media_sample) { - sourcefilter->DeliverVideoMediaSample(cur_video_media_sample); - cur_video_media_sample=NULL; - } - //ReleaseMutex(filtermutex); - return 1; -} - -long long VideoWin::SetStartOffset(long long curreftime, bool *rsync) -{ - *rsync=false; - if (offsetnotset) { - startoffset=curreftime;//offset is set for audio - offsetnotset=false; - offsetvideonotset=false; - - - } else { - if (offsetvideonotset) { - offsetvideonotset=false; - *rsync=true; - } else { - if ( (curreftime-lastrefvideotime)>10000000LL - || (curreftime-lastrefvideotime)<-10000000LL) {//if pts jumps to big resync - startoffset+=curreftime-lastrefvideotime; - lastrefaudiotime+=curreftime-lastrefvideotime; - //*rsync=true; - offsetaudionotset=true; - - } - } - - } - - lastrefvideotime=curreftime; - - return startoffset; - -} - -long long VideoWin::SetStartAudioOffset(long long curreftime, bool *rsync) -{ - *rsync=false; - if (offsetnotset) { - startoffset=curreftime; - offsetnotset=false; - offsetaudionotset=false; - }else { - if (offsetaudionotset) { - offsetaudionotset=false; - *rsync=true; - } else { - if ( (curreftime-lastrefaudiotime)>10000000LL - || (curreftime-lastrefaudiotime)<-10000000LL) {//if pts jumps to big resync - startoffset+=curreftime-lastrefaudiotime; - lastrefvideotime+=curreftime-lastrefaudiotime; - //*rsync=true; - offsetvideonotset=true; - - } - } - - } - lastrefaudiotime=curreftime; - return startoffset; - -} -void VideoWin::ResetTimeOffsets() { - offsetnotset=true; //called from demuxer - offsetvideonotset=true; - offsetaudionotset=true; - startoffset=0; - lastrefaudiotime=0; - lastrefvideotime=0; - lastreftimeRT=0; - lastreftimePTS=0; - - -} - -void VideoWin::SetAudioVolume(long volume) -{ - audiovolume=volume; - if (dsbasicaudio) dsbasicaudio->put_Volume(volume); -} - -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 - - - - +/* + Copyright 2004-2005 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + + + +#include "videowin.h" +#include "log.h" +#include "dssourcefilter.h" +#include "dsallocator.h" +#include "vdr.h" +#include "osdwin.h" +#include "audiowin.h" +#include "wwinvideofilter.h" +#include "wwinvideoh264filter.h" +#include "wtabbar.h" +#include "woptionpane.h" +#include "i18n.h" +#include "demuxer.h" + +#include +#include + +void AdjustWindow(); + + + +VideoWin::VideoWin() +{ + dsinited=false; + dsgraphbuilder=NULL; + dsmediacontrol=NULL; + dsrenderer=NULL; + dsrefclock=NULL; + dsmediafilter=NULL; + dsbasicaudio=NULL; + sourcefilter=NULL; + allocatorvmr=NULL; + cr_time=0; + lastaudiomode=MPTYPE_MPEG_AUDIO; + //lastaudiomode=MPTYPE_AC3; + dsvmrsurfnotify=NULL; + filtermutex=CreateMutex(NULL,FALSE,NULL); + offsetnotset=true; + offsetvideonotset=true; + offsetaudionotset=true; + startoffset=0; + lastrefaudiotime=0; + lastrefvideotime=0; + lastreftimeRT=0; + lastreftimePTS=0; + firstsynched=false; + cur_audio_media_sample=NULL; + cur_video_media_sample=NULL; + videoon=true; + audioon=true; + audiovolume=0; + pseudotvsize=0; + videoposx=0; + videoposy=0; + aud_type=Audio::MPEG2_PES; + iframemode=false;//We are not in Iframe mode at begining + vmrdeinterlacing=2;//Best + videofilterselected=-1; + videoH264filterselected=-1; + OSVERSIONINFO verinfo; + verinfo.dwOSVersionInfoSize=sizeof(verinfo); + GetVersionEx(&verinfo); + + if (verinfo.dwMajorVersion>=6) { + currentpresenter=EVR; + } else { + currentpresenter=VMR9; + } + videoH264dtsfix=false; + videompeg2dtsfix=false; + + + + +} + +VideoWin::~VideoWin() +{ + CleanupDS(); + CloseHandle(filtermutex); + unsigned int i; + for (i=0;ilog("VideoWin", Log::ERR , "Unable to create FilterMapper!"); + return; + } + /* Wishlist, what Mediatypes do we want */ + GUID mtypesin[]={MEDIATYPE_Video,MEDIASUBTYPE_MPEG2_VIDEO}; + IEnumMoniker *myenum; + result = filtmap->EnumMatchingFilters(&myenum,0,TRUE,MERIT_DO_NOT_USE+1, + TRUE,1,mtypesin,NULL,NULL,FALSE,TRUE,0,NULL,NULL,NULL); + if (result != S_OK) + { + filtmap->Release(); + Log::getInstance()->log("VideoWin", Log::ERR , "Unable to enum Filters!"); + return; + } + ULONG gethowmany; + IMoniker * moni; + while(myenum->Next(1,&moni,&gethowmany)==S_OK) + { + VideoFilterDesc desc; + ZeroMemory(&desc,sizeof(desc)); + + LPOLESTR string; + moni->GetDisplayName(0,0,&string); + desc.displayname=new char[wcslen(string)+1]; + wcstombs(desc.displayname,string,wcslen(string)+1); + CoTaskMemFree(string); + IPropertyBag *bag; + if (moni->BindToStorage(0,0,IID_IPropertyBag,(void**)&bag) == S_OK) + { + VARIANT vari; + VariantInit(&vari); + result = bag->Read(L"FriendlyName",&vari,NULL); + if (result == S_OK) + { + desc.friendlyname=new char[wcslen(vari.bstrVal)+1]; + wcstombs(desc.friendlyname,vari.bstrVal,wcslen(vari.bstrVal)+1); + } + VariantClear(&vari); + bag->Release(); + + } + + + videofilterlist.push_back(desc); + + + + moni->Release(); + // bctx->Release(); + } + int i; + videofilterselected=-1; + + + + myenum->Release(); + + + + filtmap->Release(); +} + +void VideoWin::initH264FilterDatabase() +{ + /* This method should determine all availiable DirectShow Filters */ + IFilterMapper2* filtmap=NULL; + HRESULT result; + result = CoCreateInstance(CLSID_FilterMapper2,NULL,CLSCTX_INPROC, + IID_IFilterMapper2,(void**)&filtmap); + if (result != S_OK) + { + Log::getInstance()->log("VideoWin", Log::ERR , "Unable to create FilterMapper!"); + return; + } + /* Wishlist, what Mediatypes do we want */ + GUID mtypesin[]={MEDIATYPE_Video,MEDIASUBTYPE_H264}; + IEnumMoniker *myenum; + result = filtmap->EnumMatchingFilters(&myenum,0,TRUE,MERIT_DO_NOT_USE+1, + TRUE,1,mtypesin,NULL,NULL,FALSE,TRUE,0,NULL,NULL,NULL); + if (result != S_OK) + { + filtmap->Release(); + Log::getInstance()->log("VideoWin", Log::ERR , "Unable to enum Filters!"); + return; + } + ULONG gethowmany; + IMoniker * moni; + while(myenum->Next(1,&moni,&gethowmany)==S_OK) + { + VideoFilterDesc desc; + ZeroMemory(&desc,sizeof(desc)); + + LPOLESTR string; + moni->GetDisplayName(0,0,&string); + desc.displayname=new char[wcslen(string)+1]; + wcstombs(desc.displayname,string,wcslen(string)+1); + CoTaskMemFree(string); + IPropertyBag *bag; + if (moni->BindToStorage(0,0,IID_IPropertyBag,(void**)&bag) == S_OK) + { + VARIANT vari; + VariantInit(&vari); + result = bag->Read(L"FriendlyName",&vari,NULL); + if (result == S_OK) + { + desc.friendlyname=new char[wcslen(vari.bstrVal)+1]; + wcstombs(desc.friendlyname,vari.bstrVal,wcslen(vari.bstrVal)+1); + } + VariantClear(&vari); + bag->Release(); + + } + + + videoH264filterlist.push_back(desc); + + + + moni->Release(); + // bctx->Release(); + } + int i; + videoH264filterselected=-1; + + + + myenum->Release(); + + + + filtmap->Release(); +} + +bool VideoWin::loadOptionsfromServer(VDR* vdr) +{ + char *name=vdr->configLoad("DirectShow","VideoFilter"); + + if (name != NULL) + { + for (int i = 0;i configLoad("DirectShow","VideoH264Filter"); + + if (name != NULL) + { + for (int i = 0;i configLoad("DirectShow","VMR9DeinterlacingMode"); + if (name != NULL) + { + if (STRCASECMP(name,"NoMix")==0) { + vmrdeinterlacing=0; + } else if (STRCASECMP(name,"None")==0) { + vmrdeinterlacing=1; + } else if (STRCASECMP(name,"Best")==0) { + vmrdeinterlacing=2; + } else if (STRCASECMP(name,"Bob")==0) { + vmrdeinterlacing=3; + } else if (STRCASECMP(name,"Weave")==0) { + vmrdeinterlacing=4; + } + } + + name=vdr->configLoad("DirectShow", "VideoPresenter"); + if (name!=NULL) { + if (STRCASECMP(name,"VMR9")==0) { + currentpresenter=VMR9; + } else if (STRCASECMP(name,"EVR")==0) { + currentpresenter=EVR; + } + } + if (!((OsdWin*)Osd::getInstance())->IsEvrSupported()) { + currentpresenter=VMR9; + } + + name=vdr->configLoad("DirectShow","videoH264dtsfix"); + if (name!=NULL) { + if (STRCASECMP(name,"YES")==0) { + videoH264dtsfix=true; + } else { + videoH264dtsfix=false; + } + } + name=vdr->configLoad("DirectShow","videompeg2dtsfix"); + if (name!=NULL) { + if (STRCASECMP(name,"YES")==0) { + videompeg2dtsfix=true; + } else { + videompeg2dtsfix=false; + } + } + + name=vdr->configLoad("DirectGraphics", "StretchFiltering"); + if (name!=NULL) { + if (STRCASECMP(name,"None")==0) { + ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_NONE); + } else if (STRCASECMP(name,"Point")==0) { + ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_POINT); + } else if (STRCASECMP(name,"Linear")==0) { + ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_LINEAR); + } + } + + + + + return true; + +} + +bool VideoWin::handleOptionChanges(Option* option) +{ + if( Video::handleOptionChanges(option)) return true; + switch(option->id) { + case 1: { + if (STRCASECMP(option->options[option->userSetChoice],"None")==0) { + ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_NONE); + } else if (STRCASECMP(option->options[option->userSetChoice],"Point")==0) { + ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_POINT); + } else if (STRCASECMP(option->options[option->userSetChoice],"Linear")==0) { + ((OsdWin*)Osd::getInstance())->setFilterType(D3DTEXF_LINEAR); + } + return true; + } break; + case 2: { + if (STRCASECMP(option->options[option->userSetChoice],"NoMix")==0) { + vmrdeinterlacing=0; + } else if (STRCASECMP(option->options[option->userSetChoice],"None")==0) { + vmrdeinterlacing=1; + } else if (STRCASECMP(option->options[option->userSetChoice],"Best")==0) { + vmrdeinterlacing=2; + } else if (STRCASECMP(option->options[option->userSetChoice],"Bob")==0) { + vmrdeinterlacing=3; + } else if (STRCASECMP(option->options[option->userSetChoice],"Weave")==0) { + vmrdeinterlacing=4; + } + }break; + case 3: { + if (STRCASECMP(option->options[option->userSetChoice],"VMR9")==0) { + currentpresenter=VMR9; + } else if (STRCASECMP(option->options[option->userSetChoice],"EVR")==0) { + currentpresenter=EVR; + } + }break; + case 4: { + if (STRCASECMP(option->options[option->userSetChoice],"Yes")==0) { + videoH264dtsfix=true; + } else { + videoH264dtsfix=false; + } + }break; + case 5: { + if (STRCASECMP(option->options[option->userSetChoice],"Yes")==0) { + videompeg2dtsfix=true; + } else { + videompeg2dtsfix=false; + } + }break; + }; + return false; + +} + +bool VideoWin::saveOptionstoServer() +{ + if (videofilterselected!=-1) { + VDR::getInstance()->configSave("DirectShow", + "VideoFilter",videofilterlist[videofilterselected].displayname); + VDR::getInstance()->configSave("DirectShow", + "VideoH264Filter",videoH264filterlist[videoH264filterselected].displayname); + } + return true; +} + +/*Option(UINT id, const char* displayText, const char* configSection, const char* configKey, UINT optionType, + UINT numChoices, UINT defaultChoice, UINT startInt, + const char * const * options, const char * const * optionkeys = NULL, AbstractOption* handler=NULL);*/ + +bool VideoWin::addOptionsToPanes(int panenumber,Options *options,WOptionPane* pane) +{ + if (!Video::addOptionsToPanes(panenumber,options,pane)) return false; + + + Option* option; + if (panenumber == 2) + { + DWORD scalingcaps=((OsdWin*)Osd::getInstance())->getFilterCaps(); + char **scalingopts=new char *[3]; + int i=0; + scalingopts[i]=new char[strlen("None")+1]; + strcpy(scalingopts[i],"None"); + i++; + if ((scalingcaps & D3DPTFILTERCAPS_MINFPOINT)!=0 + && (scalingcaps & D3DPTFILTERCAPS_MAGFPOINT)!=0) { + scalingopts[i]=new char[strlen("Point")+1]; + strcpy(scalingopts[i],"Point"); + i++; + } + if ((scalingcaps & D3DPTFILTERCAPS_MINFLINEAR)!=0 + && (scalingcaps & D3DPTFILTERCAPS_MAGFLINEAR)!=0) { + scalingopts[i]=new char[strlen("Linear")+1]; + strcpy(scalingopts[i],"Linear"); + i++; + } + option = new Option(1 ,tr("Video Stretching Filter"), "DirectGraphics", "StretchFiltering", Option::TYPE_TEXT, i, (i-1), 0, scalingopts,NULL,true, this); + options->push_back(option); + pane->addOptionLine(option); + static const char* deintopts[]={"NoMix","None","Best","Bob","Weave"}; + option = new Option(2,tr("VMR9 Deinterlacing Mode"), "DirectShow","VMR9DeinterlacingMode",Option::TYPE_TEXT,5,2,0,deintopts,NULL,false,this); + options->push_back(option); + pane->addOptionLine(option); + + if (((OsdWin*)Osd::getInstance())->IsEvrSupported()) + { + static const char* presenteropts[]={"EVR","VMR9"}; + option = new Option(3,tr("Video Presenter Filter"),"DirectShow", "VideoPresenter",Option::TYPE_TEXT,2, + (currentpresenter==EVR)?0:1,0,presenteropts,NULL,false,this); + } else { + static const char* presenteropts[]={"VMR9"}; + option = new Option(3,tr("Video Presenter Filter"),"DirectShow", "VideoPresenter",Option::TYPE_TEXT,1,0,0,presenteropts,NULL,false,this); + } + options->push_back(option); + pane->addOptionLine(option); + + static const char* yesnoopts[]={"Yes","No"}; + option = new Option(4,tr("Video H264 fix dts time"), "DirectShow","videoH264dtsfix",Option::TYPE_TEXT,2,1,0,yesnoopts,NULL,false,this); + options->push_back(option); + pane->addOptionLine(option); + + option = new Option(5,tr("Video Mpeg2 fix dts time"), "DirectShow","videompeg2dtsfix",Option::TYPE_TEXT,2,1,0,yesnoopts,NULL,false,this); + options->push_back(option); + pane->addOptionLine(option); + + + + } + + return true; +} + +IBaseFilter *VideoWin::getVideoFilter() +{ + IBaseFilter *curfilter= NULL; + if (videofilterselected == -1) + { + int i; + for (i = 0;i BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) + { + IAMDecoderCaps* desccaps=NULL; + if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK) + { + DWORD caps; + desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps); + if (caps == DECODER_CAP_SUPPORTED) + { + videofilterlist[i].vmr9tested = true; + videofilterlist[i].vmr9 = true; + videofilterselected = i; + } + else + { + videofilterlist[i].vmr9tested = true; + videofilterlist[i].vmr9 = false; + + curfilter->Release(); + curfilter=NULL; + } + } + desccaps->Release(); + } + moni->Release(); + } + delete [] name; + bindctx->Release(); + } + if (videofilterlist[i].vmr9) break; + + } + if (curfilter != NULL) + { + VDR *vdr=VDR::getInstance(); + if (vdr != NULL) + { + vdr->configSave("DirectShow","VideoFilter", + videofilterlist[videofilterselected].displayname); + } + return curfilter; + } + } + else + { + IBindCtx *bindctx=NULL; + if (CreateBindCtx(0,&bindctx)!=S_OK) return NULL; + IMoniker * moni=NULL; + LPCOLESTR name=new WCHAR[strlen(videofilterlist[videofilterselected].displayname)+1]; + mbstowcs((wchar_t*)name,videofilterlist[videofilterselected].displayname, + strlen(videofilterlist[videofilterselected].displayname)+1); + ULONG eater; + if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK) + { + if (moni->BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) + { + IAMDecoderCaps* desccaps=NULL; + if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK) + { + DWORD caps; + desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps); + if (caps == DECODER_CAP_SUPPORTED) + { + videofilterlist[videofilterselected].vmr9tested = true; + videofilterlist[videofilterselected].vmr9 = true; + } + else + { + videofilterlist[videofilterselected].vmr9tested = true; + videofilterlist[videofilterselected].vmr9 = false; + Log::getInstance()->log("VideoWin", Log::WARN ,"Filter does not support VMR9, but is selected, manual selection!"); + } + } + moni->Release(); + delete [] name; + bindctx->Release(); + return curfilter; + } + moni->Release(); + } + bindctx->Release(); + delete [] name; + return NULL; + } + return NULL; + +} + +IBaseFilter *VideoWin::getVideoH264Filter() +{ + IBaseFilter *curfilter= NULL; + if (videoH264filterselected == -1) + { + int i; + for (i = 0;i log("VideoWin", Log::DEBUG ,"Creating filter: %s",videoH264filterlist[i].friendlyname); + + + + if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK) + { + if (moni->BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) + { + IAMDecoderCaps* desccaps=NULL; + if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK) + { + DWORD caps; + desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps); + if (caps == DECODER_CAP_SUPPORTED) + { + videoH264filterlist[i].vmr9tested = true; + videoH264filterlist[i].vmr9 = true; + videoH264filterselected = i; + } + else + { + videoH264filterlist[i].vmr9tested = true; + videoH264filterlist[i].vmr9 = false; + + curfilter->Release(); + curfilter=NULL; + } + } + desccaps->Release(); + } + moni->Release(); + } + delete [] name; + bindctx->Release(); + } + if (videoH264filterlist[i].vmr9) break; + + } + if (curfilter != NULL) + { + VDR *vdr=VDR::getInstance(); + if (vdr != NULL) + { + vdr->configSave("DirectShow","VideoH264Filter", + videoH264filterlist[videoH264filterselected].displayname); + } + return curfilter; + } + } + else + { + IBindCtx *bindctx=NULL; + if (CreateBindCtx(0,&bindctx)!=S_OK) return NULL; + IMoniker * moni=NULL; + LPCOLESTR name=new WCHAR[strlen(videoH264filterlist[videoH264filterselected].displayname)+1]; + mbstowcs((wchar_t*)name,videoH264filterlist[videoH264filterselected].displayname, + strlen(videoH264filterlist[videoH264filterselected].displayname)+1); + ULONG eater; + Log::getInstance()->log("VideoWin", Log::DEBUG ,"Creating filter: %s",videoH264filterlist[videoH264filterselected].friendlyname); + if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK) + { + if (moni->BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) + { + IAMDecoderCaps* desccaps=NULL; + if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK) + { + DWORD caps; + desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps); + if (caps == DECODER_CAP_SUPPORTED) + { + videoH264filterlist[videoH264filterselected].vmr9tested = true; + videoH264filterlist[videoH264filterselected].vmr9 = true; + } + else + { + videoH264filterlist[videoH264filterselected].vmr9tested = true; + videoH264filterlist[videoH264filterselected].vmr9 = false; + Log::getInstance()->log("VideoWin", Log::WARN ,"Filter does not support VMR9, but is selected, manual selection!"); + } + } + moni->Release(); + delete [] name; + bindctx->Release(); + return curfilter; + } + moni->Release(); + } + bindctx->Release(); + delete [] name; + return NULL; + } + return NULL; + +} + + +#ifdef DS_DEBUG // This stuff would not included in vomp due to lincemse restrcitions +#include "dshelper.h" +#endif + +#define DO_VIDEO + +int VideoWin::play() +{ + if (!initted) return 0; + return 1; +} + +bool VideoWin::addOptionPagesToWTB(WTabBar *wtb) +{ + Boxx *box=new WWinVideoFilter(); + wtb->addTab(tr("Video Filter"), box); + box=new WWinVideoH264Filter(); + wtb->addTab(tr("H264 Filter"), box); + return true; +} + +const VideoFilterDescList *VideoWin::getVideoFilterList(int &selected) +{ + selected=videofilterselected; + return &videofilterlist; +} + +const VideoFilterDescList *VideoWin::getVideoH264FilterList(int &selected) +{ + selected=videoH264filterselected; + return &videoH264filterlist; +} + +bool VideoWin::selectVideoFilter(int filter) +{ + IBindCtx *bindctx=NULL; + if (CreateBindCtx(0,&bindctx)!=S_OK) return NULL; + IMoniker * moni=NULL; + LPCOLESTR name=new WCHAR[strlen(videofilterlist[filter].displayname)+1]; + mbstowcs((wchar_t*)name,videofilterlist[filter].displayname, + strlen(videofilterlist[filter].displayname)+1); + ULONG eater; + bool success=false; + if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK) + { + IBaseFilter* curfilter=NULL; + if (moni->BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) + { + IAMDecoderCaps* desccaps=NULL; + if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK) + { + DWORD caps; + HRESULT hres=desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps); + if (caps == DECODER_CAP_SUPPORTED) + { + videofilterlist[filter].vmr9tested = true; + videofilterlist[filter].vmr9 = true; + success=true; + } + else + { + videofilterlist[filter].vmr9tested = true; + videofilterlist[filter].vmr9 = false; + success=false; + } + desccaps->Release(); + } else { + videofilterlist[filter].vmr9tested = true; + videofilterlist[filter].vmr9 = false; + success=false; + } + + curfilter->Release(); + + } + moni->Release(); + } + bindctx->Release(); + delete [] name; + if (success || true) + { + videofilterselected=filter; + return true; + } + else + { + return false; + } +} + +bool VideoWin::selectVideoH264Filter(int filter) +{ + IBindCtx *bindctx=NULL; + if (CreateBindCtx(0,&bindctx)!=S_OK) return NULL; + IMoniker * moni=NULL; + LPCOLESTR name=new WCHAR[strlen(videoH264filterlist[filter].displayname)+1]; + mbstowcs((wchar_t*)name,videoH264filterlist[filter].displayname, + strlen(videoH264filterlist[filter].displayname)+1); + ULONG eater; + bool success=false; + if (MkParseDisplayName(bindctx,name,&eater,&moni)==S_OK) + { + IBaseFilter* curfilter=NULL; + if (moni->BindToObject(0,0,IID_IBaseFilter,(void**)&curfilter) == S_OK) + { + IAMDecoderCaps* desccaps=NULL; + if (curfilter->QueryInterface(IID_IAMDecoderCaps,(void**) &desccaps)==S_OK) + { + DWORD caps; + HRESULT hres=desccaps->GetDecoderCaps(AM_GETDECODERCAP_QUERY_VMR9_SUPPORT,&caps); + if (caps == DECODER_CAP_SUPPORTED) + { + videoH264filterlist[filter].vmr9tested = true; + videoH264filterlist[filter].vmr9 = true; + success=true; + } + else + { + videoH264filterlist[filter].vmr9tested = true; + videoH264filterlist[filter].vmr9 = false; + success=false; + } + desccaps->Release(); + } else { + videoH264filterlist[filter].vmr9tested = true; + videoH264filterlist[filter].vmr9 = false; + success=false; + } + + curfilter->Release(); + + } + moni->Release(); + } + bindctx->Release(); + delete [] name; + if (success || true) + { + videoH264filterselected=filter; + return true; + } + else + { + return false; + } +} + +int VideoWin::dsInitVideoFilter() +{ + #ifdef DO_VIDEO + HRESULT hres; + if (videoon) { + //We alloc the vmr9 as next step + if (currentpresenter==VMR9) + { + Log::getInstance()->log("VideoWin", Log::INFO ,"VMR9 Videopresenter selected!"); + if (hres=CoCreateInstance(CLSID_VideoMixingRenderer9,0, + CLSCTX_INPROC_SERVER,IID_IBaseFilter,(void**) &dsrenderer)!=S_OK) + { + Log::getInstance()->log("VideoWin", Log::WARN ,"Failed creating VMR9 renderer!"); + ReleaseMutex(filtermutex); + CleanupDS(); + } + /*VMR 9 stuff**/ + if (hres=dsgraphbuilder->AddFilter(dsrenderer,L"VMR9")!=S_OK) + { + ReleaseMutex(filtermutex); + CleanupDS(); + Log::getInstance()->log("VideoWin", Log::WARN ,"Failed adding VMR9 renderer!"); + return 0; + } + IVMRFilterConfig9* vmrfilconfig; + if (dsrenderer->QueryInterface(IID_IVMRFilterConfig9,(void**)&vmrfilconfig)!=S_OK) + { + ReleaseMutex(filtermutex); + CleanupDS(); + Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting VMR9 Filterconfig interface!"); + return 0; + } + if (vmrdeinterlacing!=0) vmrfilconfig->SetNumberOfStreams(1);//Enter Mixing Mode + vmrfilconfig->SetRenderingMode(VMR9Mode_Renderless); + vmrfilconfig->Release(); + if (dsrenderer->QueryInterface(IID_IVMRSurfaceAllocatorNotify9, + (void**)& dsvmrsurfnotify) != S_OK) + { + ReleaseMutex(filtermutex); + CleanupDS(); + Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting VMR9 Surface Allocator interface!"); + return 0; + } + allocatorvmr=new DsAllocator(); + dsvmrsurfnotify->AdviseSurfaceAllocator(NULL,allocatorvmr); + allocatorvmr->AdviseNotify(dsvmrsurfnotify); + + IVMRDeinterlaceControl9* deintctrl; + if (dsrenderer->QueryInterface(IID_IVMRDeinterlaceControl9,(void**)&deintctrl)!=S_OK) + { + ReleaseMutex(filtermutex); + CleanupDS(); + Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting VMR9 Deinterlace control!"); + return 0; + } + /*turnoff*/ + switch (vmrdeinterlacing) + { + case 1: //No Deinterlasing + deintctrl->SetDeinterlaceMode(0xFFFFFFFF,(LPGUID)&GUID_NULL);//Turn Off + break; + case 2: //Best + deintctrl->SetDeinterlacePrefs(DeinterlacePref_NextBest);//Choose Next Best + break; + case 3: //Bob + deintctrl->SetDeinterlacePrefs(DeinterlacePref_BOB);//Choose NBob + break; + case 4: //Weave + deintctrl->SetDeinterlacePrefs(DeinterlacePref_Weave);//Choose Weave + break; + }; + deintctrl->Release(); + /*VMR 9 stuff end */ + } + else if (currentpresenter==EVR) + { + Log::getInstance()->log("VideoWin", Log::INFO ,"EVR Videopresenter selected!"); + if (hres=CoCreateInstance(CLSID_EnhancedVideoRenderer,0, + CLSCTX_INPROC_SERVER,IID_IBaseFilter,(void**) &dsrenderer)!=S_OK) + { + Log::getInstance()->log("VideoWin", Log::WARN ,"Failed creating EVR renderer!"); + ReleaseMutex(filtermutex); + CleanupDS(); + } + /*EVR stuff**/ + if (hres=dsgraphbuilder->AddFilter(dsrenderer,L"EVR")!=S_OK) + { + ReleaseMutex(filtermutex); + CleanupDS(); + Log::getInstance()->log("VideoWin", Log::WARN ,"Failed adding EVR renderer!"); + return 0; + } + + + IMFGetService *evr_services; + if (dsrenderer->QueryInterface(IID_IMFGetService,(void**)&evr_services)!=S_OK) + { + ReleaseMutex(filtermutex); + CleanupDS(); + Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting EVR IMFGetServices interface!"); + return 0; + } + + IMFVideoDisplayControl* mfvideodisplaycontrol; + if (evr_services->GetService(MR_VIDEO_RENDER_SERVICE,IID_IMFVideoDisplayControl,(void**)&mfvideodisplaycontrol)!=S_OK) + { + ReleaseMutex(filtermutex); + CleanupDS(); + Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting EVR IMFVideoDisplayControl interface!"); + return 0; + } + + evr_services->Release(); + mfvideodisplaycontrol->SetVideoWindow(((OsdWin*) Osd::getInstance())->getWindow()); + //RECT client; + //GetClientRect(((OsdWin*) Osd::getInstance())->getWindow(), &client); + //mfvideodisplaycontrol->SetVideoPosition(NULL,&client); + + mfvideodisplaycontrol->Release(); + + + /// if (vmrdeinterlacing!=0) vmrfilconfig->SetNumberOfStreams(1);//Enter Mixing Mode //always the case for evr! + + IMFVideoRenderer *mfvideorenderer; + if (dsrenderer->QueryInterface(IID_IMFVideoRenderer,(void**)&mfvideorenderer)!=S_OK) + { + ReleaseMutex(filtermutex); + CleanupDS(); + Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting EVR IMFVideoRenderer interface!"); + return 0; + } + + allocatorvmr=new DsAllocator(); + HRESULT hres=mfvideorenderer->InitializeRenderer(NULL,allocatorvmr); + + mfvideorenderer->Release(); + //How should I do this in EVR? + /* IVMRDeinterlaceControl9* deintctrl; + if (dsrenderer->QueryInterface(IID_IVMRDeinterlaceControl9,(void**)&deintctrl)!=S_OK) + { + ReleaseMutex(filtermutex); + CleanupDS(); + Log::getInstance()->log("VideoWin", Log::WARN ,"Failed getting VMR9 Deinterlace control!"); + return 0; + } + /*turnoff* + switch (vmrdeinterlacing) + { + case 1: //No Deinterlasing + deintctrl->SetDeinterlaceMode(0xFFFFFFFF,(LPGUID)&GUID_NULL);//Turn Off + break; + case 2: //Best + deintctrl->SetDeinterlacePrefs(DeinterlacePref_NextBest);//Choose Next Best + break; + case 3: //Bob + deintctrl->SetDeinterlacePrefs(DeinterlacePref_BOB);//Choose NBob + break; + case 4: //Weave + deintctrl->SetDeinterlacePrefs(DeinterlacePref_Weave);//Choose Weave + break; + }; + deintctrl->Release();*/ + /*EVR stuff end */ + } else { + Log::getInstance()->log("VideoWin", Log::ERR ,"No videopresenter selected! Please post on the forum!"); + return -1; + } + IFilterGraph2*fg2=NULL; + if (dsgraphbuilder->QueryInterface(IID_IFilterGraph2,(void**)&fg2)!= S_OK) + { + Log::getInstance()->log("VideoWin", Log::WARN , "Failed querying for FilterGraph2 Interface!"); + ReleaseMutex(filtermutex); + CleanupDS(); + return 0; + } +/*#ifndef NEW_DS_MECHANISMENS + + if (hres=fg2->RenderEx((IPin*)sourcefilter->GetVideoPin()/*video*, + AM_RENDEREX_RENDERTOEXISTINGRENDERERS,NULL) != S_OK) + { + Log::getInstance()->log("VideoWin", Log::WARN , "Failed rendering Video!"); + fg2->Release(); + ReleaseMutex(filtermutex); + CleanupDS(); + return 0; + } + +#else*/ + IBaseFilter*videofilter; + if (h264) + { + Log::getInstance()->log("VideoWin", Log::DEBUG ,"Entering h264 playback..."); + videofilter=getVideoH264Filter(); + } + else + { + Log::getInstance()->log("VideoWin", Log::DEBUG ,"Entering MPEG2 playback..."); + videofilter=getVideoFilter(); + } + if (hres=dsgraphbuilder->AddFilter(videofilter,NULL) != S_OK) + { + Log::getInstance()->log("VideoWin", Log::WARN , "Failed adding Video Filter!"); + ReleaseMutex(filtermutex); + CleanupDS(); + return 0; + } + IEnumPins *pinenum=NULL; + bool error=false; + + mptype_video_detail vid_details; + Demuxer* demux=Demuxer::getInstance(); + vid_details.width=demux->getHorizontalSize(); + vid_details.height=demux->getVerticalSize(); + + if (h264) + { + if (vid_details.width!=0 && vid_details.height!=0) + { + sourcefilter->GetVideoPin()->SetPinMode(MPTYPE_VIDEO_H264,&vid_details); + } + else + { + sourcefilter->GetVideoPin()->SetPinMode(MPTYPE_VIDEO_H264,NULL); + } + + } + else + { + if (vid_details.width!=0 && vid_details.height!=0) + { + sourcefilter->GetVideoPin()->SetPinMode(MPTYPE_VIDEO_MPEG2,&vid_details); + } + else + { + sourcefilter->GetVideoPin()->SetPinMode(MPTYPE_VIDEO_MPEG2,NULL); + } + } + if (videofilter->EnumPins(&pinenum) == S_OK) + { + IPin *current=NULL; + ULONG fetch=0; + bool firststep=false; + + while (pinenum->Next(1,¤t,&fetch)==S_OK) + { + PIN_DIRECTION dir; + if (current->QueryDirection(&dir)==S_OK) + { + if (dir == PINDIR_INPUT) + { + if (sourcefilter->GetVideoPin()->Connect(current,NULL)==S_OK) + { + current->Release(); + firststep=true; + break; + } + } + } + current->Release(); + } + if (firststep==false) + { + Log::getInstance()->log("VideoWin", Log::WARN , "Video Filter has no suitable input!"); + videofilter->Release(); + ReleaseMutex(filtermutex); + CleanupDS(); + return 0; + } + bool secondstep=false; + pinenum->Reset(); + while (pinenum->Next(1,¤t,&fetch)==S_OK) + { + PIN_DIRECTION dir; + if (current->QueryDirection(&dir)==S_OK) + { + if (dir == PINDIR_OUTPUT) + { + + if (fg2->RenderEx((IPin*)current/*video*/, + AM_RENDEREX_RENDERTOEXISTINGRENDERERS,NULL) ==S_OK) + { + current->Release(); + secondstep=true; + break; + } + } + } + current->Release(); + } + if (secondstep==false) + { + Log::getInstance()->log("VideoWin", Log::WARN , "Video Filter has no suitable output!"); + videofilter->Release(); + ReleaseMutex(filtermutex); + CleanupDS(); + return 0; + } + + videofilter->Release(); + pinenum->Release(); + + } + + + + fg2->Release(); + return 1; + } +#endif + return 1; +} + +int VideoWin::setAudioStreamType(UCHAR type) +{ + aud_type=type; + if (!initted) return 0; + return 1; +} + +int VideoWin::dsplay() +{ + if (!initted) return 0; + CleanupDS(); + + //Build filter graph + HRESULT hres; + //So this is the real code, this prevents the feeder from calling noexisting objects! + WaitForSingleObject(filtermutex,INFINITE); + if (hres=CoCreateInstance(CLSID_FilterGraph,NULL,CLSCTX_INPROC_SERVER, + IID_IGraphBuilder,(void**)&dsgraphbuilder) != S_OK) + { + ReleaseMutex(filtermutex); + return 0; + } + #ifdef DS_DEBUG + AddToRot(dsgraphbuilder,&graphidentifier); + #endif + + firstsynched=false; + if (aud_type==Audio::MP3) { + lastaudiomode=MPTYPE_MPEG_AUDIO_LAYER3; + } else { + lastaudiomode=MPTYPE_MPEG_AUDIO; + } + //lastaudiomode=MPTYPE_AC3; + sourcefilter=new DsSourceFilter(); //Creating our Source filter for pushing Data + // to DirectShow + if (hres=dsgraphbuilder->AddFilter(sourcefilter,L"Vomp Win Source Filter") != S_OK) + { + Log::getInstance()->log("VideoWin", Log::WARN , "Failed adding Vomp Source Filter!"); + ReleaseMutex(filtermutex); + CleanupDS(); + return 0; + } + sourcefilter->GetAudioPin()->SetPinMode(lastaudiomode); + /*if (hres=dsgraphbuilder->Render((IPin*)sourcefilter->GetAudioPin()/*audio*)!=S_OK) + { + Log::getInstance()->log("VideoWin", Log::WARN , "Failed rendering audio!"); + ReleaseMutex(filtermutex); + CleanupDS(); + return 0; + }*/ + if (((AudioWin*)Audio::getInstance())->dsInitAudioFilter(dsgraphbuilder)==0) + { + Log::getInstance()->log("VideoWin", Log::WARN , "Failed rendering audio!"); + ReleaseMutex(filtermutex); + CleanupDS(); + return 0; + } + + + if (dsInitVideoFilter()==0) + { + return 0; + } + + if (hres=CoCreateInstance(CLSID_SystemClock,NULL,CLSCTX_INPROC_SERVER, + IID_IReferenceClock,(void**)&dsrefclock)!=S_OK) + { + return 0; + } + dsgraphbuilder->QueryInterface(IID_IMediaFilter,(void **) &dsmediafilter); + HRESULT hresdeb = dsmediafilter->SetSyncSource(dsrefclock); + + dsgraphbuilder->QueryInterface(IID_IMediaControl,(void **) &dsmediacontrol); + dsgraphbuilder->QueryInterface(IID_IBasicAudio,(void **) &dsbasicaudio); + if (dsbasicaudio) + dsbasicaudio->put_Volume(audiovolume); + dsinited=true; + //MILLISLEEP(100); + + hresdeb=dsmediacontrol->Run(); + iframemode=false;//exit iframe mode + ReleaseMutex(filtermutex); + return 1; +} + +int VideoWin::EnterIframePlayback() +{ + if (!initted) return 0; + CleanupDS(); + //So this is the real code, this prevents the feeder from calling noexisting objects! + WaitForSingleObject(filtermutex,INFINITE); + iframemode=true;//enter iframe mode + //Build filter graph + HRESULT hres; + if (hres=CoCreateInstance(CLSID_FilterGraph,NULL,CLSCTX_INPROC_SERVER, + IID_IGraphBuilder,(void**)&dsgraphbuilder)!=S_OK) { + ReleaseMutex(filtermutex); + return 0; + } +#ifdef DS_DEBUG + AddToRot(dsgraphbuilder,&graphidentifier); +#endif + + //firstsynched=false; + sourcefilter=new DsSourceFilter(); //Creating our Source filter for pushing Data + // to DirectShow + if (hres=dsgraphbuilder->AddFilter(sourcefilter,L"Vomp Win Source Filter")!=S_OK) { + Log::getInstance()->log("VideoWin", Log::WARN , "Failed adding Vomp Source Filter!"); + ReleaseMutex(filtermutex); + CleanupDS(); + return 0; + } +#ifdef DO_VIDEO + if (videoon) { + dsInitVideoFilter(); + } +#endif +/* if (hres=CoCreateInstance(CLSID_SystemClock,NULL,CLSCTX_INPROC_SERVER, + IID_IReferenceClock,(void**)&dsrefclock)!=S_OK) { + return 0; + }*/ + + dsgraphbuilder->QueryInterface(IID_IMediaFilter,(void **) &dsmediafilter); + dsmediafilter->SetSyncSource(/*dsrefclock*/NULL); //Run as fast as you can! + + dsgraphbuilder->QueryInterface(IID_IMediaControl,(void **) &dsmediacontrol); + dsgraphbuilder->QueryInterface(IID_IBasicAudio,(void **) &dsbasicaudio); + dsinited=true; + + + dsmediacontrol->Run(); + ReleaseMutex(filtermutex); + return 1; + +} + +int VideoWin::dsstop() +{ + if (!initted) return 0; + + CleanupDS(); + + + return 1; +} + +int VideoWin::stop() +{ + if (!initted) return 0; + + + return 1; +} + +int VideoWin::reset() +{ + if (!initted) return 0; + + + return 1; +} + +int VideoWin::dsreset() +{ + if (!initted) return 0; + videoposx=0; + videoposy=0; + iframemode=false;//exit iframe mode + CleanupDS(); + + return 1; +} + +int VideoWin::dspause() +{ + if (!initted) return 0; + WaitForSingleObject(filtermutex,INFINITE); + if (dsmediacontrol) dsmediacontrol->Pause(); + ReleaseMutex(filtermutex); + return 1; +} + +int VideoWin::pause() +{ + if (!initted) return 0; + + return 1; +} + +int VideoWin::unPause() // FIXME get rid - same as play!! +{//No on windows this is not the same, I don't get rid of! + if (!initted) return 0; + return 1; +} + +int VideoWin::dsunPause() // FIXME get rid - same as play!! +{//No on windows this is not the same, I don't get rid of! + if (!initted) return 0; + WaitForSingleObject(filtermutex,INFINITE); + if (dsmediacontrol) dsmediacontrol->Run(); + ReleaseMutex(filtermutex); + + return 1; +} + +int VideoWin::fastForward() +{ + if (!initted) return 0; + + return 1; +} + +int VideoWin::unFastForward() +{ + if (!initted) return 0; + + return 1; +} + +int VideoWin::attachFrameBuffer() +{ + if (!initted) return 0; + return 1; +} + +int VideoWin::blank(void) +{ + ((OsdWin*)Osd::getInstance())->Blank(); + return 1; +} + +ULLONG VideoWin::getCurrentTimestamp() +{ + REFERENCE_TIME startoffset; + REFERENCE_TIME ncr_time; + if (iframemode) return 0; //Not in iframe mode! + if (!dsrefclock || !sourcefilter) return 0; + FILTER_STATE state; + sourcefilter->GetState(10,&state); + + if (state==State_Running) dsrefclock->GetTime(&cr_time); + ncr_time=cr_time; + startoffset=sourcefilter->getStartOffset(); + if (startoffset==0) return 0; + ncr_time-=startoffset; + ncr_time-=lastreftimeRT; + /* ULLONG result=frameNumberToTimecode( + VDR::getInstance()->frameNumberFromPosition(lastreftimeBYTE));*/ + long long result=lastreftimePTS; + result+=(ULLONG)(ncr_time/10000LL*90LL); + if (result<0) result=(1LL << 33)-result; + return result; + +} + + +/* //to beremoved +ULONG VideoWin::timecodeToFrameNumber(ULLONG timecode) +{ + if (format == PAL) return (ULONG)(((double)timecode / (double)90000) * (double)25); + else return (ULONG)(((double)timecode / (double)90000) * (double)30); +} + +ULLONG VideoWin::frameNumberToTimecode(ULONG framenumber) +{ + if (format == PAL) return (ULLONG)(((double)framenumber * (double)90000) / (double)25); + else return (ULLONG)(((double)framenumber * (double)90000) / (double)30); +} +*/ +void VideoWin::CleanupDS() +{ + WaitForSingleObject(filtermutex,INFINITE); + dsinited=false; + if (dsmediacontrol)dsmediacontrol->Stop(); + if (cur_audio_media_sample) { + cur_audio_media_sample->Release(); + cur_audio_media_sample=NULL; + } + if (cur_video_media_sample) { + cur_video_media_sample->Release(); + cur_video_media_sample=NULL; + } + if (dsbasicaudio) { + dsbasicaudio->Release(); + dsbasicaudio=NULL; + } + if (dsvmrsurfnotify) { + dsvmrsurfnotify->Release(); + dsvmrsurfnotify=NULL; + } + if (dsrenderer) { + dsrenderer->Release(); + dsrenderer=NULL; + } + + if (allocatorvmr) { + allocatorvmr->Release(); + allocatorvmr=NULL; + } + + if (dsrefclock) { + dsrefclock->Release(); + dsrefclock=NULL; + } + if (dsmediafilter) { + dsmediafilter->Release(); + dsmediafilter=NULL; + } + + + + if (dsmediacontrol) { + dsmediacontrol->Stop(); + dsmediacontrol->Release(); + dsmediacontrol=NULL; + } + if (dsgraphbuilder){ +#ifdef DS_DEBUG + RemoveFromRot(graphidentifier); +#endif + dsgraphbuilder->Release(); + dsgraphbuilder=NULL; + + sourcefilter=NULL; //The Graph Builder destroys our SourceFilter + } + ReleaseMutex(filtermutex); + +} + +void VideoWin::PrepareMediaSample(const MediaPacketList& mplist, + UINT samplepos) +{ + mediapacket = mplist.front(); +} + +UINT VideoWin::DeliverMediaSample(UCHAR* buffer, UINT *samplepos) +{ + DeliverMediaPacket(mediapacket, buffer, samplepos); + if (*samplepos == mediapacket.length) { + *samplepos = 0; + return 1; + } + else return 0; +} + +UINT VideoWin::DeliverMediaPacket(MediaPacket packet, + const UCHAR* buffer, + UINT *samplepos) +{ + + /*First Check, if we have an audio sample*/ + if (!isdsinited()) return 0; + if (packet.type == MPTYPE_VIDEO_H264) + { + h264=true; + } + else + { + h264=false; + } + +#ifdef DO_VIDEO + if (!videoon) { + *samplepos+=packet.length; + MILLISLEEP(0); //yet not implemented//bad idea + return packet.length; + } + /*First Check, if we have an audio sample*/ + if (iframemode) { + //samplepos=0; + MILLISLEEP(10); + return 0; //Not in iframe mode! + } + IMediaSample* ms=NULL; + REFERENCE_TIME reftime1=0; + REFERENCE_TIME reftime2=0; + + UINT headerstrip=0; + if (packet.disconti) { + firstsynched=false; + DeliverVideoMediaSample(); + } + + + + /*Inspect PES-Header */ + + if (*samplepos==0) {//stripheader + headerstrip=buffer[packet.pos_buffer+8]+9/*is this right*/; + *samplepos+=headerstrip; + if ( packet.synched ) { + DeliverVideoMediaSample();//write out old data + /* if (packet.presentation_time<0) { //Preroll? + *samplepos=packet.length;//if we have not processed at least one + return packet.length;//synched packet ignore it! + }*/ + + reftime1=packet.presentation_time; + reftime2=reftime1+1; + firstsynched=true; + } else { + if (!firstsynched) {// + *samplepos=packet.length;//if we have not processed at least one + return packet.length;//synched packet ignore it! + } + } + } + BYTE *ms_buf; + UINT ms_length; + UINT ms_pos; + UINT haveToCopy; + + if (!getCurrentVideoMediaSample(&ms) || ms==NULL) {// get the current sample + //samplepos=0; + //MessageBox(0,"da isser","hei",0); + //MILLISLEEP(1); + return 0; + } + ms_pos=ms->GetActualDataLength(); + ms_length=ms->GetSize(); + haveToCopy=min(ms_length-ms_pos,packet.length-*samplepos); + if ((ms_length-ms_pos)<1 ) { + DeliverVideoMediaSample(); //we are full! + if (!getCurrentVideoMediaSample(&ms) || ms==NULL) {// get the current sample + //samplepos=0; + //MessageBox(0,"da isser","hei",0); + //MILLISLEEP(10); + return 0; + } + ms_pos=ms->GetActualDataLength(); + ms_length=ms->GetSize(); + haveToCopy=min(ms_length-ms_pos,packet.length-*samplepos); + } + ms->GetPointer(&ms_buf); + + + if (ms_pos==0) {//will only be changed on first packet + if (packet.disconti) { + ms->SetDiscontinuity(TRUE); + } else { + ms->SetDiscontinuity(FALSE); + } + if (packet.synched) { + ms->SetSyncPoint(TRUE); + ms->SetTime(&reftime1,&reftime2); + //Log::getInstance()->log("VideoWin", Log::DEBUG , "Setted videotime to %lld %lld",reftime1,reftime2); + //Log::getInstance()->log("VideoWin", Log::DEBUG , "Packet pts %lld dts %lld",packet.pts,packet.dts); + //ms->SetTime(NULL,NULL); + ms->SetMediaTime(NULL, NULL); + if (reftime1<0) ms->SetPreroll(TRUE); + else ms->SetPreroll(FALSE); + /*Timecode handling*/ + lastreftimeRT=reftime1; + lastreftimePTS=packet.pts; + + } + else + { + ms->SetSyncPoint(FALSE); + ms->SetTime(NULL,NULL); + ms->SetMediaTime(NULL, NULL); + ms->SetPreroll(FALSE); + + // ms->SetSyncPoint(TRUE); + } + } + + + + memcpy(ms_buf+ms_pos,buffer+packet.pos_buffer+*samplepos,haveToCopy); + ms->SetActualDataLength(haveToCopy+ms_pos); + + *samplepos+=haveToCopy; + + return haveToCopy+headerstrip; + +#else + + *samplepos+=packet.length; + MILLISLEEP(0); //yet not implemented//bad idea + return packet.length; +#endif +} + +int VideoWin::getCurrentAudioMediaSample(IMediaSample** ms) +{ + //WaitForSingleObject(filtermutex,INFINITE); + if (!sourcefilter){ + // ReleaseMutex(filtermutex); + return 0; + } + if (cur_audio_media_sample) { + *ms=cur_audio_media_sample;//already open + return 1; + } + if (!sourcefilter->getCurrentAudioMediaSample(ms)) { + // ReleaseMutex(filtermutex); + } + if (*ms) (*ms)->SetActualDataLength(0); + cur_audio_media_sample=*ms; + //Don't release the mutex before deliver + return 1; +} + +int VideoWin::getCurrentVideoMediaSample(IMediaSample** ms) +{ + //WaitForSingleObject(filtermutex,INFINITE); + if (!sourcefilter){ + // ReleaseMutex(filtermutex); + return 0; + } + if (cur_video_media_sample) { + *ms=cur_video_media_sample;//already open + return 1; + } + if (!sourcefilter->getCurrentVideoMediaSample(ms)) { + // ReleaseMutex(filtermutex); + } + if (*ms) (*ms)->SetActualDataLength(0); + + cur_video_media_sample=*ms; + //Don't release the mutex before deliver + return 1; +} + +int VideoWin::DeliverAudioMediaSample(){ + if (cur_audio_media_sample) { + sourcefilter->DeliverAudioMediaSample(cur_audio_media_sample); + cur_audio_media_sample=NULL; + } + //ReleaseMutex(filtermutex); + return 1; +} + +int VideoWin::DeliverVideoMediaSample(){ + if (cur_video_media_sample) { + sourcefilter->DeliverVideoMediaSample(cur_video_media_sample); + cur_video_media_sample=NULL; + } + //ReleaseMutex(filtermutex); + return 1; +} + +long long VideoWin::SetStartOffset(long long curreftime, bool *rsync) +{ + *rsync=false; + if (offsetnotset) { + startoffset=curreftime;//offset is set for audio + offsetnotset=false; + offsetvideonotset=false; + + + } else { + if (offsetvideonotset) { + offsetvideonotset=false; + *rsync=true; + } else { + if ( (curreftime-lastrefvideotime)>10000000LL + || (curreftime-lastrefvideotime)<-10000000LL) {//if pts jumps to big resync + startoffset+=curreftime-lastrefvideotime; + lastrefaudiotime+=curreftime-lastrefvideotime; + //*rsync=true; + offsetaudionotset=true; + + } + } + + } + + lastrefvideotime=curreftime; + + return startoffset; + +} + +long long VideoWin::SetStartAudioOffset(long long curreftime, bool *rsync) +{ + *rsync=false; + if (offsetnotset) { + startoffset=curreftime; + offsetnotset=false; + offsetaudionotset=false; + }else { + if (offsetaudionotset) { + offsetaudionotset=false; + *rsync=true; + } else { + if ( (curreftime-lastrefaudiotime)>10000000LL + || (curreftime-lastrefaudiotime)<-10000000LL) {//if pts jumps to big resync + startoffset+=curreftime-lastrefaudiotime; + lastrefvideotime+=curreftime-lastrefaudiotime; + //*rsync=true; + offsetvideonotset=true; + + } + } + + } + lastrefaudiotime=curreftime; + return startoffset; + +} +void VideoWin::ResetTimeOffsets() { + offsetnotset=true; //called from demuxer + offsetvideonotset=true; + offsetaudionotset=true; + startoffset=0; + lastrefaudiotime=0; + lastrefvideotime=0; + lastreftimeRT=0; + lastreftimePTS=0; + + +} + +void VideoWin::SetAudioVolume(long volume) +{ + audiovolume=volume; + if (dsbasicaudio) dsbasicaudio->put_Volume(volume); +} + +bool VideoWin::displayIFrame(const UCHAR* buffer, UINT length) +{ + if (!iframemode) EnterIframePlayback(); + if (!isdsinited()) return false; + +#ifdef DO_VIDEO + IMediaSample* ms=NULL; + REFERENCE_TIME reftime1=0; + REFERENCE_TIME reftime2=0; + if (!videoon) return; + if (!getCurrentVideoMediaSample(&ms) || ms==NULL) {// get the current sample + MILLISLEEP(10); + return ; + } + BYTE *ms_buf; + DWORD ms_length; + ms->GetPointer(&ms_buf); + ms_length=ms->GetSize(); + + /*First Check, if we have an video sample*/ + DWORD read_pos = 0, write_pos = 0; + DWORD pattern, packet_length; + DWORD headerstrip=0; + bool first=true; + if (length < 4) return ; + //Now we strip the pes header + pattern = (buffer[0] << 16) | (buffer[1] << 8) | (buffer[2]); + while (read_pos + 7 <= length) + { + pattern = ((pattern << 8) & 0xFFFFFFFF) | buffer[read_pos+3]; + if (pattern < 0x000001E0 || pattern > 0x000001EF) { + read_pos++; + continue; + } + else + { + headerstrip=buffer[read_pos+8]+9/*is this right*/; + packet_length = ((buffer[read_pos+4] << 8) | (buffer[read_pos+5])) + 6; + if (read_pos + packet_length > length) + read_pos = length; + else + { + if ( (headerstrip < packet_length) && + (write_pos+packet_length-headerstrip)>ms_length) { + if (first) { + ms->SetSyncPoint(TRUE); + ms->SetDiscontinuity(TRUE); + first=false; + } else ms->SetSyncPoint(FALSE); + ms->SetTime(NULL,NULL); + ms->SetMediaTime(NULL, NULL); + ms->SetActualDataLength(write_pos); + DeliverVideoMediaSample(); + + if (!getCurrentVideoMediaSample(&ms) || ms==NULL) {// get the current sample + MILLISLEEP(10); + return ; + } + write_pos=0; + ms_length=ms->GetSize(); + ms->GetPointer(&ms_buf); + } + if (packet_length>headerstrip) { + memcpy(ms_buf+write_pos, buffer+read_pos+headerstrip, packet_length-headerstrip); + write_pos += packet_length-headerstrip; + } + read_pos += packet_length; + + pattern = (buffer[read_pos] << 16) | (buffer[read_pos+1] << 8) + | (buffer[read_pos+2]); + } + } + } + + if (first) {ms->SetSyncPoint(TRUE); + ms->SetDiscontinuity(TRUE); + first=false;} + else ms->SetSyncPoint(FALSE); + ms->SetTime(NULL,NULL); + ms->SetMediaTime(NULL, NULL); + ms->SetActualDataLength(write_pos); + DeliverVideoMediaSample(); + + return true; +#else + + // *samplepos+=packet.length; + MILLISLEEP(0); //yet not implemented//bad idea + return true; +#endif +} + +bool VideoWin::supportsAc3(){ + if (sourcefilter != NULL) { + return sourcefilter->supportsAc3(); + } else { + return false; + } +} + +bool VideoWin::supportsh264() +{ + if (videoH264filterlist.size()>0) return true; + else return false; +} + + +bool VideoWin::changeAType(int type,IMediaSample* ms){ + if (sourcefilter!= NULL) { + lastaudiomode=type; + return sourcefilter->changeAType(type,ms); + } + else + { + return false; + } +} + +#ifdef DEV +int VideoWin::test() +{ + return 0; +} + +int VideoWin::test2() +{ + return 0; +} +#endif + + + + diff --git a/videowin.h b/videowin.h index 4293d21..3300433 100644 --- a/videowin.h +++ b/videowin.h @@ -1,228 +1,228 @@ -/* - Copyright 2004-2005 Chris Tallon - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#ifndef VIDEOWIN_H -#define VIDEOWIN_H - -#include -#include -#include -#include -#include -#include -#include - -#include "defines.h" -#include "video.h" - -//#define DS_DEBUG -#define NEW_DS_MECHANISMENS - -#ifdef NEW_DS_MECHANISMENS -struct VideoFilterDesc { - char * displayname; - char * friendlyname; - bool vmr9; - bool vmr9tested; -}; -using namespace std; -typedef vector VideoFilterDescList; -#endif - -class DsSourceFilter; -class DsAllocator; - -class VideoWin : public Video -{ -public: - VideoWin(); - virtual ~VideoWin(); - - int init(UCHAR format); - int shutdown(); - - int setFormat(UCHAR format); - 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 - - - +/* + Copyright 2004-2005 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef VIDEOWIN_H +#define VIDEOWIN_H + +#include +#include +#include +#include +#include +#include +#include + +#include "defines.h" +#include "video.h" + +//#define DS_DEBUG +#define NEW_DS_MECHANISMENS + +#ifdef NEW_DS_MECHANISMENS +struct VideoFilterDesc { + char * displayname; + char * friendlyname; + bool vmr9; + bool vmr9tested; +}; +using namespace std; +typedef vector VideoFilterDescList; +#endif + +class DsSourceFilter; +class DsAllocator; + +class VideoWin : public Video +{ +public: + VideoWin(); + virtual ~VideoWin(); + + int init(UCHAR format); + int shutdown(); + + int setFormat(UCHAR format); + 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 + + + diff --git a/vmediaview.cc b/vmediaview.cc index 33e4432..3745f7f 100644 --- a/vmediaview.cc +++ b/vmediaview.cc @@ -1,1457 +1,1457 @@ -/* - Copyright 2004-2005 Chris Tallon, Andreas Vogel - - This file is part of VOMP. - - VOMP is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - VOMP is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with VOMP; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#include - -#include "vmediaview.h" - -#include "vpicturebanner.h" -#include "vcolourtuner.h" -#include "audioplayer.h" -#include "timers.h" -#include "boxx.h" -#include "wselectlist.h" -#include "remote.h" -#include "wsymbol.h" -#include "boxstack.h" -#include "vdr.h" -#include "media.h" -#include "video.h" -#include "vinfo.h" -#include "i18n.h" -#include "message.h" -#include "command.h" -#include "mediaoptions.h" -#include "mediaplayer.h" -#include "log.h" - -const int VMediaView::EVENT_SLIDESHOW=100; -const int VMediaView::EVENT_DRAWINGDONE=101; -const int VMediaView::EVENT_DRAWINGERROR=102; -const int VMediaView::EVENT_DIRECTORYDONE=103; - - -/** - * the combined user interface for pictures and audio - * has 2 surfaces to enable drawing in a separate thread - * the info display can either show a picture info or and audio info - * if the audio player comes on top it disables the picture info display - * basically we have 3 modes: - * - HIDDEN viewer is hidden (pictureEnabled=false, audioEnabled=false) - * if justPlaying=true the audioplayer is still running - * - PICTURE picture viewer on top ("slide show") - pictureEnabled=true, audioEnabled=false - * no AudioBanner visible - * if justPlaying=true the audio player runs in bg - * - AUDIO audioPlayer on top ("playlist mode") - pictureEnabled=true, audioEnabled=true - * the picture viewer is currently halted (should be able to continue if no AudioInfo) - * no picture banner (we should have an audio banner to indicate the audio player on top!) - * state transitions (via setPictureMode, setAudioMode) - * - show Picture -> pictureEnabled=true, - * only called from command handler if audioEnabled=false - * call from mediaList only possible if audioEnabled=false - * *** TODO: would be better to have separate function for external call/internal call - * - play [Audio] -> if activate is set -> audioEnabled=true (-> Mode AUDIO) - * used for calls from medialist - * internal calls do not set activate! - * - YELLOW key - toggle - * if audioActive==true -> audioActive=false (new mode depends on pictureEnabled - either HIDDEN or PICTURE) - * if audioActive==false -> audioActive=true (new mode AUDIO) - * *** open handling if no audio file there (ignore key???) - currently empty player - * - BACK if mode AUDIO -> audioEnabled=false, justPlaying=false - * - BACK if mode PICTURE -> pictureEnabled=false; - * - player StreamEnd -> audioEnabled=false (new mode depends on pciture - either HIDDEN or PICTURE) - * info/banner handling: - * - AudioInfo - alternativ to pictureInfo - * off when disabling audio or after timer or with OK in AUDIO mode - * on currently any command or player event (end/new song) when audioEnabled==true and retriggerAudioInfo=true - * on on OK in AUDIO mode - * - Picture Info - * off when disabling Picture or after timer, OK, green - * on only on green when pictureEnabled==true - * - PictureBanner - * 2 modes: loading/normal - * normal: - * off when disabling picture, OK, after timer - * on when enabling picture, with OK - * loading: - * off when disabling picture and when loading done - * on on start loading - * *** open: do not show when audio enabled and slide show running (currently slideshow is paused) - * - AudioBanner - * always shown when audioEnabled=true - * on when enabling Audio - * off when disabling audio - * update in play - * timers: 1 - slide show - * 2 - pictureBanner - * 3 - info showtime - * 4 - audio short update - */ - -#define DRAWING_THREAD - -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;igetScreenHeight(); - UINT vwidth=Video::getInstance()->getScreenWidth(); - if (height > vheight-2*AUDIOBANNER_BOTTOM_MARGIN) - height=vheight-2*AUDIOBANNER_BOTTOM_MARGIN; - info->setSize(vwidth -2*AUDIOBANNER_X_MARGIN, height); - info->createBuffer(); - if (Video::getInstance()->getFormat() == Video::PAL) - { - info->setPosition(AUDIOBANNER_X_MARGIN, vheight-height-AUDIOBANNER_BOTTOM_MARGIN); - } - else - { - info->setPosition(AUDIOBANNER_X_MARGIN, vheight-height-AUDIOBANNER_BOTTOM_MARGIN); - } - - info->setTitleBarOn(0); - info->setDropThrough(); - //from vvideorec - //set the regions for the closcks and bars on banner - barRegion.x = info->getWidth()-AUDIOBARLEN-20; - barRegion.y = info->getHeight() - 30; // FIXME, need to be - 1? and below? - barRegion.w = info->getWidth()-AUDIOBARLEN+10; - barRegion.h = 30; - - clocksRegion.x = 130; - clocksRegion.y = barRegion.y + 3; - clocksRegion.w = 190; - clocksRegion.h = getFontHeight(); - Log::getInstance()->log("VMediaView",Log::DEBUG,"created AudioInfo %p",info); - BoxStack::getInstance()->add(info); - char *buf=new char [len]; - if (playerError || audioError) { - SNPRINTF(buf,len,"%s",first); - } - else { - char tbuf[Media::TIMEBUFLEN]; - if (playall) { - SNPRINTF(buf,len,"%s\n" - "%s:%s\n" - "%s: %d/%d\n" - "%s:%s\n" - "%s:%s\n", - first, - tr("FileName"),currentAudio->getDisplayName(), - pl,num,parent->getNumEntries(MEDIA_TYPE_AUDIO), - tr("Directory"),parent->getDirname(MEDIA_TYPE_AUDIO), - tr("Time"),currentAudio->getTimeString(tbuf)); - } - else { - SNPRINTF(buf,len,"%s\n" - "%s:%s\n" - "%s:%s\n" - "%s:%s\n", - first, - tr("FileName"),currentAudio->getDisplayName(), - tr("Directory"),parent->getDirname(MEDIA_TYPE_AUDIO), - tr("Time"),currentAudio->getTimeString(tbuf)); - } - } - Log::getInstance()->log("VMediaView",Log::DEBUG,"info: (%d)%s",strlen(buf),buf); - //now the real drawing functions - info->draw(); - //title - info->rectangle(0, 0, info->getWidth(), 30, 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; - } -} +/* + Copyright 2004-2005 Chris Tallon, Andreas Vogel + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include + +#include "vmediaview.h" + +#include "vpicturebanner.h" +#include "vcolourtuner.h" +#include "audioplayer.h" +#include "timers.h" +#include "boxx.h" +#include "wselectlist.h" +#include "remote.h" +#include "wsymbol.h" +#include "boxstack.h" +#include "vdr.h" +#include "media.h" +#include "video.h" +#include "vinfo.h" +#include "i18n.h" +#include "message.h" +#include "command.h" +#include "mediaoptions.h" +#include "mediaplayer.h" +#include "log.h" + +const int VMediaView::EVENT_SLIDESHOW=100; +const int VMediaView::EVENT_DRAWINGDONE=101; +const int VMediaView::EVENT_DRAWINGERROR=102; +const int VMediaView::EVENT_DIRECTORYDONE=103; + + +/** + * the combined user interface for pictures and audio + * has 2 surfaces to enable drawing in a separate thread + * the info display can either show a picture info or and audio info + * if the audio player comes on top it disables the picture info display + * basically we have 3 modes: + * - HIDDEN viewer is hidden (pictureEnabled=false, audioEnabled=false) + * if justPlaying=true the audioplayer is still running + * - PICTURE picture viewer on top ("slide show") - pictureEnabled=true, audioEnabled=false + * no AudioBanner visible + * if justPlaying=true the audio player runs in bg + * - AUDIO audioPlayer on top ("playlist mode") - pictureEnabled=true, audioEnabled=true + * the picture viewer is currently halted (should be able to continue if no AudioInfo) + * no picture banner (we should have an audio banner to indicate the audio player on top!) + * state transitions (via setPictureMode, setAudioMode) + * - show Picture -> pictureEnabled=true, + * only called from command handler if audioEnabled=false + * call from mediaList only possible if audioEnabled=false + * *** TODO: would be better to have separate function for external call/internal call + * - play [Audio] -> if activate is set -> audioEnabled=true (-> Mode AUDIO) + * used for calls from medialist + * internal calls do not set activate! + * - YELLOW key - toggle + * if audioActive==true -> audioActive=false (new mode depends on pictureEnabled - either HIDDEN or PICTURE) + * if audioActive==false -> audioActive=true (new mode AUDIO) + * *** open handling if no audio file there (ignore key???) - currently empty player + * - BACK if mode AUDIO -> audioEnabled=false, justPlaying=false + * - BACK if mode PICTURE -> pictureEnabled=false; + * - player StreamEnd -> audioEnabled=false (new mode depends on pciture - either HIDDEN or PICTURE) + * info/banner handling: + * - AudioInfo - alternativ to pictureInfo + * off when disabling audio or after timer or with OK in AUDIO mode + * on currently any command or player event (end/new song) when audioEnabled==true and retriggerAudioInfo=true + * on on OK in AUDIO mode + * - Picture Info + * off when disabling Picture or after timer, OK, green + * on only on green when pictureEnabled==true + * - PictureBanner + * 2 modes: loading/normal + * normal: + * off when disabling picture, OK, after timer + * on when enabling picture, with OK + * loading: + * off when disabling picture and when loading done + * on on start loading + * *** open: do not show when audio enabled and slide show running (currently slideshow is paused) + * - AudioBanner + * always shown when audioEnabled=true + * on when enabling Audio + * off when disabling audio + * update in play + * timers: 1 - slide show + * 2 - pictureBanner + * 3 - info showtime + * 4 - audio short update + */ + +#define DRAWING_THREAD + +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;igetScreenHeight(); + UINT vwidth=Video::getInstance()->getScreenWidth(); + if (height > vheight-2*AUDIOBANNER_BOTTOM_MARGIN) + height=vheight-2*AUDIOBANNER_BOTTOM_MARGIN; + info->setSize(vwidth -2*AUDIOBANNER_X_MARGIN, height); + info->createBuffer(); + if (Video::getInstance()->getFormat() == Video::PAL) + { + info->setPosition(AUDIOBANNER_X_MARGIN, vheight-height-AUDIOBANNER_BOTTOM_MARGIN); + } + else + { + info->setPosition(AUDIOBANNER_X_MARGIN, vheight-height-AUDIOBANNER_BOTTOM_MARGIN); + } + + info->setTitleBarOn(0); + info->setDropThrough(); + //from vvideorec + //set the regions for the closcks and bars on banner + barRegion.x = info->getWidth()-AUDIOBARLEN-20; + barRegion.y = info->getHeight() - 30; // FIXME, need to be - 1? and below? + barRegion.w = info->getWidth()-AUDIOBARLEN+10; + barRegion.h = 30; + + clocksRegion.x = 130; + clocksRegion.y = barRegion.y + 3; + clocksRegion.w = 190; + clocksRegion.h = getFontHeight(); + Log::getInstance()->log("VMediaView",Log::DEBUG,"created AudioInfo %p",info); + BoxStack::getInstance()->add(info); + char *buf=new char [len]; + if (playerError || audioError) { + SNPRINTF(buf,len,"%s",first); + } + else { + char tbuf[Media::TIMEBUFLEN]; + if (playall) { + SNPRINTF(buf,len,"%s\n" + "%s:%s\n" + "%s: %d/%d\n" + "%s:%s\n" + "%s:%s\n", + first, + tr("FileName"),currentAudio->getDisplayName(), + pl,num,parent->getNumEntries(MEDIA_TYPE_AUDIO), + tr("Directory"),parent->getDirname(MEDIA_TYPE_AUDIO), + tr("Time"),currentAudio->getTimeString(tbuf)); + } + else { + SNPRINTF(buf,len,"%s\n" + "%s:%s\n" + "%s:%s\n" + "%s:%s\n", + first, + tr("FileName"),currentAudio->getDisplayName(), + tr("Directory"),parent->getDirname(MEDIA_TYPE_AUDIO), + tr("Time"),currentAudio->getTimeString(tbuf)); + } + } + Log::getInstance()->log("VMediaView",Log::DEBUG,"info: (%d)%s",strlen(buf),buf); + //now the real drawing functions + info->draw(); + //title + info->rectangle(0, 0, info->getWidth(), 30, 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; + } +} -- 2.39.2