/* * Image plugin to VDR (C++) * * (C) 2004 Andreas Brachold * 2003 Kai Tobias Burwieck * * This code is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This code is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * Or, point your browser to http://www.gnu.org/copyleft/gpl.html */ #include #include #include #include #include #include #include #include "player-image.h" #include "control-image.h" #include "setup-image.h" #include "data-image.h" #include "image.h" #include "list.h" #include "i18n.h" #include "libimage/pnm.h" #include "libimage/xpm.h" const char *g_szConvertScript = "imageplugin.sh"; //----------cImagePlayer------------- cImagePlayer::cImagePlayer(cSlideShow *pCurSlideShow) : cStillImagePlayer( #if VDRVERSNUM >= 10308 (ImageSetup.m_bLiveAudio != 0)?pmVideoOnly: #endif pmAudioVideo ) , m_bConvertRunning(false) , m_szError(NULL) { theSlideShow.Assign(pCurSlideShow); } bool cImagePlayer::GetIndex(int &nCurrent, int &nTotal, bool /*bSnapToIFrame*/) { // Notify other status-plugins with one picture as one second nCurrent = theSlideShow.ImageCurrent(); // Notify other status-plugins count of picture as totalcount of seconds nTotal = theSlideShow.ImageTotal(); return true; } cImagePlayer::~cImagePlayer() { // Remove Slideshow theSlideShow.Shutdown(); } void cImagePlayer::Activate(bool bOn) { if(bOn) { if(theSlideShow.GetImage()) { cStillImagePlayer::Activate(bOn); Convert(""); } } else cStillImagePlayer::Activate(false); } bool cImagePlayer::NextImage(int nOffset) { return theSlideShow.NextImage(nOffset); } bool cImagePlayer::PrevImage(int nOffset) { return theSlideShow.PrevImage(nOffset); } bool cImagePlayer::GotoImage(unsigned int nNewPictureIndex) { return theSlideShow.GotoImage(nNewPictureIndex); } bool cImagePlayer::Convert(const char *szChange) { cImage* pImage = theSlideShow.GetImage(); if(pImage) { cShellWrapper* pCmd = new cShellWrapper; pCmd->bClearBackground = true; pCmd->nOffLeft = m_StillImage.GetBorderWidth(); pCmd->nOffTop = m_StillImage.GetBorderHeight(); pCmd->nWidth = UseWidth(); pCmd->nHeight = UseHeight(); // Build image_convert.sh "source.jpg" "/tmp/image/dest.pnm" 720 576 0 0 0 0 original pCmd->szPNM = strdup(pImage->NamePNM()); asprintf(&pCmd->szCmd, "%s \"%s\" \"%s\" %d %d %d %d %d %s", g_szConvertScript, pImage->Name(), pCmd->szPNM, pCmd->nWidth, pCmd->nHeight, 0, 0, 0, szChange ? szChange : ""); Exec(pCmd); return true; } return false; } bool cImagePlayer::ConvertJump(int nOffset) { register unsigned int w,h; const unsigned int MAX_BILDER = 9; cImage* pImage[MAX_BILDER]; for (w = 0; w < MAX_BILDER; ++w) pImage[w] = NULL; int nBilder = theSlideShow.GetJumpNames(nOffset,pImage,MAX_BILDER); if(nBilder > 0 && pImage[0]) { unsigned int nMatrix = (nBilder < 5) ? 2 : 3; for (h = 0; h < nMatrix; ++h) for (w = 0; w < nMatrix && pImage[(h*nMatrix)+w]; ++w) { cShellWrapper* pCmd = new cShellWrapper; pCmd->bClearBackground = (w == 0 && h == 0); pCmd->nWidth = UseWidth(); pCmd->nHeight = UseHeight(); pCmd->nWidth /= nMatrix; pCmd->nHeight /= nMatrix; pCmd->nOffLeft = (pCmd->nWidth * w) + m_StillImage.GetBorderWidth(); pCmd->nOffTop = (pCmd->nHeight * h) + m_StillImage.GetBorderHeight(); // Build image_convert.sh "source.jpg" "/tmp/image/source.jpg-i.pnm" 256 192 0 0 0 0 original pCmd->szPNM = strdup(pImage[(h*nMatrix)+w]->NameIndex()); asprintf(&pCmd->szCmd, "%s \"%s\" \"%s\" %d %d %d %d %d %s", g_szConvertScript, pImage[(h*nMatrix)+w]->Name(), pCmd->szPNM, pCmd->nWidth, pCmd->nHeight, 0, 0, 0, /*szChange ? szChange : */""); pCmd->szNumber = '0'+((h*nMatrix)+w)+1; Exec(pCmd); } return true; } return false; } bool cImagePlayer::ConvertZoom(const char *szChange, int nZoomFaktor, int nLeftPos, int nTopPos) { cImage* pImage = theSlideShow.GetImage(); if(pImage) { cShellWrapper* pCmd = new cShellWrapper; pCmd->bClearBackground = true; pCmd->nOffLeft = m_StillImage.GetBorderWidth() + (nLeftPos>0?0:(nLeftPos*-1)); pCmd->nOffTop = m_StillImage.GetBorderHeight() + (nTopPos>0?0:(nTopPos*-1)); pCmd->nWidth = UseWidth(); pCmd->nHeight = UseHeight(); // Build image_convert.sh "source.jpg" "/tmp/image/dest.pnm" 720 576 0 0 0 0 original pCmd->szPNM = strdup(pImage->NameZoom()); asprintf(&pCmd->szCmd, "%s \"%s\" \"%s\" %d %d %d %d %d %s", g_szConvertScript, pImage->Name(), pCmd->szPNM, pCmd->nWidth, pCmd->nHeight, nZoomFaktor, nLeftPos>0?nLeftPos:0, nTopPos>0?nTopPos:0, szChange ? szChange : ""); Exec(pCmd); return true; } return false; } void cImagePlayer::LoadImage(cShellWrapper* pShell) { cPNM pnmImage; bool bSuccess = false; register unsigned int nHeight = m_StillImage.GetHeight(); register unsigned int nWidth = m_StillImage.GetWidth(); register unsigned int nOffLeft = 0; register unsigned int nOffTop = 0; errno = 0; if(!pShell || pShell->bClearBackground) m_StillImage.ClearRGBMem(); if(pShell && pShell->szPNM) { nHeight = std::min(nHeight,pShell->nHeight); nWidth = std::min(nWidth,pShell->nWidth); nOffLeft = pShell->nOffLeft; nOffTop = pShell->nOffTop; FILE *f=fopen(pShell->szPNM, "r"); if(f) { xel* pRow = NULL; register unsigned int w; register unsigned int h; if(pnmImage.readHeader(f)) { register unsigned int nColorDepth = pnmImage.GetColorDepth(); if(pnmImage.GetWidth() < nWidth) nOffLeft += (nWidth - pnmImage.GetWidth()) / 2; if(pnmImage.GetHeight() < nHeight) nOffTop += (nHeight - pnmImage.GetHeight()) / 2; for(h = 0; h < pnmImage.GetHeight() && h < nHeight && h+nOffTop < m_StillImage.GetHeight(); ++h) { if(!pnmImage.allocrow(&pRow) ||!pnmImage.readrow(f, pRow) ) break; xel* pP = pRow; for(w = 0;w < pnmImage.GetWidth() && w < nWidth && w+nOffLeft < m_StillImage.GetWidth(); ++w,++pP) { uint8_t* pImageRGB = m_StillImage.GetRGBMem() + ((((h+nOffTop)*m_StillImage.GetWidth())+w+nOffLeft)*3); if(nColorDepth == 0xFF) // normal 8-bit at any canal (24 Bit) colordepth { *(pImageRGB + 0) = (uint8_t) PPM_GETR(*pP); *(pImageRGB + 1) = (uint8_t) PPM_GETG(*pP); *(pImageRGB + 2) = (uint8_t) PPM_GETB(*pP); } else if(nColorDepth == 1) // black/white image { *(pImageRGB + 0) = (uint8_t) PPM_GETR(*pP)==0?0x00:0xFF; *(pImageRGB + 1) = (uint8_t) PPM_GETG(*pP)==0?0x00:0xFF; *(pImageRGB + 2) = (uint8_t) PPM_GETB(*pP)==0?0x00:0xFF; } else // Adjust other colordepth to 8bit { *(pImageRGB + 0) = (uint8_t) (PPM_GETR(*pP)*255 / pnmImage.GetColorDepth()) & 0xFF; *(pImageRGB + 1) = (uint8_t) (PPM_GETG(*pP)*255 / pnmImage.GetColorDepth()) & 0xFF; *(pImageRGB + 2) = (uint8_t) (PPM_GETB(*pP)*255 / pnmImage.GetColorDepth()) & 0xFF; } } pnmImage.freerow(pRow); pRow = NULL; } if(pRow) pnmImage.freerow((char*)pRow); else { if(pShell->szNumber && ImageSetup.ShowNumbers) cXPM::Overlay(pShell->szNumber,m_StillImage.GetRGBMem(), m_StillImage.GetWidth(),m_StillImage.GetHeight(), cXPM::TopRight,nOffLeft,nOffTop,pnmImage.GetWidth(),pnmImage.GetHeight()); bSuccess = true; } } fclose(f); } } if(!bSuccess) { // Merge Errorimage with Encoder-Memory if(pShell && pShell->szNumber) cXPM::Overlay('s',m_StillImage.GetRGBMem(), m_StillImage.GetWidth(),m_StillImage.GetHeight(), cXPM::Center,pShell->nOffLeft,pShell->nOffTop,pShell->nWidth,pShell->nHeight); else cXPM::Error(m_StillImage.GetRGBMem(), m_StillImage.GetWidth(),m_StillImage.GetHeight()); // Build Error, depends PNM Errormsg or system messages char szErr[128]; if(pnmImage.GetError()) strncpy(szErr,pnmImage.GetError(),sizeof(szErr)); else if(errno) { int nErr = errno; szErr[sizeof(szErr)-1] = '\0'; if(0 != strerror_r(nErr,szErr,sizeof(szErr)-1)) { szErr[0] = '\0'; } } // Make Syslog entry if(pShell && pShell->szPNM && szErr) esyslog("imageplugin: Error until read %s : '%s'", pShell->szPNM, szErr); else if(pShell && pShell->szPNM) esyslog("imageplugin: Error until read %s", pShell->szPNM); else esyslog("imageplugin: Error until read image %s",szErr?szErr:""); { // Copy Errormessage forward to OSD Thread cMutexLock lock(&m_MutexErr); if(m_szError) free(m_szError); if(szErr) asprintf(&m_szError, "%s : %s", tr("Image couldn't load"),szErr); else m_szError = strdup(tr("Image couldn't load")); } } } //////////////////////////////////////////////////////////////////////////////// /** void cImagePlayer::ExecFailed() Show a precompiled Image if Exec can't find a converted Image @param - char* szErr @return - nothing */ void cImagePlayer::ExecFailed(cShellWrapper* pShell,const char* szErr) { if(!pShell || pShell->bClearBackground) m_StillImage.ClearRGBMem(); // Merge Errorimage with Encoder-Memory if(pShell && pShell->szNumber) cXPM::Overlay('s',m_StillImage.GetRGBMem(), m_StillImage.GetWidth(),m_StillImage.GetHeight(), cXPM::Center,pShell->nOffLeft,pShell->nOffTop,pShell->nWidth,pShell->nHeight); else cXPM::Error(m_StillImage.GetRGBMem(), m_StillImage.GetWidth(),m_StillImage.GetHeight()); // Store Message for OSD-Thread { cMutexLock lock(&m_MutexErr); if(m_szError) free(m_szError); m_szError = strdup(szErr); } } //////////////////////////////////////////////////////////////////////////////// /** void cImagePlayer::ErrorMsg() ThreadSafe Method to show messages from Worker thread, this functions is called only from cImageControl::ProcessKey(kNone) @return - nothing */ void cImagePlayer::ErrorMsg() { char* szErr = NULL; { cMutexLock lock(&m_MutexErr); szErr = m_szError; m_szError = NULL; } if(szErr) { OSD_ErrorMsg(szErr); free(szErr); } } const char* cImagePlayer::FileName(void) const { cImage* pImage = theSlideShow.GetImage(); return pImage?pImage->Name():NULL; } void cImagePlayer::Exec(cShellWrapper* pCmd) { if(pCmd->szPNM) m_bConvertRunning = true; if(pCmd) { cMutexLock lock(&m_Mutex); if(!m_queue.add(pCmd)) delete pCmd; //Queue full or Thread is dead } } bool cImagePlayer::Worker(bool bDoIt) { bool bQueueEmpty; cShellWrapper *pShell = NULL; { // Protect the queue ++ cMutexLock lock(&m_Mutex); if(bDoIt && !m_queue.empty()) { pShell = *m_queue.begin(); m_queue.erase(m_queue.begin()); } bQueueEmpty = m_queue.empty(); } // ++ if(!pShell) { m_bConvertRunning = m_StillImage.EncodeRequired(); return bQueueEmpty; } if(pShell->szCmd) { //dsyslog("imageplugin: executing script '%s'", pShell->szCmd); ImageSetup.SetEnv(); if(0 == SystemExec(pShell->szCmd)) { if(pShell->szPNM) { LoadImage(pShell); m_StillImage.EncodeRequired(true); } } else { esyslog("imageplugin: script execution failed '%s'", pShell->szCmd); if(pShell->szPNM) { ExecFailed(pShell,tr("Script execution failed")); m_StillImage.EncodeRequired(true); } } } delete pShell; return bQueueEmpty; }