From daf441ffaeee872b13838727600b811aa4cb0849 Mon Sep 17 00:00:00 2001 From: Marten Richter Date: Thu, 17 May 2012 10:13:43 +0200 Subject: [PATCH] Add files specific for raspberry pi --- audiovpe.cc | 300 +++++++++++ audiovpe.h | 84 +++ feed.h | 29 + ledraspberry.cc | 56 ++ ledraspberry.h | 44 ++ mtdraspberry.cc | 51 ++ mtdraspberry.h | 41 ++ osdopengl.cc | 677 +++++++++++++++++++++++ osdopengl.h | 133 +++++ remotelinux.cc | 636 ++++++++++++++++++++++ remotelinux.h | 70 +++ shaders/generic__vertex_shader.h | 33 ++ shaders/osd__frag_shader.h | 44 ++ surfaceopengl.cc | 366 +++++++++++++ surfaceopengl.h | 70 +++ threadpandroid.cc | 144 +++++ threadpandroid.h | 68 +++ threadsystem.h | 29 + videovpeogl.cc | 769 ++++++++++++++++++++++++++ videovpeogl.h | 113 ++++ wjpegcomplex.cc | 892 +++++++++++++++++++++++++++++++ wjpegcomplex.h | 236 ++++++++ wjpegsimple.cc | 66 +++ wjpegsimple.h | 50 ++ 24 files changed, 5001 insertions(+) create mode 100644 audiovpe.cc create mode 100644 audiovpe.h create mode 100644 feed.h create mode 100644 ledraspberry.cc create mode 100644 ledraspberry.h create mode 100644 mtdraspberry.cc create mode 100644 mtdraspberry.h create mode 100644 osdopengl.cc create mode 100644 osdopengl.h create mode 100644 remotelinux.cc create mode 100644 remotelinux.h create mode 100644 shaders/generic__vertex_shader.h create mode 100644 shaders/osd__frag_shader.h create mode 100644 surfaceopengl.cc create mode 100644 surfaceopengl.h create mode 100644 threadpandroid.cc create mode 100644 threadpandroid.h create mode 100644 threadsystem.h create mode 100644 videovpeogl.cc create mode 100644 videovpeogl.h create mode 100644 wjpegcomplex.cc create mode 100644 wjpegcomplex.h create mode 100644 wjpegsimple.cc create mode 100644 wjpegsimple.h diff --git a/audiovpe.cc b/audiovpe.cc new file mode 100644 index 0000000..1957b74 --- /dev/null +++ b/audiovpe.cc @@ -0,0 +1,300 @@ +/* + Copyright 2004-2005 Chris Tallon, 2009 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 "audiovpe.h" +#include "videovpeogl.h" +#include "log.h" + +AudioVPE::AudioVPE() +{ + if (instance) return; + initted = 0; + lastpacketnum=-1; + currentpacketnum=-1; + streamType = 0; + volume = 20; + muted = 0; + lastAType = MPTYPE_MPEG_AUDIO; +} + +AudioVPE::~AudioVPE() +{ +} + +int AudioVPE::init(UCHAR tstreamType) +{ + if (initted) return 0; + initted = 1; + +// // if ((fdAudio = open("/dev/adec_mpg", O_RDWR | O_NONBLOCK)) < 0) return 0; + // if ((fdAudio = open("/dev/adec_mpg", O_WRONLY)) < 0) return 0; + + streamType = tstreamType; + + if (!initAllParams()) + { + shutdown(); + return 0; + } + + unMute(); + + // Set the volume variable to what the hardware is set at now + int hwvol = -1; +/* int hwvolFail = ioctl(fdAudio, AV_GET_AUD_VOLUME, &hwvol); + if (!hwvolFail) + { + volume = 20 - ((hwvol >> 16) & 0xFF); + if ((volume < 0) || (volume > 20)) volume = 20; + }*/ + + return 1; +} + +int AudioVPE::initAllParams() +{ + return (setStreamType(streamType) && setChannel() && setSource()); +} + +int AudioVPE::shutdown() +{ + if (!initted) return 0; + initted = 0; + //close(fdAudio); + return 1; +} + +int AudioVPE::setStreamType(UCHAR type) +{ + if (!initted) return 0; + + // if (ioctl(fdAudio, AV_SET_AUD_STREAMTYPE, type) != 0) return 0; + return 1; +} + +int AudioVPE::setChannel() +{ + if (!initted) return 0; + + // if (ioctl(fdAudio, AV_SET_AUD_CHANNEL, 0) != 0) return 0; + return 1; +} + +int AudioVPE::setSource() +{ + if (!initted) return 0; + + // if (ioctl(fdAudio, AV_SET_AUD_SRC, 1) != 0) return 0; + return 1; +} + +int AudioVPE::sync() +{ + if (!initted) return 0; + + // if (ioctl(fdAudio, AV_SET_AUD_SYNC, 2) != 0) return 0; + return 1; +} + +int AudioVPE::play() +{ + if (!initted) return 0; + lastpacketnum=-1; + currentpacketnum=-1; + ((VideoVPEOGL*) Video::getInstance())->initMuxer(); + + //if (ioctl(fdAudio, AV_SET_AUD_PLAY, 0) != 0) return 0; + return 1; +} + +int AudioVPE::stop() +{ + if (!initted) return 0; + ((VideoVPEOGL*) Video::getInstance())->deinitMuxer(); + + //if (ioctl(fdAudio, AV_SET_AUD_RESET, 0x11) != 0) return 0; + return 1; +} + +int AudioVPE::mute() +{ + if (!initted) return 0; + +// if (ioctl(fdAudio, AV_SET_AUD_MUTE, 1) != 0) return 0; + Log::getInstance()->log("Audio", Log::DEBUG, "MUTE MUTE MUTE"); + + muted = 1; + return 1; +} + +int AudioVPE::unMute() +{ + if (!initted) return 0; + +// if (ioctl(fdAudio, AV_SET_AUD_MUTE, 0) != 0) return 0; + Log::getInstance()->log("Audio", Log::DEBUG, "MUTE OFF OFF OFF"); + + muted = 0; + return 1; +} + +int AudioVPE::pause() +{ + if (!initted) return 0; + + // if (ioctl(fdAudio, AV_SET_AUD_PAUSE, 1) != 0) return 0; + return 1; +} + +int AudioVPE::unPause() +{ + if (!initted) return 0; + + // if (ioctl(fdAudio, AV_SET_AUD_UNPAUSE, 1) != 0) return 0; + return 1; +} + +int AudioVPE::reset() +{ + if (!initted) return 0; +//test(); + Log::getInstance()->log("Audio", Log::DEBUG, "reset called"); + ((VideoVPEOGL*) Video::getInstance())->deinitMuxer(); + +// if (ioctl(fdAudio, AV_SET_AUD_RESET, 0x11) != 0) return 0; +// Log::getInstance()->log("Audio", Log::DEBUG, "reset back"); + // if (ioctl(fdAudio, AV_SET_AUD_PLAY, 0) != 0) return 0; + + doMuting(); + return 1; +} + +int AudioVPE::setVolume(int tvolume) +{ + // parameter: 0 for silence, 20 for full + if ((tvolume < 0) || (tvolume > 20)) return 0; + +// volume = 2 * (20 - volume); +// Right, that one was rubbish... 0-10 were almost +// inaudible, 11-20 did what should have been done +// over the whole 0-20 range + + tvolume = 20 - tvolume; + + audio_volume Avolume; + Avolume.frontleft = tvolume + Aoffset.frontleft; + Avolume.frontright = tvolume + Aoffset.frontright; + Avolume.rearleft = tvolume + Aoffset.rearleft; + Avolume.rearright = tvolume + Aoffset.rearright; + Avolume.center = tvolume + Aoffset.center; + Avolume.lfe = tvolume + Aoffset.lfe; + +// if (ioctl(fdAudio, AV_SET_AUD_VOLUME, &Avolume) != 0) return 0; + +// unsigned long vol = (tvolume << 24) | (tvolume << 16); +// +// Log::getInstance()->log("Audio", Log::DEBUG, "%lx", vol); +// Log::getInstance()->log("Audio", Log::DEBUG, "%i", tvolume); + +// if (ioctl(fdAudio, AV_SET_AUD_VOLUME, &vol) != 0) return 0; + return 1; +} + +#ifdef DEV +int AudioVPE::test() +{ +// ULLONG stc = 0; +// return ioctl(fdAudio, AV_SET_AUD_STC, &stc); + +/* aud_sync_parms_t a; + a.parm1 = 0; + a.parm2 = 0; +*/ +// int b = ioctl(fdAudio, AV_SET_AUD_DISABLE_SYNC, &a); + + + /*OK*/ //printf("Audio sync disable = %i\n", b); + + return 1; + + +} +#endif + +void AudioVPE::PrepareMediaSample(const MediaPacketList& mplist,UINT samplepos) +{ + packet = mplist.front(); +} + +UINT AudioVPE::DeliverMediaSample(UCHAR* buffer, UINT* samplepos) +{/* + VideoVPEOGL *video=(VideoVPEOGL*)Video::getInstance(); + //Log::getInstance()->log("Audio", Log::DEBUG, "lastpacket %d lastbuddypacket %d currentpacket %d",lastpacketnum,video->getLastPacketNum(),packet.index); + currentpacketnum=packet.index; + if (lastpacketnum!=-1 && packet.index-1!=lastpacketnum && packet.index-1>video->getLastPacketNum()) return 0; //Not in order + + //format pes + switch (packet.type) { + case MPTYPE_MPEG_AUDIO: + buffer[packet.pos_buffer + 3]=0xc0; break; + case MPTYPE_AC3: + buffer[packet.pos_buffer +buffer[packet.pos_buffer+8]+9]=0x80; + buffer[packet.pos_buffer + 3]=0xbd; break; + break; + default: + case MPTYPE_AC3_PRE13://Not tested no recording availiable + buffer[packet.pos_buffer + 3]=0xbd; break; + break; + }; + + if (packet.type!=lastAType && lastpacketnum!=-1){//Format Change //Push data out ! + video->deinitMuxer(); + lastAType=packet.type; + video->initMuxer(); + } + lastAType=packet.type; + + + /* Mutex *mutex=video->getMuxMutex(); + mutex->Lock(); + FILE *muxout=video->getMuxFile(); + if (!muxout) {mutex->Unlock(); return 0;}* + int written=0; + //written=fwrite(buffer + packet.pos_buffer,1,packet.length,muxout); + written=video->WriteOutTS(buffer + packet.pos_buffer,packet.length,packet.type); + lastpacketnum=packet.index; + + //mutex->Unlock(); + + //Log::getInstance()->log("Audio", Log::DEBUG, "wrote %d bytes to mux",written); + + if (written == (int)packet.length) { *samplepos = 0; return 1;} + if (written <= 0) { + return 0; + } + *samplepos = written;*/ + // Handle a partial write. Is this possible? Should we bother? No! + + return 1; +} + +void AudioVPE::ResetTimeOffsets() +{ +} diff --git a/audiovpe.h b/audiovpe.h new file mode 100644 index 0000000..b9be7ac --- /dev/null +++ b/audiovpe.h @@ -0,0 +1,84 @@ +/* + Copyright 2004-2005 Chris Tallon, 2009 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 AUDIOVPE_H +#define AUDIOVPE_H + +#include +#include +#include +#include + +#include "defines.h" +#include "audio.h" + + + + + +class AudioVPE : public Audio +{ + public: + AudioVPE(); + virtual ~AudioVPE(); + + 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; } + + //Writing Data to Audiodevice + 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(); + + int getLastPacketNum() {return lastpacketnum;}; + int getCurrentPacketNum(){return currentpacketnum;}; + UCHAR getLastAType() {return lastAType;} + +#ifdef DEV + int test(); +#endif + + private: + int initAllParams(); + UCHAR streamType; + UCHAR lastAType; + int lastpacketnum; + int currentpacketnum; + + MediaPacket packet; + UINT packetpos; +}; + +#endif diff --git a/feed.h b/feed.h new file mode 100644 index 0000000..5f6c14b --- /dev/null +++ b/feed.h @@ -0,0 +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 diff --git a/ledraspberry.cc b/ledraspberry.cc new file mode 100644 index 0000000..bda573a --- /dev/null +++ b/ledraspberry.cc @@ -0,0 +1,56 @@ +/* + Copyright 2004-2005 Chris Tallon, 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. +*/ + +#include "ledraspberry.h" + +LedRaspberry::LedRaspberry() +{ + initted = 0; +} + +LedRaspberry::~LedRaspberry() +{ +} + +int LedRaspberry::init(int tdevice) +{ + if (initted) return 0; + initted = 1; + return 1; +} + +int LedRaspberry::shutdown() +{ + if (!initted) return 0; + initted = 0; + return 1; +} + +int LedRaspberry::on() +{ + if (!initted) return 0; + return 0; +} + +int LedRaspberry::off() +{ + if (!initted) return 0; + return 0; +} diff --git a/ledraspberry.h b/ledraspberry.h new file mode 100644 index 0000000..6e394cb --- /dev/null +++ b/ledraspberry.h @@ -0,0 +1,44 @@ +/* + 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 LEDRASPBERRY_H +#define LEDRASPBERRY_H + +#include + +#include "led.h" + +class LedRaspberry : public Led +{ + public: + LedRaspberry(); + virtual ~LedRaspberry(); + + int init(int device); + int shutdown(); + + int on(); + int off(); + + private: + int initted; +}; + +#endif diff --git a/mtdraspberry.cc b/mtdraspberry.cc new file mode 100644 index 0000000..c46abff --- /dev/null +++ b/mtdraspberry.cc @@ -0,0 +1,51 @@ +/* + 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 "mtdraspberry.h" +#include "video.h" + +MtdRaspberry::MtdRaspberry() +{ + initted = 0; +} + +MtdRaspberry::~MtdRaspberry() +{ +} + +int MtdRaspberry::init() +{ + if (initted) return 0; + initted = 1; + return 1; +} + +int MtdRaspberry::shutdown() +{ + if (!initted) return 0; + initted = 0; + return 1; +} + +short MtdRaspberry::getPALorNTSC() +{ + return Video::PAL; //Fixme! +} + diff --git a/mtdraspberry.h b/mtdraspberry.h new file mode 100644 index 0000000..5a80854 --- /dev/null +++ b/mtdraspberry.h @@ -0,0 +1,41 @@ +/* + Copyright 2004-2005 Chris Tallon, 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 MTDRASPBERRY_H +#define MTDRASPBERRY_H + +#include "mtd.h" + +class MtdRaspberry: public Mtd { +public: + MtdRaspberry(); + virtual ~MtdRaspberry(); + + virtual int init(); + virtual int shutdown(); + + virtual short getPALorNTSC(); + +private: + int initted; +}; + +#endif + diff --git a/osdopengl.cc b/osdopengl.cc new file mode 100644 index 0000000..27008d0 --- /dev/null +++ b/osdopengl.cc @@ -0,0 +1,677 @@ +/* + 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 "videovpeogl.h" +#include "surfaceopengl.h" + + +#include "message.h" +#include "command.h" + +#include "shaders/generic__vertex_shader.h" +#include "shaders/osd__frag_shader.h" + +#define BACKBUFFER_WIDTH 1920 +#define BACKBUFFER_HEIGHT 1080 + + + +long long getTimeMS() { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec*1000+ts.tv_nsec/1000000LL; +} + + + +OsdOpenGL::OsdOpenGL() +{ + glmutex.Lock(); + + external_driving=false; + + lastrendertime=getTimeMS(); + display_height=0; + display_width=0; + osd_shader=0; + gen_shader=0; + osd_program=0; + + + +} + +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?) + bcm_host_init(); + + //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!",eglGetError()); + return 0; + } + + if (eglInitialize(egl_display, NULL, NULL)==EGL_FALSE) { + Log::getInstance()->log("OSD", Log::WARN, "Initialising display failed! %d",eglGetError()); + 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_CONFORMANT, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; // Here, we might have to select the resolution! + + EGLConfig ourconfig; //maybe accept more configs? + EGLint number; + + if (eglChooseConfig(egl_display, attributs, &ourconfig, 1, &number)==EGL_FALSE) { + Log::getInstance()->log("OSD", Log::WARN, "Choosing egl config failed! %d",eglGetError()); + return 0; + } + + const EGLint attr_context[]={ + EGL_CONTEXT_CLIENT_VERSION,2, + EGL_NONE + }; + + egl_context=eglCreateContext(egl_display,ourconfig,EGL_NO_CONTEXT,attr_context); + if (egl_context==EGL_NO_CONTEXT) { + Log::getInstance()->log("OSD", Log::WARN, "Creating egl context failed! %d",eglGetError()); + 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) "); + 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,display_width<<16,display_height<<16}; + DISPMANX_DISPLAY_HANDLE_T bcm_display; + DISPMANX_ELEMENT_HANDLE_T bcm_element; + 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, + 0,&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=display_height; + nativewindow.width=display_width; + + egl_surface = eglCreateWindowSurface(egl_display,ourconfig, &nativewindow,NULL ); + if (egl_surface==EGL_NO_SURFACE) { + Log::getInstance()->log("OSD", Log::WARN, "Creating egl window surface failed!"); + return 0; + } + + if (eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context)== EGL_FALSE) { + Log::getInstance()->log("OSD", Log::WARN, "Making egl Current failed"); + return 0; + } + // Test stuff + + + + //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 + + gen_shader=CreateShader(generic_vertex_shader, GL_VERTEX_SHADER); + osd_shader=CreateShader(osd_frag_shader, GL_FRAGMENT_SHADER); + + + osd_program=glCreateProgram(); + if (osd_program==0) { + Log::getInstance()->log("OSD", Log::WARN, "Creating glsl program failed!%d",glGetError()); + return 0; + } + glAttachShader(osd_program,gen_shader); + glAttachShader(osd_program,osd_shader); + glBindAttribLocation(osd_program,0,"vec_pos"); + glBindAttribLocation(osd_program,1,"tex_coord"); + + osd_sampler_loc=glGetUniformLocation(osd_program,"texture"); + + glLinkProgram(osd_program); + GLint link_status; + glGetShaderiv(osd_program,GL_LINK_STATUS, &link_status); + + if (!link_status) { + char buffer[1024]; + glGetProgramInfoLog(osd_program,1024,NULL,buffer); + Log::getInstance()->log("OSD", Log::WARN, "Compiling Programm failed!"); + Log::getInstance()->log("OSD", Log::WARN, "%s",buffer); + glDeleteProgram(osd_program); + return 0; + } + + glClearColor(0.0f,0.0f,0.0f,1.f); + + eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT ); + glmutex.Unlock(); + threadStart(); + + return 1; +} + + +GLuint OsdOpenGL::CreateShader(const GLchar * source, GLenum type) +{ + GLuint ret_shad=0; + + ret_shad=glCreateShader(type); + if (ret_shad==0 ) { + Log::getInstance()->log("OSD", Log::WARN, "Creating Shader failed! %d",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("OSD", Log::WARN, "Compiling Shader failed!"); + glGetShaderInfoLog(ret_shad,1024,NULL,buffer); + Log::getInstance()->log("OSD", Log::WARN, "%s",buffer); + glDeleteShader(ret_shad); + return 0; + } + return ret_shad; +} + + +void OsdOpenGL::InitVertexBuffer(float scalex,float scaley) +{ + Video* video=Video::getInstance(); + float texx=((float)video->getScreenWidth())/1024.f; + float texy=((float)video->getScreenHeight())/1024.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; + + + + + // glBindBuffer(GL_ARRAY_BUFFER, vB); + // glBufferData(GL_ARRAY_BUFFER, sizeof(osdvertices), osdvertices, GL_STATIC_DRAW); + + + // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iB); + //glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(osdindices), osdindices, GL_STATIC_DRAW); + + + + return; +} + +int OsdOpenGL::shutdown() +{ + if (!initted) return 0; + initted = 0; + + + if (osd_shader!=0) glDeleteShader(osd_shader); + if (gen_shader!=0) glDeleteShader(gen_shader); + if (osd_program!=0) glDeleteProgram(osd_program); + + 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 ); + + return 1; +} + +void OsdOpenGL::screenShot(const char* fileName) +{ + screen->screenShot(fileName); +} + +void OsdOpenGL::threadMethod() +{ + // We have to claim the gl context for this thread + //glmutex.Lock(); + + //glmutex.Unlock(); + while (true) + { + unsigned int waittime=10; + if (initted){ + // if (evrstate==EVR_pres_off || evrstate==EVR_pres_pause) + // { + Render(); + //TODO get surfaces from Video object + /* } 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) MILLISLEEP(min(10,waittime)); + //Sleep(1); + } + //eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT ); +} + + +void OsdOpenGL::threadPostStopCleanup() +{ + //Doing nothing + //goo; +} + + +// This function is called from the WinMain function in order to get Screen updates +void OsdOpenGL::Render() +{ + if (!initted) return ; + if (external_driving) { + long long time1=getTimeMS(); + + if ((time1-lastrendertime)>200) {//5 fps for OSD updates are enough, avoids tearing + InternalRendering(NULL); + lastrendertime=getTimeMS(); + } else { + //Sleep(5); //Sleep for 5 ms, in order to avoid blocking the other threads + } + } else { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + long long time1=ts.tv_sec*1000+ts.tv_nsec/1000000LL; + if ((time1-lastrendertime)>50) {//10 fps for OSD updates are enough, avoids tearing + InternalRendering(NULL); + lastrendertime=getTimeMS(); + } else { + //Sleep(5); + + } + + } +} + +void OsdOpenGL::RenderDS(GLuint present){ + if (!initted) return; + if (external_driving) { + InternalRendering(present); + lastrendertime=getTimeMS(); + } +} + + +void OsdOpenGL::InternalRendering(GLuint 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); + }*/ + + + //InitVertexBuffer(display_width,display_height); + InitVertexBuffer(1.f,1.f); + + + glViewport(0, 0, display_width,display_height); + + glClear(GL_COLOR_BUFFER_BIT); + glUseProgram(osd_program); + + 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); + + + + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D,((SurfaceOpenGL*)screen)->getTexture()); + + 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); + + glEnable(GL_BLEND); + glBlendFuncSeparate (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,GL_ZERO,GL_ONE); + +/* glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE,sizeof(OSDVERTEX), + (GLvoid*)(((char*)osdvertices)+3*sizeof(GLfloat))); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE,sizeof(OSDVERTEX), + (GLvoid*)(((char*)osdvertices)+3*sizeof(GLfloat)+sizeof(OSDCOLOR)));*/ + //glDisable(GL_LIGHTING); + //glEnable(GL_TEXTURE_2D); + //glEnable(GL_BLEND); + //glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + //glDepthFunc(GL_ALWAYS); + //glDisable(GL_DEPTH_TEST); + //glDisable(GL_STENCIL_TEST); + //glDisable(GL_CULL_FACE); + + + +/* + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D,((SurfaceOpenGL*)screen)->getTexture()); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);*/ +// glUniform1i(mTextureUniformHandle, present); + + + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + //glDrawElements(GL_TRIANGLES, sizeof(osdindices)/sizeof(osdindices[0]), GL_UNSIGNED_BYTE, 0); + + + + +/* glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,sizeof(OSDVERTEX), 0); + glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE,sizeof(OSDVERTEX), + (GLvoid*)(3*sizeof(GLfloat))); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE,sizeof(OSDVERTEX), + (GLvoid*)(3*sizeof(GLfloat)+sizeof(OSDCOLOR)));*/ + + //glDisable(GL_BLEND); + //glDisable(GL_TEXTURE_2D); + + //Show it to the user! + eglSwapBuffers(egl_display, egl_surface); + + EndPainting(); + + +// if (!external_driving) { +// Sleep(4);//The User can wait for 4 milliseconds to see his changes +// } +} + +bool OsdOpenGL::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; + +} + + +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::setExternalDriving(/*DsAllocator* dsall,*/unsigned int width, unsigned 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 OsdOpenGL::Blank() { + BeginPainting(); + glClearColor(0.15f, 0.25f, 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 new file mode 100644 index 0000000..0e9d08e --- /dev/null +++ b/osdopengl.h @@ -0,0 +1,133 @@ +/* + 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 + +#include "osd.h" +#include "defines.h" +#include "log.h" +#include "threadp.h" +#include "mutex.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(); + + + + + // This function is called from the threadMethod function in order to get Screen updates + void Render(); + void RenderDS(GLuint present); + void BeginPainting(); + void EndPainting(); + + void setExternalDriving(/*DsAllocator* dsall,*/ unsigned int width, unsigned int height); + void Blank(); + + + + +private: + + //Maybe move the following stuff to a generic opengl object also for boosting DCT etc. + + GLuint CreateShader(const GLchar * source, GLenum type); + + 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(GLuint present); + bool DoLost(); + void InitVertexBuffer(float scalex,float scaley); + OSDVERTEX osdvertices[4]; + GLubyte osdindices[6]; + + + GLuint osd_shader; + GLuint gen_shader; + + GLuint osd_program; + + GLint osd_sampler_loc; + + /* BCM specific */ + + uint32_t display_height; + uint32_t display_width; + + EGLDisplay egl_display; + EGLSurface egl_surface; + EGLContext egl_context; + +}; + +#endif diff --git a/remotelinux.cc b/remotelinux.cc new file mode 100644 index 0000000..93517da --- /dev/null +++ b/remotelinux.cc @@ -0,0 +1,636 @@ +/* + Copyright 2004-2005 Chris Tallon; 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 "remotelinux.h" +#include "i18n.h" +#include +#include + + +#include + +#include +#include + + + + +#define W_G_HCW(type,code) ( (((ULLONG)(type))<<32) | code) + +#define W_HCW_KC 1 /* key code as defined by kernel for keyboard and remotes through /dev/input */ +#define W_HCW_CEC 2 /* HDMI_CEC */ +#define W_HCW_LIRC 3 /* remote control LIRC*/ + + +RemoteLinux::RemoteLinux() +{ + initted = 0; + curevent=0; + hascurevent=false; + signal=false; + tv.tv_sec = 0; + tv.tv_usec = 0; + +} + +RemoteLinux::~RemoteLinux() +{ + for (unsigned int i=0; ilog("Remote", Log::NOTICE, "Probe /dev/input/event%d",eventid); + // file exists + unsigned long ev_type=0; + int new_fd=open(buffer,O_RDONLY); + if (new_fd<0) { + Log::getInstance()->log("Remote", Log::NOTICE, "Can not open /dev/input/event%d",eventid); + continue; + } + if (ioctl(new_fd, EVIOCGBIT(0, EV_MAX), &ev_type) < 0) { + Log::getInstance()->log("Remote", Log::NOTICE, "Ioctl failed /dev/input/event%d %d",eventid,errno); + close(new_fd); + } + //Now test if it generates keyboard presses + if (test_bit((char*)&ev_type , EV_KEY)) { + Log::getInstance()->log("Remote", Log::NOTICE, "Add /dev/input/event%d to List",eventid); + devices.push_back(new_fd); + } else { + close(new_fd); + } + + + + + } + + } + + + + + return 1; +} + +int RemoteLinux::shutdown() +{ + if (!initted) return 0; + + initted = 0; + return 1; +} + +UCHAR RemoteLinux::getButtonPress(int waitType) +{ + /* how = 0 - block + how = 1 - start new wait + how = 2 - continue wait + how = 3 - no wait + */ + + + struct timeval* passToSelect = NULL; + int retval; + fd_set readfds; + + if (waitType == 0) + { + passToSelect = NULL; + } + else if (waitType == 1) + { + tv.tv_sec = 1; + tv.tv_usec = 000000; + passToSelect = &tv; + } + else if (waitType == 2) + { + if ((tv.tv_sec == 0) && (tv.tv_usec == 0)) // protection in case timer = 0 + { + tv.tv_sec = 1; + tv.tv_usec = 000000; + } + passToSelect = &tv; + } + else if (waitType == 3) + { + tv.tv_sec = 0; + tv.tv_usec = 0; + passToSelect = &tv; + } + FD_ZERO(&readfds); + + int maxfd=0; + for (int i=0; i> 32; + char *rt=NULL; + switch(type) + { + case W_HCW_KC:{ + unsigned int vk=(ULONG)hcw; + rt=new char[10]; + const char *desc=linux_keymap[vk]; + if (desc) { + strncpy(rt,desc,9); + } else { + sprintf(rt,"0x%x",vk); + } + }break; + //TODO + /* case W_HCW_CEC:{ + ULONG ch=(ULONG)hcw; + ULONG scancode=OemKeyScan(ch); + + rt=new char[10]; + GetKeyNameText(scancode << 16,rt,10); + }break; + case W_HCW_LIRC:{ + ULONG ri=(ULONG)hcw; + rt=new char[10]; + sprintf(rt,"R: %X",ri); + }break;*/ + + }; + return rt; +} + + + + + + + +/* +void RemoteLinux::Signal() { + signal=true; + //PulseEvent(event); + SetEvent(event); +} + +void RemoteLinux::SendPower() +{ + + curevent=POWER; + hascurevent=true; + SetEvent(event); +}*/ + diff --git a/remotelinux.h b/remotelinux.h new file mode 100644 index 0000000..c4f3ffa --- /dev/null +++ b/remotelinux.h @@ -0,0 +1,70 @@ +/* + Copyright 2004-2005 Chris Tallon; 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 REMOTELINUX_H +#define REMOTELINUX_H + +#include + +#include "defines.h" +#include "log.h" +#include "remote.h" + +#include + + + + + +class RemoteLinux : public Remote +{ + public: + RemoteLinux(); + virtual ~RemoteLinux(); + + int init(char *devName); + int shutdown(); + int getDevice(); + UCHAR getButtonPress(int how); + void clearBuffer(); + // void Signal(); + +// void SendPower(); + void InitHWCListwithDefaults(); + const char*HardcodedTranslateStr(UCHAR command); + char* HCWDesc(ULLONG hcw); + + + + private: + int initted; + bool signal; + UCHAR curevent; + bool hascurevent; + + UCHAR TranslateHWCFixed(ULLONG code); + void InitKeymap(); + vector devices; + struct timeval tv; + +}; + +#endif + diff --git a/shaders/generic__vertex_shader.h b/shaders/generic__vertex_shader.h new file mode 100644 index 0000000..e5f1ac5 --- /dev/null +++ b/shaders/generic__vertex_shader.h @@ -0,0 +1,33 @@ +/* + 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 OSD_GEN_SHADER_H +#define OSD_GEN_SHADER_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"; +#endif diff --git a/shaders/osd__frag_shader.h b/shaders/osd__frag_shader.h new file mode 100644 index 0000000..50421e8 --- /dev/null +++ b/shaders/osd__frag_shader.h @@ -0,0 +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 OSD_FRAG_SHADER_H +#define OSD_FRAG_SHADER_H + +const GLchar osd_frag_shader[] = + "precision mediump float;\n" + "uniform sampler2D texture;" + "varying vec2 out_texCoord;\n" + "void main()\n" + "{\n" + // " vec4 temp=vec4(0.3,0.4,0.5,0.5);\n" + // " temp.r=0.6;\n" + " gl_FragColor=texture2D(texture,out_texCoord);\n" + // " temp.r=temp2.r;\n" + // " temp.b=temp2.b;\n" + // "gl_FragColor=temp;\n" + "}\n"; +#endif +//"vec4 temp=vec4(0.3,0.4,0.5);\n" +// "temp.a=1.0f;\n" +// " gl_FragColor.r=texture2D(texture,out_texCoord.st).r;\n" +// " gl_FragColor.b=0.2f;\n" +// " gl_FragColor.g=0.2f;\n" +// "gl_FragColor.a=0.5f;\n" +//"gl_FragColor=vec4(0.3,0.4,0.5,0.5);\n" diff --git a/surfaceopengl.cc b/surfaceopengl.cc new file mode 100644 index 0000000..034b782 --- /dev/null +++ b/surfaceopengl.cc @@ -0,0 +1,366 @@ +/* + 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 + sheight = 64; + swidth = 64; + while (sheight < height) { // should be a power of 2 + sheight *= 2; + } + while (swidth < width) { // should be a power of 2 + swidth *= 2; + } + 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, unsigned int 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); + + 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, unsigned int c) +{ + fillblt(x1, y, x2-x1, 1, c); +} + +void SurfaceOpenGL::drawVertLine(int x, int y1, int y2, unsigned int c) +{ + fillblt(x, y1, 1, y2-y1, c); +} + +void SurfaceOpenGL::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 SurfaceOpenGL::updateToScreen(int sx, int sy, int w, int h, int dx, int dy) // FIXME new, replace others with this FIXME +{ + srf_mutex.Lock();//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 + }*/ + OsdOpenGL* osd=((OsdOpenGL*)(Osd::getInstance())); + + GLuint screengltexture=((SurfaceOpenGL*)screen)->getTexture(); + + osd->BeginPainting(); + glBindTexture(GL_TEXTURE_2D, screengltexture); + //Log::getInstance()->log("Surface", Log::WARN, "UTS Mark3 %d",glGetError()); + + for (int y=0;ylog("Surface", Log::WARN, "UTS Mark43 %d",glGetError()); + + } + //Log::getInstance()->log("Surface", Log::WARN, "UTS Mark4 %d",glGetError()); + 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 new file mode 100644 index 0000000..6bec52a --- /dev/null +++ b/surfaceopengl.h @@ -0,0 +1,70 @@ +/* + 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, unsigned int c); + void drawPixel(int x, int y, Colour& c, bool fastdraw=false); + void drawPixel(int x, int y, unsigned int c, bool fastdraw=false); + void drawHorzLine(int x1, int x2, int y, unsigned int c); + void drawVertLine(int x, int y1, int y2, unsigned int c); + void drawBitmap(int x, int y, const Bitmap& bm); + int updateToScreen(int sx, int sy, int w, int h, int dx, int dy); + void readPixel(int x, int y, unsigned char* r, unsigned char* g, unsigned char* b); + void screenShot(const char* fileName); + void ReleaseSurface(); + int blt(int fd, unsigned long shandle, int sx, int sy, int width, int height, unsigned long dhandle, int dx, int dy); + void drawJpeg(const char *fileName,int x, int y,int *width, int *height); +/* void drawJpeg(char *buffer,ULONG buflength,DWORD x, DWORD y,DWORD *width, DWORD *height);*/ + + GLuint getTexture() {return gltexture;}; + + + + private: + GLuint gltexture; + UINT sheight,swidth; + char * data; + + + + Mutex srf_mutex; + +}; + +#endif diff --git a/threadpandroid.cc b/threadpandroid.cc new file mode 100644 index 0000000..693286a --- /dev/null +++ b/threadpandroid.cc @@ -0,0 +1,144 @@ +/* + 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 "threadpandroid.h" +#include "log.h" + +// Undeclared functions, only for use in this file to start the thread +void threadPAndroidInternalStart(void *arg) +{ + // I don't want signals + sigset_t sigs; + sigfillset(&sigs); + pthread_sigmask(SIG_BLOCK, &sigs, NULL); + + Thread *t = (Thread *)arg; + t->threadInternalStart2(); +} + +int ThreadPAndroid ::threadStart() +{ + pthread_cond_init(&threadCond, NULL); + pthread_cond_init(&threadKillable, NULL); + pthread_mutex_init(&threadCondMutex, NULL); + + threadActive = 1; + killable=false; + if (pthread_create(&pthread, NULL, (void*(*)(void*))threadPAndroidInternalStart, (void *)this) == -1) return 0; + return 1; +} + +void ThreadPAndroid ::threadStop() +{ + threadActive = 0; + // Signal thread here in case it's waiting + threadSignal(); + pthread_join(pthread, NULL); + this->threadPostStopCleanup(); +} + + +void ThreadPAndroid ::threadCancel() +{ + threadActive = 0; + threadSignalNoLock(); + if (killable) pthread_cond_wait(&threadKillable,&threadCondMutex); + // pthread_cancel(pthread); + pthread_join(pthread, NULL); + this->threadPostStopCleanup(); +} + + + +void ThreadPAndroid ::threadCheckExit() +{ + if (!threadActive) { + pthread_cond_signal(&threadKillable); + pthread_exit(NULL); + } +} + +void ThreadPAndroid ::threadLock() +{ + pthread_mutex_lock(&threadCondMutex); +} + +void ThreadPAndroid ::threadUnlock() +{ + pthread_mutex_unlock(&threadCondMutex); +} + +void ThreadPAndroid ::threadSignal() +{ + pthread_mutex_lock(&threadCondMutex); + pthread_cond_signal(&threadCond); + pthread_mutex_unlock(&threadCondMutex); +} + +void ThreadPAndroid ::threadSignalNoLock() +{ + pthread_cond_signal(&threadCond); +} + +void ThreadPAndroid ::threadWaitForSignal() +{ + pthread_cond_wait(&threadCond, &threadCondMutex); +} + +void ThreadPAndroid ::threadWaitForSignalTimed(struct timespec* ts) +{ + pthread_cond_timedwait(&threadCond, &threadCondMutex, ts); +} + +void ThreadPAndroid ::threadSetKillable() +{ + //pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + //pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + killable=true; + pthread_cond_signal(&threadKillable); +} + + +pthread_t ThreadPAndroid ::getThreadID() // returns the ID of this thread +{ + return pthread; +} + +// Static functions + +void ThreadPAndroid ::threadSuicide() +{ + if(!pthread_detach(pthread_self())) + { + MILLISLEEP(1000); + if(!pthread_detach(pthread_self())) + { + MILLISLEEP(1000); + pthread_detach(pthread_self()); + } + } + + pthread_exit(NULL); +} + +pthread_t ThreadPAndroid ::thisThreadID() // returns the ID of the calling thread +{ + return pthread_self(); +} diff --git a/threadpandroid.h b/threadpandroid.h new file mode 100644 index 0000000..955bde1 --- /dev/null +++ b/threadpandroid.h @@ -0,0 +1,68 @@ +/* + 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 THREADPANDROID_H +#define THREADPANDROID_H + +#include +#include + +#include "defines.h" +#include "thread.h" + +class ThreadPAndroid : public Thread +{ + protected: + // Override this method in derived classes + virtual void threadMethod()=0; + virtual void threadPostStopCleanup()=0; + + // Methods to use from outside the thread + int threadStart(); // start the thread. threadMethod() will be called in derived class + void threadStop(); // stop the thread nicely. thread will be stopped when it next calls threadCheckExit() + void threadCancel(); // stop thread immediately. thread will be stopped at the next cancellation point + void threadSignal(); // releases a thread that has called threadWaitForSignal + void threadSignalNoLock(); // same as above but without locking guarantees. probably not a good idea. + + // Methods to use from inside the thread + void threadSetKillable(); // allows threadCancel() to work + void threadCheckExit(); // terminates thread if threadStop() has been called + void threadWaitForSignal(); // pauses thread until threadSignal() is called + void threadWaitForSignalTimed(struct timespec*); // pauses thread until threadSignal() is called or timer expires + void threadLock(); // locks the mutex used for internal cond/signal stuff + void threadUnlock(); // unlocks. + + // Internal bits and pieces + + pthread_t pthread; + pthread_cond_t threadCond; + pthread_cond_t threadKillable; + pthread_mutex_t threadCondMutex; + + public: + pthread_t getThreadID(); // returns the ID of the thread represented by this object + + + static pthread_t thisThreadID(); // Self identification - returns ID of calling thread + static void threadSuicide(); // Self termination + bool killable; +}; + +#endif diff --git a/threadsystem.h b/threadsystem.h new file mode 100644 index 0000000..cddc15f --- /dev/null +++ b/threadsystem.h @@ -0,0 +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 diff --git a/videovpeogl.cc b/videovpeogl.cc new file mode 100644 index 0000000..418a7bb --- /dev/null +++ b/videovpeogl.cc @@ -0,0 +1,769 @@ +/* + Copyright 2004-2005 Chris Tallon, 2009 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 "videovpeogl.h" +#include "audiovpe.h" +#include "mtdraspberry.h" +#include "demuxer.h" +// temp +#include "log.h" + + + +//taken from libsi +//taken and adapted from libdtv, (c) Rolf Hakenes +// CRC32 lookup table for polynomial 0x04c11db7 +unsigned int crc_table[256] = { + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, + 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, + 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, + 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, + 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, + 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, + 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, + 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, + 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, + 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, + 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, + 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, + 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, + 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, + 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, + 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, + 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, + 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, + 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, + 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, + 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, + 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, + 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, + 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, + 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, + 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, + 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, + 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, + 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, + 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4}; + +unsigned int crc32 (const char *d, int len, unsigned int crc) +{ + register int i; + const unsigned char *u=(unsigned char*)d; // Saves '& 0xff' + + for (i=0; i> 24) ^ *u++)]; + + return crc; +} + + + +VideoVPEOGL::VideoVPEOGL() +{ + if (instance) return; + muxout=NULL; + muxnumber=0; + lastpacketnum=-1; + audioconti=videoconti=0; + pmtversion=patversion=0; + +} + +VideoVPEOGL::~VideoVPEOGL() +{ + instance = NULL; +} + +int VideoVPEOGL::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; +} + + + +int VideoVPEOGL::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 VideoVPEOGL::setDefaultAspect() +{ + return setAspectRatio(tvsize); +} + +int VideoVPEOGL::shutdown() +{ + if (!initted) return 0; + initted = 0; +// close(fdVideo); + return 1; +} + + +int VideoVPEOGL::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 VideoVPEOGL::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 VideoVPEOGL::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 VideoVPEOGL::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 VideoVPEOGL::signalOff() +{ +// if (ioctl(fdVideo, AV_SET_VID_DENC, 0) != 0) return 0; + return 1; +} + +int VideoVPEOGL::signalOn() +{ +// if (ioctl(fdVideo, AV_SET_VID_DENC, 1) != 0) return 0; + return 1; +} + +int VideoVPEOGL::setSource() +{ + if (!initted) return 0; + + // What does this do... +// if (ioctl(fdVideo, AV_SET_VID_SRC, 1) != 0) return 0; + return 1; +} + +int VideoVPEOGL::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 VideoVPEOGL::sync() +{ + if (!initted) return 0; + +// if (ioctl(fdVideo, AV_SET_VID_SYNC, 2) != 0) return 0; + return 1; +} + +void VideoVPEOGL::initMuxer(bool iframe) +{ + char buffer[1024]; + lastpacketnum=-1; + iframemode=iframe; + if (muxout) return; + sprintf(buffer,"/share/dev/muxout%d.ts",muxnumber); + filemutex.Lock(); + audioconti=videoconti=0; + muxout=fopen(buffer,"wb"); + filemutex.Unlock(); + WriteOutPATPMT(); + Log::getInstance()->log("Video", Log::DEBUG, + "Create file %s with result %d",buffer,muxout); +} + +void VideoVPEOGL::deinitMuxer() +{ + if (muxout) + { + filemutex.Lock(); + FILE*temp=muxout; + muxout=NULL; + audioconti=videoconti=0; + filemutex.Unlock(); + Log::getInstance()->log("Video", Log::DEBUG, + "Close mux"); + + fclose(temp); + muxnumber++; + } +} + + +int VideoVPEOGL::play() +{ + if (!initted) return 0; + + + +// if (ioctl(fdVideo, AV_SET_VID_PLAY, 0) != 0) return 0; + return 1; +} + +int VideoVPEOGL::stop() +{ + if (!initted) return 0; + + + +// if (ioctl(fdVideo, AV_SET_VID_STOP, 0) != 0) return 0; + return 1; +} + +int VideoVPEOGL::reset() +{ + if (!initted) return 0; + +// if (ioctl(fdVideo, AV_SET_VID_RESET, 0x11) != 0) return 0; + return 1; +} + +int VideoVPEOGL::pause() +{ + if (!initted) return 0; + +// if (ioctl(fdVideo, AV_SET_VID_PAUSE, 0) != 0) return 0; + return 1; +} + +int VideoVPEOGL::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 VideoVPEOGL::fastForward() +{ + if (!initted) return 0; + +// if (ioctl(fdVideo, AV_SET_VID_FFWD, 1) != 0) return 0; + return 1; +} + +int VideoVPEOGL::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 VideoVPEOGL::attachFrameBuffer() +{ + if (!initted) return 0; + +// if (ioctl(fdVideo, AV_SET_VID_FB, 0) != 0) return 0; + return 1; +} + +int VideoVPEOGL::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 VideoVPEOGL::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; + }*/ + return 0; +} + +ULONG VideoVPEOGL::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 VideoVPEOGL::test() +{ + return 0; + +// ULLONG stc = 0; +// return ioctl(fdVideo, AV_SET_VID_STC, &stc); +/* + // reset(); + return 1; +*/ +} + +int VideoVPEOGL::test2() +{ + return 0; +} +#endif + +void VideoVPEOGL::PrepareMediaSample(const MediaPacketList& mplist,UINT samplepos) +{ + mediapacket = mplist.front(); +} + +UINT VideoVPEOGL::DeliverMediaSample(UCHAR* buffer, UINT* samplepos) +{ + if (!muxout) return 0; + + AudioVPE *audio=((AudioVPE*)audio->getInstance()); + // Log::getInstance()->log("Video", Log::DEBUG, "lastpacket %d lastbuddypacket %d currentpacket %d",lastpacketnum,audio->getLastPacketNum(),mediapacket.index); + + //format pes + switch (mediapacket.type) { + case MPTYPE_VIDEO_MPEG2: + h264=false; + buffer[mediapacket.pos_buffer + 3]=0xe0; break; + case MPTYPE_VIDEO_H264: + h264=true; + buffer[mediapacket.pos_buffer + 3]=0xe0; break; + }; + + + if (lastpacketnum!=-1 && mediapacket.index-1!=lastpacketnum && mediapacket.index-1>audio->getLastPacketNum() && mediapacket.index>audio->getCurrentPacketNum()) { + Log::getInstance()->log("Video", Log::DEBUG, "Packet not in order, packet num %d, lastpacketnum %d, audio lastpacketnum %d",mediapacket.index,lastpacketnum,audio->getLastPacketNum()); + return 0; //Not in order + } + + +/* filemutex.Lock(); + int written=fwrite(buffer + mediapacket.pos_buffer,1,mediapacket.length,muxout);*/ + if (mediapacket.synched) WriteOutPATPMT(); + + int written=WriteOutTS(buffer + mediapacket.pos_buffer,mediapacket.length, mediapacket.type); + lastpacketnum=mediapacket.index; + //filemutex.Unlock(); + //Log::getInstance()->log("Video", Log::DEBUG, "wrote %d bytes to mux",written); + + + if (written == (int)mediapacket.length) { *samplepos = 0; return 1;} + if (written <= 0) { + return 0; + } + *samplepos = written; + // Handle a partial write. Is this possible? Should we bother? No! + + return 1; +} +/* + +int VideoVPEOGL::WriteOutPCR(ULLONG pts) +{ + unsigned char ts_buffer[188]; + unsigned char ts_crc[4]; + unsigned char conti; + memset(ts_buffer,0xFF,188); + + conti=videoconti; + ts_buffer[0]=0x47; //TS_SYNC + ts_buffer[1]=0x40; //Payload_start + ts_buffer[2]=100; + ts_buffer[3]=0x20| conti; + ts_buffer[4]=183; + ts_buffer[5]=0x10; //PCR! + + + + int tocopy=length; + unsigned char *dest=buffer; + filemutex.Lock(); + unsigned int outlength=184; + while (tocopy>=outlength) { + + ts_buffer[3]=0x10 | conti; + fwrite(ts_buffer,1,4,muxout); //Header out + + fwrite(dest,1, outlength,muxout); + + + dest+=outlength; + tocopy-=outlength; + conti++; + conti&=0x0F; + ts_buffer[1]=0x00; //Payload_start + } + if (tocopy>0) { + ts_buffer[3]=0x30 | conti;//we have an adpation field + fwrite(ts_buffer,1,4,muxout); //Header out + unsigned char adaptionfield[2]={outlength-tocopy-1,0x00}; + if (tocopy<183) fwrite(adaptionfield,1,2,muxout); + else fwrite(adaptionfield,1,1,muxout); + unsigned char paddy=0xFF; + if (outlength>tocopy+2) { + for (int i=0;i<(outlength-tocopy-2);i++) fwrite(&paddy,1,1,muxout); + } + fwrite(dest,1,tocopy,muxout); + } + + + conti++; + conti&=0x0F; + filemutex.Unlock(); + + + videoconti=conti; + +}*/ + +int VideoVPEOGL::WriteOutTS(const unsigned char *buffer,int length, int type) +{ + unsigned char ts_buffer[4]; + unsigned char ts_crc[4]; + unsigned char conti; + ts_buffer[0]=0x47; //TS_SYNC + ts_buffer[1]=0x40; //Payload_start + switch (type) { + case MPTYPE_VIDEO_MPEG2: + case MPTYPE_VIDEO_H264: + conti=videoconti; + ts_buffer[2]=100; break; + + case MPTYPE_MPEG_AUDIO: + conti=audioconti; + ts_buffer[2]=101; break; + case MPTYPE_AC3: + case MPTYPE_AC3_PRE13: + conti=audioconti; + ts_buffer[2]=102; break; + default: + return 0; break; //???? + }; + int tocopy=length; + const unsigned char *dest=buffer; + filemutex.Lock(); + unsigned int outlength=184; + while (tocopy>=outlength) { + + ts_buffer[3]=0x10 | conti; + fwrite(ts_buffer,1,4,muxout); //Header out + + fwrite(dest,1, outlength,muxout); + + + dest+=outlength; + tocopy-=outlength; + conti++; + conti&=0x0F; + ts_buffer[1]=0x00; //Payload_start + } + if (tocopy>0) { + ts_buffer[3]=0x30 | conti;//we have an adpation field + fwrite(ts_buffer,1,4,muxout); //Header out + unsigned char adaptionfield[2]={outlength-tocopy-1,0x00}; + if (tocopy<183) fwrite(adaptionfield,1,2,muxout); + else fwrite(adaptionfield,1,1,muxout); + unsigned char paddy=0xFF; + if (outlength>tocopy+2) { + for (int i=0;i<(outlength-tocopy-2);i++) fwrite(&paddy,1,1,muxout); + } + fwrite(dest,1,tocopy,muxout); + } + + + conti++; + conti&=0x0F; + filemutex.Unlock(); + + switch (type) { + case MPTYPE_VIDEO_MPEG2: + case MPTYPE_VIDEO_H264: + videoconti=conti; break; + + case MPTYPE_MPEG_AUDIO: + case MPTYPE_AC3: + case MPTYPE_AC3_PRE13: + audioconti=conti; + break; + }; + return length; + +} + +void VideoVPEOGL::WriteOutPATPMT() +{ + unsigned char ts_buffer[188]; + //PAT + memset(ts_buffer,0xFF,188); + ts_buffer[0]=0x47; //TS_SYNC + ts_buffer[1]=0x40; //Payload_start + ts_buffer[2]=0x00; + ts_buffer[3]=0x10; + ts_buffer[4]=0x00; + ts_buffer[5]=0x00;//table id + ts_buffer[6]=0xB0; //Section syntax + ts_buffer[7]=13; //Section length + ts_buffer[8]=0x80;//PSID + ts_buffer[9]=0x08;//PSID lo + ts_buffer[10]=0xC1 | (patversion << 1); + ts_buffer[11]=0x00;//section numbrt + ts_buffer[12]=0x00; + ts_buffer[13]=0x00; //hi program number + ts_buffer[14]=98; + ts_buffer[15]=0xe0; + ts_buffer[16]=98; + int crc =crc32((const char *)ts_buffer+5, 12, 0xFFFFFFFF); + int i = 0; + ts_buffer[17] = crc >> 24; + ts_buffer[18] = crc >> 16; + ts_buffer[19] = crc >> 8; + ts_buffer[20] = crc; + patversion++; + patversion&=0x1F; + filemutex.Lock(); + fwrite(ts_buffer,1,188,muxout); //Header out + filemutex.Unlock(); + + //PMT + memset(ts_buffer,0xFF,188); + ts_buffer[0]=0x47; //TS_SYNC + ts_buffer[1]=0x40; //Payload_start + ts_buffer[2]=98; + ts_buffer[3]=0x10; + ts_buffer[4]=0x00; + ts_buffer[5]=0x02;//table id + ts_buffer[6]=0xB0; //Section syntax + ts_buffer[7]=23; //Section length + ts_buffer[8]=0x00;//PSID + ts_buffer[9]=98;//PSID lo + ts_buffer[10]=0xC1 | (pmtversion << 1); + ts_buffer[11]=0x00;//section numbrt + ts_buffer[12]=0x00; + ts_buffer[13]=0xE0 | 0x1F; + ts_buffer[14]=0xFF;//Video is pcr + ts_buffer[15]=0xF0;//dummy + ts_buffer[16]=0x00; //program info + //Video + + if (h264) + { + ts_buffer[17] = 0x1B; // stream type video + } + else + { + ts_buffer[17] = 0x02; // stream type video + } + ts_buffer[18] = 0xE0; // + ts_buffer[19]=100; //Video pid + ts_buffer[20]= 0xF0; // + ts_buffer[21]= 0x00; // + // audio + int add_length=0; + unsigned char atype=((AudioVPE*)Audio::getInstance())->getLastAType(); + switch (atype) { + default: + case MPTYPE_MPEG_AUDIO: + ts_buffer[22] = 0x04; // stream type audio + ts_buffer[23] = 0xE0; // + ts_buffer[24]=101; //Audio pid + ts_buffer[25]= 0xF0; // + ts_buffer[26]= 0x00; // + break; + case MPTYPE_AC3: + case MPTYPE_AC3_PRE13: + ts_buffer[22] = 0x06; // stream type private + ts_buffer[23] = 0xE0; // + ts_buffer[24]=102; //ac3 + ts_buffer[25]= 0xF0; // + ts_buffer[26]= 0x03; // + ts_buffer[27] = 0x6A; + ts_buffer[28] = 0x01; // length + ts_buffer[29] = 0x00; + ts_buffer[7]+=3; + add_length=3; + break; + }; + crc =crc32((const char *)ts_buffer+5, 22+add_length, 0xFFFFFFFF); + ts_buffer[27+add_length] = crc >> 24; + ts_buffer[28+add_length] = crc >> 16; + ts_buffer[29+add_length] = crc >> 8; + ts_buffer[30+add_length] = crc; + pmtversion++; + pmtversion&=0x1F; + filemutex.Lock(); + fwrite(ts_buffer,1,188,muxout); //Header out + filemutex.Unlock(); + + + +} + + +void VideoVPEOGL::ResetTimeOffsets() +{ +} + +bool VideoVPEOGL::displayIFrame(const UCHAR* buffer, UINT length) +{ + //write(fdVideo, buffer, length); + if (!iframemode) EnterIframePlayback(); + WriteOutTS(buffer,length, h264?MPTYPE_VIDEO_H264 :MPTYPE_VIDEO_MPEG2 ); + + lastpacketnum=-1; + return true; +} + +int VideoVPEOGL::EnterIframePlayback() +{ + deinitMuxer(); + initMuxer(true); + return 0; +} + diff --git a/videovpeogl.h b/videovpeogl.h new file mode 100644 index 0000000..f554ad4 --- /dev/null +++ b/videovpeogl.h @@ -0,0 +1,113 @@ +/* + Copyright 2004-2005 Chris Tallon 2009 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 VIDEOVPEOGL_H +#define VIDEOVPEOGL_H + +#include "mutex.h" + +#include +#include +#include +#include +#include + +#include + +#include "defines.h" +#include "video.h" + +#define WRITE_PACKETS 16 +#define WRITE_LENGTH (32*1024) + + +class VideoVPEOGL : public Video +{ + public: + VideoVPEOGL(); + virtual ~VideoVPEOGL(); + + int init(UCHAR format); + int shutdown(); + + int setFormat(UCHAR format); + int setConnection(UCHAR connection); + int setAspectRatio(UCHAR aspectRatio); // This one does the pin 8 scart widescreen switching + int setMode(UCHAR mode); + int setTVsize(UCHAR size); // Is the TV a widescreen? + int setDefaultAspect(); + int setSource(); + int setPosition(int x, int y); + int sync(); + int play(); + int stop(); + int pause(); + int unPause(); + int fastForward(); + int unFastForward(); + int reset(); + int blank(); + int signalOn(); + int signalOff(); + int attachFrameBuffer(); // What does this do? + ULONG timecodeToFrameNumber(ULLONG timecode); + ULLONG getCurrentTimestamp(); + bool displayIFrame(const UCHAR* buffer, UINT length); + + // Writing Data to Videodevice + virtual void PrepareMediaSample(const MediaPacketList&, UINT samplepos); + virtual UINT DeliverMediaSample(UCHAR* buffer, UINT* samplepos); + virtual long long SetStartOffset(long long curreftime, bool *rsync) + { return 0; }; + virtual void ResetTimeOffsets(); + + virtual bool supportsh264(){return true;}; + + int WriteOutTS(const unsigned char *buffer,int length, int type); + void WriteOutPATPMT(); + + + Mutex *getMuxMutex() {return &filemutex;}; //File must be pointer of pointer Thread safety! + FILE *getMuxFile() {return muxout;}; //File must be pointer of pointer Thread safety! + void initMuxer(bool iframe=false); + void deinitMuxer(); + + int getLastPacketNum() {return lastpacketnum;}; +#ifdef DEV + int test(); + int test2(); +#endif + + private: + int EnterIframePlayback(); + bool iframemode; + + FILE* muxout; + int muxnumber; + Mutex filemutex; + int lastpacketnum; + unsigned char audioconti,videoconti; + unsigned int patversion,pmtversion; + + MediaPacket mediapacket; +}; + +#endif diff --git a/wjpegcomplex.cc b/wjpegcomplex.cc new file mode 100644 index 0000000..b0a58f6 --- /dev/null +++ b/wjpegcomplex.cc @@ -0,0 +1,892 @@ +/* + Copyright 2004-2005 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#include "boxx.h" +#include "wjpegcomplex.h" +#include +#include +#include + +#ifndef WIN32 +#include +#else + +#endif + +#include "i18n.h" +#include "log.h" +#include "surface.h" + +#ifndef __ANDROID__ +extern "C" +{ + #include +} +#endif + +#ifndef __ANDROID__ +//#define USE_BUFFER +//#define EXTENDED_JPEGLIB + +//a struct to store user data for the jpeg decompressor +class jpegUserData{ + public: + WJpeg::JpegControl * ctl; + JpegReader *reader; + jpegUserData() { + ctl=NULL; + reader=NULL; + } + }; + +//the scale factors supported by the jpeg lib + +struct scale{ + UINT num; + UINT denom; +}; + + +struct scale jpegFactors[]={ +#ifndef EXTENDED_JPEGLIB + {1,1},{1,2},{1,4},{1,8} +#else + {1,1},{7,8},{3,4},{5,8},{1,2},{3,8},{1,4},{1,8} +#endif +}; +#endif + +#ifdef WIN32 +#define LocalReader WindowsResourceJpegReader +class WindowsResourceJpegReader: public JpegReader { + public: + virtual ULONG initRead(const char *filename); + virtual ULONG readChunk(ULONG offset,ULONG len,char **buf); + virtual ULONG getSize(); + virtual ~WindowsResourceJpegReader(); +protected: + HGLOBAL hres; + LPVOID buffer; + DWORD size; +}; + +ULONG WindowsResourceJpegReader::initRead(const char *filename) +{ + HRSRC res=FindResource(NULL,filename,RT_RCDATA); + hres=LoadResource(NULL,res); + buffer=LockResource(hres); + size=SizeofResource(NULL,res); + //CloseHandle(hres); + return size; +} + + ULONG WindowsResourceJpegReader::readChunk(ULONG offset,ULONG len,char **buf) +{ + if (offset>size) return 0; + ULONG toread=min(size-offset,len); + char* buffy=(char*)malloc(toread); + memcpy(buffy,((char*)buffer)+offset,toread); + *buf=buffy; + return toread; +} + + WindowsResourceJpegReader::~WindowsResourceJpegReader(){ + buffer=NULL; + size=0; + FreeResource(hres); + } + + ULONG WindowsResourceJpegReader::getSize(){ + return (ULONG)size; +} +#else + +#define LocalReader LocalJpegReader +class LocalJpegReader: public JpegReader { + public: + virtual ULONG initRead(const char *filename); + virtual ULONG readChunk(ULONG offset,ULONG len,char **buf); + virtual ~LocalJpegReader(); + virtual ULONG getSize(); + LocalJpegReader(); +protected: + FILE *file; + ULONG size; +}; + +LocalJpegReader::LocalJpegReader(){ + file=NULL; + size=0; +} + +ULONG LocalJpegReader::initRead(const char *filename) +{ + if (file) fclose(file); + size=0; + file=fopen(filename,"r"); + if (file) { + struct stat st; + if (fstat(fileno(file), &st) != 0) return 0; + size= st.st_size; + return size; + } + Log::getInstance()->log("WJepg", Log::ERR, "localReader unable to open File %s", filename); + return 0; +} + +ULONG LocalJpegReader::readChunk(ULONG offset,ULONG len,char **buf) +{ + *buf=NULL; + ULONG bread=0; + if (file) { + ULLONG cpos=ftell(file); + if (offset != cpos) { + fseek(file,offset-cpos,SEEK_CUR); + } + if (offset != (ULONG)ftell(file)) { + Log::getInstance()->log("WJepg", Log::ERR, "readChunk pos = %lu not available", offset); + } + else { + *buf=(char *)malloc(len); + if ( ! (*buf)) { + Log::getInstance()->log("WJepg", Log::ERR, "readChunk pos = %lu not available", offset); + } + else { + bread=fread(*buf,1,len,file); + } + } + } + return bread; +} + +LocalJpegReader::~LocalJpegReader(){ + if (file) fclose(file); + file=NULL; +} +ULONG LocalJpegReader::getSize(){ + return size; +} +#endif + +#ifndef __ANDROID__ +/*---------------------------------------------------------------- + the jpeg lib routines + ---------------------------------------------------------------- + */ + +extern "C" { + + +struct my_error_mgr { + struct jpeg_error_mgr pub; /* "public" fields */ + + jmp_buf setjmp_buffer; /* for return to caller */ +}; + +typedef struct my_error_mgr * my_error_ptr; + +/* + * Here's the routine that will replace the standard error_exit method: + */ + +METHODDEF(void) +my_error_exit (j_common_ptr cinfo) +{ + /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */ + my_error_ptr myerr = (my_error_ptr) cinfo->err; + + /* Always display the message. */ + /* We could postpone this until after returning, if we chose. */ + (*cinfo->err->output_message) (cinfo); + /* Return control to the setjmp point */ + longjmp(myerr->setjmp_buffer, 1); +} + +ULONG jpeg_call_reader(ULONG offset,ULONG size,char ** buf,void *cb) { + jpegUserData *user=(jpegUserData *)cb; + return user->reader->readChunk(offset,size,buf); +} +//the memory buffer reader for the jpeg lib +//taken from jdatasrc.c + +#include "jinclude.h" +#include "jpeglib.h" +#include "jerror.h" + + +typedef struct { + struct jpeg_source_mgr pub; /* public fields */ + + JOCTET * buffer; /* start of buffer */ + boolean start_of_file; /* have we gotten any data yet? */ + void * userdata; /* used for callback */ + ULONG offset; +} my_source_mgr; + +typedef my_source_mgr * my_src_ptr; + +#define INPUT_BUF_SIZE (64*4096) /* choose an efficiently fread'able size */ + + +/* + * Initialize source --- called by jpeg_read_header + * before any data is actually read. + */ + +METHODDEF(void) +linit_source (j_decompress_ptr cinfo) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + + /* We reset the empty-input-file flag for each image, + * but we don't clear the input buffer. + * This is correct behavior for reading a series of images from one source. + */ + src->start_of_file = TRUE; + src->offset=0; +} + + +/* + * Fill the input buffer --- called whenever buffer is emptied. + * + * In typical applications, this should read fresh data into the buffer + * (ignoring the current state of next_input_byte & bytes_in_buffer), + * reset the pointer & count to the start of the buffer, and return TRUE + * indicating that the buffer has been reloaded. It is not necessary to + * fill the buffer entirely, only to obtain at least one more byte. + * + * There is no such thing as an EOF return. If the end of the file has been + * reached, the routine has a choice of ERREXIT() or inserting fake data into + * the buffer. In most cases, generating a warning message and inserting a + * fake EOI marker is the best course of action --- this will allow the + * decompressor to output however much of the image is there. However, + * the resulting error message is misleading if the real problem is an empty + * input file, so we handle that case specially. + * + * In applications that need to be able to suspend compression due to input + * not being available yet, a FALSE return indicates that no more data can be + * obtained right now, but more may be forthcoming later. In this situation, + * the decompressor will return to its caller (with an indication of the + * number of scanlines it has read, if any). The application should resume + * decompression after it has loaded more data into the input buffer. Note + * that there are substantial restrictions on the use of suspension --- see + * the documentation. + * + * When suspending, the decompressor will back up to a convenient restart point + * (typically the start of the current MCU). next_input_byte & bytes_in_buffer + * indicate where the restart point will be if the current call returns FALSE. + * Data beyond this point must be rescanned after resumption, so move it to + * the front of the buffer rather than discarding it. + */ + +METHODDEF(boolean) +lfill_input_buffer (j_decompress_ptr cinfo) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + size_t nbytes; + if (src->buffer) free(src->buffer); + src->buffer=NULL; + nbytes = jpeg_call_reader(src->offset, INPUT_BUF_SIZE,(char **)&(src->buffer), src->userdata); + + if (nbytes <= 0) { + WARNMS(cinfo, JWRN_JPEG_EOF); + src->buffer = (JOCTET *)malloc(2); + src->buffer[0] = (JOCTET) 0xFF; + src->buffer[1] = (JOCTET) JPEG_EOI; + nbytes = 2; + + } + src->offset+=nbytes; + + src->pub.next_input_byte = src->buffer; + src->pub.bytes_in_buffer = nbytes; + src->start_of_file = FALSE; + + return TRUE; +} + + +/* + * Skip data --- used to skip over a potentially large amount of + * uninteresting data (such as an APPn marker). + * + * Writers of suspendable-input applications must note that skip_input_data + * is not granted the right to give a suspension return. If the skip extends + * beyond the data currently in the buffer, the buffer can be marked empty so + * that the next read will cause a fill_input_buffer call that can suspend. + * Arranging for additional bytes to be discarded before reloading the input + * buffer is the application writer's problem. + */ + +METHODDEF(void) +lskip_input_data (j_decompress_ptr cinfo, long num_bytes) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + + /* Just a dumb implementation for now. Could use fseek() except + * it doesn't work on pipes. Not clear that being smart is worth + * any trouble anyway --- large skips are infrequent. + */ + if (num_bytes > 0) { + while (num_bytes > (long) src->pub.bytes_in_buffer) { + num_bytes -= (long) src->pub.bytes_in_buffer; + (void) lfill_input_buffer(cinfo); + /* note we assume that fill_input_buffer will never return FALSE, + * so suspension need not be handled. + */ + } + src->pub.next_input_byte += (size_t) num_bytes; + src->pub.bytes_in_buffer -= (size_t) num_bytes; + } +} + + +/* + * An additional method that can be provided by data source modules is the + * resync_to_restart method for error recovery in the presence of RST markers. + * For the moment, this source module just uses the default resync method + * provided by the JPEG library. That method assumes that no backtracking + * is possible. + */ + + +/* + * Terminate source --- called by jpeg_finish_decompress + * after all data has been read. Often a no-op. + * + * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding + * application must deal with any cleanup that should happen even + * for error exit. + */ + +METHODDEF(void) +lterm_source (j_decompress_ptr cinfo) +{ + /* no work necessary here */ +} + + +/* + * Prepare for input from a stdio stream. + * The caller must have already opened the stream, and is responsible + * for closing it after finishing decompression. + */ + +extern "C" void +jpeg_memio_src (j_decompress_ptr cinfo, void * userdata) +{ + my_src_ptr src; + + /* The source object and input buffer are made permanent so that a series + * of JPEG images can be read from the same file by calling jpeg_stdio_src + * only before the first one. (If we discarded the buffer at the end of + * one image, we'd likely lose the start of the next one.) + * This makes it unsafe to use this manager and a different source + * manager serially with the same JPEG object. Caveat programmer. + */ + if (cinfo->src == NULL) { /* first time for this JPEG object? */ + cinfo->src = (struct jpeg_source_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(my_source_mgr)); + src = (my_src_ptr) cinfo->src; + src->buffer = NULL; + } + + src = (my_src_ptr) cinfo->src; + src->pub.init_source = linit_source; + src->pub.fill_input_buffer = lfill_input_buffer; + src->pub.skip_input_data = lskip_input_data; + src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ + src->pub.term_source = lterm_source; + src->userdata=userdata; + src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ + src->pub.next_input_byte = NULL; /* until buffer loaded */ + src->offset=0; + src->userdata=userdata; + if (src->buffer) { + free(src->buffer); + src->buffer=NULL; + } +} +/* cleanup to be called before cleanup of cinfo*/ +extern "C" void +jpeg_memio_cleanup (j_decompress_ptr cinfo) { + my_src_ptr src=(my_src_ptr)cinfo->src; + Log::getInstance()->log("BJpeg", Log::DEBUG, "cleanup src, src=%p, buf=%p",src,(src?src->buffer:0)); + if (src && src->buffer) { + free(src->buffer); + src->buffer=NULL; + } +} + +//taken from mvpmc +//http://git.mvpmc.org/cgi-bin/gitweb.cgi?p=mvpmc.git;a=blob_plain;h=02d4354c0cbbed802a9aa1478918a49fcbf61d00;f=libs/libwidget/image_jpeg.c + +#define GET2BYTES(cinfo, V, swap, offset) do { \ + if (cinfo->src->bytes_in_buffer == 0) (*mysrc->pub.fill_input_buffer)(cinfo); \ + cinfo->src->bytes_in_buffer--; \ + V = (*cinfo->src->next_input_byte++) << (swap?0:8); \ + if (cinfo->src->bytes_in_buffer == 0) (*mysrc->pub.fill_input_buffer)(cinfo); \ + cinfo->src->bytes_in_buffer--; \ + V += (*cinfo->src->next_input_byte++) << (swap?8:0); \ + offset += 2; } while(0) + +#define GET4BYTES(cinfo, V, swap, offset) do { \ + if (cinfo->src->bytes_in_buffer == 0) (*mysrc->pub.fill_input_buffer)(cinfo); \ + cinfo->src->bytes_in_buffer--; \ + V = (*cinfo->src->next_input_byte++) << (swap?0:24); \ + if (cinfo->src->bytes_in_buffer == 0) (*mysrc->pub.fill_input_buffer)(cinfo); \ + cinfo->src->bytes_in_buffer--; \ + V += (*cinfo->src->next_input_byte++) << (swap?8:16); \ + if (cinfo->src->bytes_in_buffer == 0) (*mysrc->pub.fill_input_buffer)(cinfo); \ + cinfo->src->bytes_in_buffer--; \ + V += (*cinfo->src->next_input_byte++) << (swap?16:8); \ + if (cinfo->src->bytes_in_buffer == 0) (*mysrc->pub.fill_input_buffer)(cinfo); \ + cinfo->src->bytes_in_buffer--; \ + V += (*cinfo->src->next_input_byte++) << (swap?24:0); \ + offset += 4; } while(0) + +static boolean +get_exif_orient (j_decompress_ptr cinfo) +/* Get the Exif orientation info */ +{ + unsigned int tmp, offset, length, numtags; + int orient=-1; + jpegUserData * ud=0; + boolean swap; + orient = 1; + offset = 0; + my_src_ptr mysrc = (my_src_ptr) cinfo->src; + + /* marker length */ + GET2BYTES(cinfo, length, 0, offset); + if (length<8) goto err; + /* Exif header */ + GET4BYTES(cinfo, tmp, 0, offset); + if (tmp != 0x45786966) goto err; + GET2BYTES(cinfo, tmp, 0, offset); + if (tmp != 0x0000) goto err; + /* Byte-order */ + GET2BYTES(cinfo, tmp, 0, offset); + if (tmp == 0x4949) swap = 1; + else if (tmp == 0x4d4d) swap = 0; + else goto err; + GET2BYTES(cinfo, tmp, swap, offset); + if (tmp != 0x002A) goto err; + /* offset to first IFD */ + GET4BYTES(cinfo, tmp, swap, offset); + offset += tmp-8; + (*mysrc->pub.skip_input_data)(cinfo, tmp-8); + /* number of tags in IFD */ + GET2BYTES(cinfo, numtags, swap, offset); + if (numtags == 0) goto err; + + /* Search for Orientation Tag in IFD0 */ + for (;;) { + if (offset > length-12) goto err; + GET2BYTES(cinfo, tmp, swap, offset); + if (tmp == 0x0112) break; /* found Orientation Tag */ + if (--numtags == 0) goto err; + offset += 10; + (*mysrc->pub.skip_input_data)(cinfo, 10); + } + offset += 6; + (*mysrc->pub.skip_input_data)(cinfo, 6); + GET2BYTES(cinfo, orient, swap, offset); + if( orient==0 || orient>8 ) orient = 1; + + Log::getInstance()->log("WJpeg", Log::DEBUG, "read exif orientation %u", orient); + ud=(jpegUserData *)(mysrc->userdata); + switch(orient) { + case 3: + ud->ctl->exifRotation=WJpeg::ROT_180; + break; + case 6: + ud->ctl->exifRotation=WJpeg::ROT_90; + break; + case 8: + ud->ctl->exifRotation=WJpeg::ROT_270; + break; + } + +err: + (*mysrc->pub.skip_input_data)(cinfo, length-offset); + return TRUE; +} +} +#endif +/*---------------------------------------------------------------- + the implementation + ---------------------------------------------------------------- + */ + + +WJpegComplex::WJpegComplex(){ + reader=NULL; + owningReader=false; + errbuf[0]=0; +} + +WJpegComplex::~WJpegComplex() { + if (owningReader && reader) delete reader; +} + +int WJpegComplex::init(const char* tfileName) +{ + if (owningReader && reader) delete reader; + errbuf[0]=0; //clean error state + LocalReader *myreader=new LocalReader(); + reader=myreader; + owningReader=true; + ULONG psize=myreader->initRead(tfileName); + if (psize == 0) { + delete reader; + reader=NULL; + owningReader=false; + SNPRINTF(errbuf,200,"unable to open %s",tfileName); + return 0; + } + return 1; +} + + + + +bool WJpegComplex::hasError() { + return (errbuf[0] != 0); +} +void WJpegComplex::draw() +{ + bool ok=false; + JpegControl ctl; + if (reader) { + Region myRegion; + Surface *sfc=getSurface(myRegion); + myRegion.w=area.w; + myRegion.h=area.h; + ctl.area=myRegion; + ctl.enlarge=true; + if (drawJpeg(&ctl,sfc,reader,backgroundColour) ) ok=true; + } + else { + SNPRINTF(errbuf,200,"jpeg reader not initialized"); + } + if (! ok) { + drawTextCentre(tr("Jpeg ERROR"), 240, 170, Colour::LIGHTTEXT); + if (errbuf[0] != 0) drawTextCentre(errbuf,240,200, Colour::LIGHTTEXT); + if (ctl.error[0] != 0) drawTextCentre(ctl.error,240,230, Colour::LIGHTTEXT); + } +} + + + +/** + the main drawing function + this will read the pciture via the reader + and draw directly into the surface + it will compute an appropriate scale and set the infos in the + JpegControl structure +**/ + +#ifndef __ANDROID__ + +bool WJpegComplex::drawJpeg(JpegControl * ctl,Surface * sfc,JpegReader *rdr, Colour & backgroundColour) { + Log* logger = Log::getInstance(); + if (! rdr || ! sfc) { + logger->log("BJpeg", Log::ERR, "JPEG error rdr=NULL or sfc=NULL"); + return false; + } + logger->log("BJpeg", Log::DEBUG, "draw Jpeg Started sfc=%p, rdr=%p",sfc,rdr); + unsigned char* buffer =NULL; + struct jpeg_decompress_struct cinfo; + struct my_error_mgr jerr; + cinfo.err = jpeg_std_error(&jerr.pub); + jerr.pub.error_exit = my_error_exit; + /* Establish the setjmp return context for my_error_exit to use. */ + if (setjmp(jerr.setjmp_buffer)) { + /* If we get here, the JPEG code has signaled an error. + * We need to clean up the JPEG object, close the input file, and return. + */ + if (rdr) jpeg_memio_cleanup(&cinfo); + jpeg_destroy_decompress(&cinfo); + logger->log("BJpeg", Log::ERR, "JPEG error"); + if (buffer) free(buffer); + return false; + } + jpegUserData userdata; + int xpos=0; + int ypos=0; + jpeg_create_decompress(&cinfo); + userdata.reader=rdr; + userdata.ctl=ctl; + ctl->exifRotation=ROT_0; + ctl->error[0]=0; + ctl->exifDate[0]=0; + //create the input for the jpeg lib + jpeg_memio_src(&cinfo,(void *)(&userdata)); + //processor for EXIF data + jpeg_set_marker_processor(&cinfo, JPEG_APP0+1, get_exif_orient); + //read in header info + jpeg_read_header(&cinfo, TRUE); + ctl->picw=cinfo.image_width; + ctl->pich=cinfo.image_height; + ctl->compressedSize=rdr->getSize(); + //now we have important info available in ctl (pictuerw,h, exif orientation,size) + //compute rotation due to the defined enum values we can simply add them + ctl->finalRotation=(enum Rotation)((ctl->rotation+ctl->exifRotation)%4); + logger->log("BJpeg", Log::DEBUG, "JPEG read header w=%i h=%i, rot=%i", ctl->picw,ctl->pich,ctl->finalRotation); + //now we have to compute the scale + //input: ctl->picw,ctl->pich,ctl->finalRotation,ctl->mode,ctl->scaleAmount, ctl->scaleafter + // list of available jpeg scale factors + //out: scalenum,scaledenom,scaleafter + + UINT picturew=ctl->picw; + UINT pictureh=ctl->pich; + switch (ctl->finalRotation){ + case ROT_90: + case ROT_270: + pictureh=ctl->picw; + picturew=ctl->pich; + break; + default: + break; + } + UINT scalenum=1; + UINT scaledenom=1; + UINT scaleafter=1; + if (! ctl->enlarge) { + //scale - compute the factors to fit 100% + int scalew=1000*picturew/ctl->area.w; + int scaleh=1000*pictureh/ctl->area.h; + int scale=scaleh; + if (scalew > scaleh) scale=scalew; + + //OK now find out which is the optimal setting + //rule: find settings being nearest to: + // mode=LETTER - smaller or equal screen size (i.e. scale*scalefactor <= 1000) + // mode=CROP - bigger or equal screen size (i.e. scale *scalefactor>= 1000) + // mode=CROPPERCENT - smaller or equal screensize*scaleamount (i.e. scale*scalefactor<= 1000*scaleamount) + // scaleamount is in % - so in reality we use scaleamount*10 instead scaleamount*1000 + //the scalefactor computes as scalenum/(scaledenom*scaleafter) + scaledenom=8; + int minDiff=1000; + logger->log("BJpeg", Log::DEBUG, "start scaling screenw=%u, screenh=%u, pw=%u,ph=%u, scale=%d, mode=%d, %%=%u, after=%u", + ctl->area.w,ctl->area.h,picturew,pictureh,scale,(int)ctl->mode,ctl->scaleAmount, ctl->scaleafter); + for (UINT j=0;jscaleafter;sa++) { + int curf=(scale*jpegFactors[j].num)/(jpegFactors[j].denom*sa); + bool setThis=false; + logger->log("BJpeg", Log::DEBUG, "testing scale curf=%d,num=%u,denom=%u,after=%u,minDiff=%d", + curf,jpegFactors[j].num,jpegFactors[j].denom,sa,minDiff); + switch(ctl->mode) { + case CROP: + if (curf >= 1000 && curf < (minDiff +1000)) { + setThis=true; + minDiff=curf-1000; + } + break; + case LETTER: + if (curf <= 1000 && curf > (1000-minDiff)) { + setThis=true; + minDiff=1000-curf; + } + break; + case CROPPERCENT: + if (curf <= 10*(int)ctl->scaleAmount ) { + int abs=curf-1000; + if (abs < 0) abs=-abs; + if (abs < minDiff) { + setThis=true; + minDiff=abs; + } + } + break; + } + if (setThis) { + logger->log("BJpeg", Log::DEBUG, "testing scale curf=%d,take this",curf); + scalenum=jpegFactors[j].num; + scaledenom=jpegFactors[j].denom; + scaleafter=sa; + } + } + } + ctl->scale=100*scalenum/(scaledenom*scaleafter); + + logger->log("BJpeg", Log::DEBUG, "JPEG scaling found scale=%i num=%i denom=%i after=%i result=%i scale=%i%% ", + scale,scalenum,scaledenom,scaleafter,scale*ctl->scale/100,ctl->scale); + + cinfo.scale_denom=scaledenom; + cinfo.scale_num=scalenum; + } + else + { + //set drawing area according to picture + ctl->area.w=ctl->picw; + ctl->area.h=ctl->pich; + } + + //now we know the scaling + //compute the scaled size and position + + jpeg_start_decompress(&cinfo); + //picturew/h is now the output width from the decompressor and afterscaler + //this is unrotated + picturew=cinfo.output_width; + pictureh=cinfo.output_height; + if (scaleafter > 1) { + picturew=picturew/scaleafter; + pictureh=pictureh/scaleafter; + } + //if our image is smaller - center it + if (! ctl->enlarge) { + if (ctl->finalRotation == ROT_90 || ctl->finalRotation == ROT_270) { + int dim=pictureh; + xpos=(((int)ctl->area.w)-dim)/2; + dim=picturew; + ypos=(((int)ctl->area.h)-dim)/2; + } else { + int dim=picturew; + xpos=(((int)ctl->area.w)-dim)/2; + dim=pictureh; + ypos=(((int)ctl->area.h)-dim)/2; + } + if (xpos <0) xpos=0; + if (ypos <0) ypos=0; + } + xpos+=ctl->area.x; + ypos+=ctl->area.y; + //remember the jpeg dimensions for computing the buffer + UINT jpegwidth=cinfo.output_width; + UINT jpegheight=cinfo.output_height; + logger->log("BJpeg", Log::DEBUG, "JPEG startup done pw=%i ph=%i, xo=%i,yo=%i, jw=%i, jh=%i, rot=%d", + picturew, pictureh,xpos,ypos,jpegwidth,jpegheight,(int)ctl->finalRotation*90); + + //fill the background + sfc->fillblt(ctl->area.x,ctl->area.y,ctl->area.w,ctl->area.h,backgroundColour.rgba()); + + //line len in bytes (3 bytes per Pixel) - for buffer (can be bigger then surface) + int linelen=jpegwidth*3; +#ifdef USE_BUFFER + // MAKE THE 2D ARRAY + buffer = (unsigned char*)malloc(jpegheight * linelen); + logger->log("BJpeg", Log::DEBUG, "Buffer allocated at %p, lines = %i linelen = %i", buffer, jpegheight, linelen); + if (buffer == NULL) { + if (rdr) jpeg_memio_cleanup(&cinfo); + jpeg_destroy_decompress(&cinfo); + logger->log("BJpeg", Log::ERR, "JPEG error - no room for buffer"); + SNPRINTF(ctl->error,100,"no room for buffer"); + return false; + } +#endif + +#ifndef USE_BUFFER + //unsigned char lbuf[linelen]; + unsigned char *lbuf=new unsigned char[linelen*scaleafter]; + unsigned char * ptr=lbuf; + UINT outy=0; +#else + unsigned char * ptr=buffer; +#endif + + int rowsread = 0; + + Colour c; + sfc->startFastDraw();//Tell the surface, that we will draw a lot of pixel, + //so that performance, can be optimized + logger->log("BJpeg", Log::DEBUG, "start drawing "); + UINT colincr=0; + //factors to base 1024 + UINT fac=1024; + if (scaleafter > 1) { + colincr=3*scaleafter-3; + fac=1024/(scaleafter*scaleafter); + } + logger->log("BJpeg", Log::DEBUG, "jpeg draw scale %d image: %u %u, picture: %u %u", scaleafter,picturew,pictureh,jpegwidth,jpegheight); + while (cinfo.output_scanline < jpegheight) + { +// logger->log("BJpeg", Log::DEBUG, "%i", rowsread); + rowsread += jpeg_read_scanlines(&cinfo,&ptr,1); +#ifdef USE_BUFFER + ptr+=linelen; +#else + if (scaleafter > 1) { + if (rowsread % scaleafter != scaleafter-1) { + //this simple approach wold maybe forget scaleafter -1 lines at the end... + ptr+=linelen; + continue; + } + ptr=lbuf; + } + drawLine(sfc,ctl->finalRotation,ptr,scaleafter,picturew,pictureh,xpos,ypos,outy,linelen,colincr,scaleafter,fac); + outy++; + +#endif + } + sfc->endFastDraw(); + + logger->log("BJpeg", Log::DEBUG, "Done all jpeg_read"); + + jpeg_finish_decompress(&cinfo); + jpeg_memio_cleanup(&cinfo); + jpeg_destroy_decompress(&cinfo); + + logger->log("BJpeg", Log::DEBUG, "jpeg shutdown done"); + rdr->drawingDone(); + +#ifdef USE_BUFFER + UINT y; + //Tell the surface, that we will draw a lot of pixel, + //so that performance, can be optimized + sfc->startFastDraw(); + logger->log("BJpeg", Log::DEBUG, "jpeg start buffer draw" ); + unsigned char* p=buffer; //start of first row + UINT rowincr=linelen*scaleafter; + //for simplicity omit last line to avoid running out of buffer + for (y = 0; y < pictureh-1 ;y++) + { + drawLine(sfc,ctl->finalRotation,p,scaleafter,picturew,pictureh,xpos,ypos,y,linelen,colincr,scaleafter,fac); + p+=rowincr; + } + sfc->endFastDraw(); + logger->log("BJpeg", Log::DEBUG, "end draw"); + free(buffer); +#else + delete[] lbuf; +#endif + logger->log("BJpeg", Log::DEBUG, "deleted buffer"); + return true; +} + + +#else +bool WJpegComplex::drawJpeg(JpegControl * ctl,Surface * sfc,JpegReader *rdr, Colour & backgroundColour) { + return true; +} +#endif + +//get my own surface +Surface * WJpegComplex::getSurface(Region & r) { + r.x=getRootBoxOffsetX(); + r.y=getRootBoxOffsetY(); + r.w=area.w; + r.h=area.h; + return Boxx::getSurface(); +} + diff --git a/wjpegcomplex.h b/wjpegcomplex.h new file mode 100644 index 0000000..06f9ebd --- /dev/null +++ b/wjpegcomplex.h @@ -0,0 +1,236 @@ +/* + 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 WJPEGCOMPLEX_H +#define WJPEGCOMPLEX_H + +// This is the complex jpeg readeing stuff not supported on all plattforms + + +#include +#include + +#ifdef WIN32 + +#include +#include + + +//#define NEED_FAR_POINTERS +#define XMD_H //workaround some compiling issues +#endif + +class Surface; +class Boxx; + +//a reader to be implemented by the caller +class JpegReader { + public: + //read the next chunk of jpeg data + //offset - from start of file + //len I buf len (max bytes to read) + //return read len, 0 on EOF, -1 on error, *buf set to buffer + //will be released with free(!!!) after decoding + virtual ULONG readChunk(ULONG offset,ULONG len,char **buf)=0; + //a callback when the drawing is complete + //the implementation is optional + virtual void drawingDone(){}; + //get the size of the current picture + virtual ULONG getSize(){ return 0;} + virtual ~JpegReader(){}; +}; +class WJpegComplex : public WJpeg +{ + public: + + // temp for boxx + void setDimensions(int width, int height) {area.w=width;area.h=height;}; + + + WJpegComplex(); + virtual ~WJpegComplex(); + //old style usage - load local file + //the sequence is init(filename), draw + //the new usage is drawJpeg (with having the right offsets computed) + int init(const char* fileName); + void draw(); + + bool hasError(); + + //mode for scaling pictures + enum ScaleMode { + LETTER=0, + CROP=1, + CROPPERCENT=2}; + //rotations + enum Rotation{ + ROT_0=0, + ROT_90=1, + ROT_180=2, + ROT_270=3 + }; + //jpeg info + struct JpegControl { + public: + //the available drawing area + Region area; + bool enlarge; + //the maximum allowed scale factor after decompress + UINT scaleafter; + //the scale mode + enum ScaleMode mode; + //the size value if scaleMode==cropPercent + //%of the drawing area size + UINT scaleAmount; + //the rotation (user defined as input) + //if exif rotation is found this will be added + enum Rotation rotation; + + //paremeters filled during Jpeg parsing + enum Rotation exifRotation; + char exifDate[30]; + char error[100]; + UINT picw; + UINT pich; + ULONG compressedSize; + + //parameters computed to display picture + enum Rotation finalRotation; + //scale in % + UINT scale; + JpegControl() { + area.x=0; + area.y=0; + area.w=0; + area.h=0; + enlarge=false; + scaleafter=3; + scaleAmount=110; + mode=CROPPERCENT; + rotation=ROT_0; + exifRotation=ROT_0; + finalRotation=ROT_0; + exifDate[0]='\0'; + error[0]='\0'; + picw=0; + pich=0; + compressedSize=0; + scale=100; + } + }; + + //the standalone drawing function + //this will draw into the provided surface + //the reader has to be initialized before + //calling this function does not need a WJpeg being instantiated + //it simply draws into the surface + bool static drawJpeg(JpegControl * control, Surface* sfc, JpegReader *rdr, Colour & backgroundColour); + + private: + //our drawPixel with considers rotation + /* handle picture rotation + 90: xr=h-y + yr=x + 180:xr=w-x + yr=h-y + 270:xr=y + yr=w-x + */ +#ifndef __ANDROID__ + inline static void drawPixel(Surface * sfc,enum Rotation rotate,int x, int y,int w,int h,int xpos, int ypos,Colour c){ + int xb=0; + int yb=0; + switch(rotate) { + case ROT_0: + xb=x; + yb=y; + break; + case ROT_90: + xb=h-y; + yb=x; + break; + case ROT_180: + xb=w-x; + yb=h-y; + break; + case ROT_270: + xb=y; + yb=w-x; + break; + } + xb+=xpos; + yb+=ypos; + if (xb < 0 || yb < 0 ) { + return; + } + sfc->drawPixel((UINT)xb,(UINT)yb,c,true); + } + + /** + draw a line of pixels coming from the decompressor + if scaleafter > 1 we draw that many lines (numlines is the# lines in the buffer) + picturew is the resulting width of the picture + **/ + inline static void drawLine(Surface *sfc,enum Rotation rotate, unsigned char *cp,UINT scaleafter,UINT picturew,UINT pictureh, + UINT xpos, UINT ypos, UINT outy, UINT linelen,UINT pixeloffset, UINT numlines, UINT fac) { + Colour c; + for (UINT x = 0; x < picturew; x++) + { + if (scaleafter > 1 ) { + //boxfilter scalefactor*scalefactor + //take 0...scalefactor pixels in x and y direction + for (int colornum=0;colornum<3;colornum++) { + UINT comp=0; + unsigned char * accp=cp; + for (UINT rows=0;rows> 10; + if (colornum == 0) c.red=comp; + if (colornum == 1) c.green=comp; + if (colornum == 2) c.blue=comp; + cp++; + } + + } + else { + c.red = *cp;cp++; + c.green = *cp;cp++; + c.blue = *cp;cp++; + } + cp+=pixeloffset; + drawPixel(sfc,rotate,x, outy, picturew,pictureh,xpos,ypos,c); + } + } +#endif + //find my own surface and fill the area with my x and y offset within + Surface * getSurface(Region &a); + + JpegReader *reader; + bool owningReader; + char errbuf[200]; +}; + +#endif diff --git a/wjpegsimple.cc b/wjpegsimple.cc new file mode 100644 index 0000000..04095d1 --- /dev/null +++ b/wjpegsimple.cc @@ -0,0 +1,66 @@ +/* + Copyright 2004-2005 Chris Tallon + + This file is part of VOMP. + + VOMP is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + VOMP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with VOMP; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#include "boxx.h" +#include "wjpegsimple.h" +#include +#include +#include + + + +#include "i18n.h" +#include "log.h" +#include "surface.h" + + + + +WJpegSimple::WJpegSimple(){ + +} + +WJpegSimple::~WJpegSimple() { + +} + +int WJpegSimple::init(const char* tfileName) +{ + fileName=tfileName; + + return 1; +} + + + + + +void WJpegSimple::draw() +{ + int width,height; + width=height=1; + drawJpeg(fileName,0,0,&width,&height);//This should went into the abstract base classes? + //Windows has a problem with the leading / fixme + setDimensions(width, height); + +} + + + + diff --git a/wjpegsimple.h b/wjpegsimple.h new file mode 100644 index 0000000..21bf4dc --- /dev/null +++ b/wjpegsimple.h @@ -0,0 +1,50 @@ +/* + 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 WJPEGSIMPLE_H +#define WJPEGSIMPLE_H + +#include +#include +#include "wjpeg.h" + + +class WJpegSimple : public WJpeg +{ +public: + + // temp for boxx + void setDimensions(int width, int height) {area.w=width;area.h=height;}; + + + WJpegSimple(); + virtual ~WJpegSimple(); + // Only old style usage - load local file + + int init(const char* fileName); + void draw(); + +private: + const char* fileName; + + +}; + +#endif -- 2.39.5