From: Marten Richter Date: Thu, 17 May 2012 08:13:43 +0000 (+0200) Subject: Add files specific for raspberry pi X-Git-Url: https://git.vomp.tv/gitweb/?a=commitdiff_plain;h=daf441ffaeee872b13838727600b811aa4cb0849;p=vompclient-marten.git Add files specific for raspberry pi --- 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