From 51d4ad259bb0d67810035dff2b619b7a2dc4fc0c Mon Sep 17 00:00:00 2001 From: Marten Richter Date: Sun, 20 Jul 2014 18:42:48 +0200 Subject: [PATCH] Move Pictureading to separate thread, prepare for HW accelarated Image reading --- command.cc | 11 +-- defines.h | 3 + message.h | 2 +- osdopenvg.cc | 54 ++++++---- osdopenvg.h | 7 +- osdvector.cc | 272 +++++++++++++++++++++++++++++++++++++++++---------- osdvector.h | 104 +++++++++++++++++--- vdr.cc | 4 +- vdr.h | 1 + 9 files changed, 364 insertions(+), 94 deletions(-) diff --git a/command.cc b/command.cc index b942f2c..ef50069 100644 --- a/command.cc +++ b/command.cc @@ -445,17 +445,14 @@ void Command::processMessage(Message* m) break; } - case Message::PICTURES_ARRIVED: + case Message::NEW_PICTURE: { - Log::getInstance()->log("Command", Log::DEBUG, "TVMedia Pictures arrived"); + Log::getInstance()->log("Command", Log::DEBUG, "TVMedia NEW_PICTURE"); OsdVector *osdv=dynamic_cast(Osd::getInstance()); if (osdv) { - osdv->processReceivedPictures(); - /* if (osdv->processReceivedPictures()) { - Log::getInstance()->log("Command", Log::DEBUG, "TVMedia Boxstack update triggered"); - boxstack->update(NULL,NULL); - }*/ + osdv->informPicture(m->tag,m->parameter); } + } break; } } diff --git a/defines.h b/defines.h index c5d6f79..8b62f5d 100644 --- a/defines.h +++ b/defines.h @@ -122,6 +122,9 @@ long long getTimeMS(); #define HANDLE_VT_SWITCHING #define GRADIENT_DRAWING + #define PICTURE_DECODER_MAGICK + #define PICTURE_DECODER_OMX + #define VOMP_LINUX_CLOCK CLOCK_MONOTONIC #endif diff --git a/message.h b/message.h index 1494129..e79a5a3 100644 --- a/message.h +++ b/message.h @@ -77,7 +77,7 @@ class Message const static ULONG TELETEXTUPDATEFIRSTLINE = 33; const static ULONG SUBTITLE_CHANGE_CHANNEL = 34; const static ULONG MOUSE_ANDROID_SCROLL = 35; - const static ULONG PICTURES_ARRIVED = 36; + const static ULONG NEW_PICTURE = 36; }; #endif diff --git a/osdopenvg.cc b/osdopenvg.cc index 207063a..12a0e6d 100644 --- a/osdopenvg.cc +++ b/osdopenvg.cc @@ -101,6 +101,7 @@ OsdOpenVG::~OsdOpenVG() int OsdOpenVG::init(void* device) { if (initted) return 0; + reader.init(); Video* video = Video::getInstance(); //window=*((HWND*)device); @@ -395,6 +396,7 @@ void OsdOpenVG::purgeAllReferences() int OsdOpenVG::shutdown() { + reader.shutdown(); if (!initted) return 0; initted = 0; @@ -1085,6 +1087,27 @@ unsigned int OsdOpenVG::handleTask(OpenVGCommand& command) //Log::getInstance()->log("OSD", Log::DEBUG, "Draw create file %d %d %x %d",command.param1,command.param2,vgGetError(),handle); return handle; } break; + case OVGcreateImageMemory: { + PictureInfo *info = (PictureInfo*) command.data; + VGImage handle; + //Log::getInstance()->log("OSD", Log::DEBUG, "TVMedia OVGcreateImageMemory"); + handle=vgCreateImage(VG_sXBGR_8888,info->width,info->height,VG_IMAGE_QUALITY_BETTER); + vgImageSubData(handle,info->image,info->width*4, + VG_sXBGR_8888,0,0,info->width,info->height); + info->decoder->freeReference(info->reference); + Message* m = new Message(); + // We have a pictures! send a message to ourself, to switch to gui thread + m->message=Message::NEW_PICTURE; + m->from=this; + m->to=Command::getInstance(); + m->parameter = handle; + m->tag = info->lindex; + Command::getInstance()->postMessageFromOuterSpace(m); // inform command about new picture + + delete info; + + } break; + case OVGcreateColorRef :{ VGPaint handle; handle=vgCreatePaint(); @@ -1156,14 +1179,17 @@ bool OsdOpenVG::processTasks() vgmutex.Lock(); while (vgcommands.size()>0) { - OpenVGCommand &comm=vgcommands.front(); + OpenVGCommand comm=vgcommands.front(); + vgcommands.pop_front(); + taskmutex.Unlock(); + OpenVGResponse resp; resp.result=handleTask(comm); resp.id=comm.id; + taskmutex.Lock(); if (comm.id) { vgresponses.push_back(resp); } - vgcommands.pop_front(); taskmutex.Unlock(); vgmutex.Unlock(); //threadCheckExit(); @@ -1257,7 +1283,6 @@ void OsdOpenVG::destroyImageRef(ImageIndex index) ImageIndex OsdOpenVG::createJpeg(const char* fileName, int *width,int *height) { Image* magicimage=NULL; - bool mem=false; struct OpenVGCommand comm; comm.task=OVGcreateImageFile; @@ -1291,26 +1316,19 @@ ImageIndex OsdOpenVG::createJpeg(const char* fileName, int *width,int *height) return putOpenVGCommand(comm,true); } -ImageIndex OsdOpenVG::createPicture(unsigned char *data, unsigned int length) +void OsdOpenVG::createPicture(struct PictureInfo& pict_inf) { - Image* magicimage=NULL; - bool mem=false; struct OpenVGCommand comm; - comm.task=OVGcreateImageFile; + if (pict_inf.type == PictureInfo::RGBAMemBlock) { + comm.task = OVGcreateImageMemory; + comm.data = new PictureInfo(pict_inf); + putOpenVGCommand(comm,false); + } else { + // unsupported + pict_inf.decoder->freeReference(pict_inf.reference); - try{ - // Now figure out, if it is a special case - //Log::getInstance()->log("OSD", Log::DEBUG, "createPicture"); - magicimage=new Image(Blob(data,length)); // fix me this is an unnecessary memcpy - free(data); - }catch( Exception &error_ ) - { - Log::getInstance()->log("OSD", Log::DEBUG, "Libmagick: %s",error_.what()); - return 0; } - comm.data=magicimage; - return putOpenVGCommand(comm,true); } diff --git a/osdopenvg.h b/osdopenvg.h index 051e173..afcaebc 100644 --- a/osdopenvg.h +++ b/osdopenvg.h @@ -51,7 +51,8 @@ enum OpenVGTask { OVGcreateColorRef, OVGimageUploadLine, OVGcreateImageFile, - OVGreplacefont + OVGreplacefont, + OVGcreateImageMemory }; struct OpenVGCommand @@ -94,12 +95,14 @@ class OsdOpenVG : public OsdVector, public Thread_TYPE protected: + + /*osd vector implementation*/ void destroyImageRef(ImageIndex index); ImageIndex createJpeg(const char* fileName, int *width,int *height); ImageIndex createMonoBitmap(void *base,int width,int height); ImageIndex createImagePalette(int width,int height,const unsigned char *image_data,const unsigned int*palette_data); - ImageIndex createPicture(unsigned char *data, unsigned int length); + void createPicture(struct PictureInfo& pict_inf); void destroyStyleRef(unsigned int index); unsigned int createStyleRef(const DrawStyle &c); unsigned int createColorRef(const Colour &c); diff --git a/osdvector.cc b/osdvector.cc index 8012ff6..1b1bd76 100644 --- a/osdvector.cc +++ b/osdvector.cc @@ -25,15 +25,90 @@ #include "command.h" #include "message.h" +// The next section is activated, if the magick++ PictureReader is provided, it should be available for many POSIX platforms +#ifdef PICTURE_DECODER_MAGICK +#include + +using namespace Magick; + +class MagickDecoder: public OsdVector::PictureDecoder { +public: + MagickDecoder(OsdVector::PictureReader* treader): OsdVector::PictureDecoder(treader) {pictInfValid=false;}; + + bool decodePicture(LoadIndex index, unsigned char * buffer, unsigned int length); + + bool getDecodedPicture( struct OsdVector::PictureInfo& pict_inf); + + void freeReference(void * ref); + +protected: + OsdVector::PictureInfo pictInf; + bool pictInfValid; +}; + +bool MagickDecoder::decodePicture(LoadIndex index, unsigned char * buffer, unsigned int length) +{ + if (pictInfValid) return false; // does support only one image at a Time; + Image magicimage; + Blob *imageblob = new Blob(); + + try{ + Log::getInstance()->log("MagickDecoder", Log::DEBUG, "decodePicture"); + Blob myblob; + myblob.updateNoCopy(buffer,length,Blob::MallocAllocator); + magicimage.read(myblob); + + magicimage.write(imageblob,"RGBA"); + + }catch( Exception &error_ ) + { + Log::getInstance()->log("MagickDecoder", Log::DEBUG, "Libmagick: %s",error_.what()); + delete imageblob; + + return false; + } + pictInf.reference = (void*) imageblob; + pictInf.width = magicimage.columns(); + pictInf.height = magicimage.rows(); + pictInf.image = imageblob->data(); + pictInf.decoder = this; + pictInf.type = OsdVector::PictureInfo::RGBAMemBlock; + pictInf.lindex = index; + pictInfValid = true; + + + + // I can handle everything, so the return value is always true + return true; +} +void MagickDecoder::freeReference(void * ref) +{ + Blob *todelete = (Blob*) ref; + delete todelete; +} + +bool MagickDecoder::getDecodedPicture(struct OsdVector::PictureInfo& pict_inf) +{ + if (!pictInfValid) return false; + pict_inf=pictInf; + pictInfValid = false; + return true; +} + + +#endif + OsdVector::OsdVector() { setlocale(LC_CTYPE,"C.UTF-8"); - picture_update=true; +#ifdef PICTURE_DECODER_MAGICK + reader.addDecoder(new MagickDecoder(&reader)); +#endif + } OsdVector::~OsdVector() { - } @@ -268,7 +343,7 @@ void OsdVector::removeImageRef(const ImageIndex ref) images_ref[ref]--; } -unsigned int OsdVector::getLoadIndexRef(LoadIndex index) +int OsdVector::getLoadIndexRef(LoadIndex index) { if (loadindex_ref.find(index)==loadindex_ref.end()) { return -1; @@ -298,15 +373,21 @@ void OsdVector::removeLoadIndexRef(const LoadIndex ref) tvmedias_loaded.erase(ref); tvmedias_load.erase(tvmedias_load_inv[ref]); tvmedias_load_inv.erase(ref); + + reader.invalidateLoadIndex(ref); + + } } + + void OsdVector::cleanupOrphanedRefs() { // Do some garbage collection map::iterator mitty=monobitmaps.begin(); while (mitty!=monobitmaps.end()) { - map::iterator curitty=images_ref.find((*mitty).second); + map::iterator curitty=images_ref.find((*mitty).second); int count=(*curitty).second; if (count==0) { ImageIndex ref=(*curitty).first; @@ -318,7 +399,7 @@ void OsdVector::cleanupOrphanedRefs() map::iterator jitty=jpegs.begin(); while (jitty!=jpegs.end()) { - map::iterator curitty=images_ref.find((*jitty).second); + map::iterator curitty=images_ref.find((*jitty).second); int count=(*curitty).second; if (count==0) { ImageIndex ref=(*curitty).first; @@ -330,11 +411,9 @@ void OsdVector::cleanupOrphanedRefs() map::iterator titty=tvmedias.begin(); while (titty!=tvmedias.end()) { - map::iterator curitty=images_ref.find((*titty).second); + map::iterator curitty=images_ref.find((*titty).second); int count=(*curitty).second; if (count==0) { - - Log::getInstance()->log("OsdVector", Log::DEBUG, "TVMedia destroy Picture"); ImageIndex ref=(*curitty).first; tvmedias.erase(titty++); images_ref.erase(curitty++); @@ -345,7 +424,7 @@ void OsdVector::cleanupOrphanedRefs() map,unsigned int>::iterator sitty=styles.begin(); while (sitty!=styles.end()) { - map::iterator curitty=styles_ref.find((*sitty).second); + map::iterator curitty=styles_ref.find((*sitty).second); int count=(*curitty).second; if (count==0) { unsigned int ref=(*curitty).first; @@ -359,7 +438,7 @@ void OsdVector::cleanupOrphanedRefs() } -unsigned int OsdVector::getImageRef(ImageIndex index) +int OsdVector::getImageRef(ImageIndex index) { if (images_ref.find(index)==images_ref.end()) { return -1; @@ -417,7 +496,7 @@ unsigned int OsdVector::getColorRef(const Colour &c) return style_handle; } -unsigned int OsdVector::getStyleRef(unsigned int index) +int OsdVector::getStyleRef(unsigned int index) { if (styles_ref.find(index)==styles_ref.end()) { return -1; @@ -466,19 +545,26 @@ LoadIndex OsdVector::loadTVMedia(TVMediaInfo& tvmedia) return index; } -void OsdVector::setTVMedia(LoadIndex index, unsigned char * buffer, unsigned int length) + + +void OsdVector::informPicture(LoadIndex index, ImageIndex imageIndex) { //Beware for thread safety ImageIndex image_index=0; TVMediaInfo tvmedia=tvmedias_load_inv[index]; Log::getInstance()->log("OsdVector", Log::DEBUG, "TVMedia Picture for request id %d arrived",index); - if (buffer) { - if (getLoadIndexRef(index)<1) { + if (imageIndex) { + image_index=tvmedias[tvmedia]=imageIndex; + tvmedias_loaded[index]=image_index; + if (getLoadIndexRef(index) < 1) { // we do not want the picture anymore . Really... + // fill images_ref in to not irritate the garbage collector + if (getImageRef(image_index) < 0) { + images_ref[image_index]=0; + } } else { - image_index=tvmedias[tvmedia]=createPicture(buffer,length); - tvmedias_loaded[index]=image_index; + incImageRef(image_index); // hold one index until all loadings refs are gone; } } @@ -488,6 +574,8 @@ void OsdVector::setTVMedia(LoadIndex index, unsigned char * buffer, unsigned int + + ImageIndex OsdVector::getJpegRef(const char* fileName, int *width,int *height) { ImageIndex image_handle=0; @@ -532,55 +620,139 @@ ImageIndex OsdVector::getImagePalette(int width,int height,const unsigned char return image_handle; } -void OsdVector::receivePicture(VDR_ResponsePacket *vresp) +OsdVector::PictureReader::~PictureReader() +{ + decoders_lock.Lock(); + while ( decoders.size()) { + PictureDecoder* dec=decoders.front(); + decoders.pop_front(); + delete dec; + } + + decoders_lock.Unlock(); +} + +void OsdVector::PictureReader::init() +{ + threadStart(); +} + +void OsdVector::PictureReader::shutdown() +{ + threadStop(); + + +} + +void OsdVector::PictureReader::addDecoder(PictureDecoder* decoder) +{ + decoders_lock.Lock(); + decoders.push_front(decoder); + decoders_lock.Unlock(); +} + +void OsdVector::PictureReader::threadMethod() +{ + OsdVector *osdvector = dynamic_cast(Osd::getInstance()); + Log::getInstance()->log("OsdVector", Log::DEBUG, "TVMedia Start Picture Reader"); + while (true) { + if (!threadIsActive()) { + Log::getInstance()->log("OsdVector", Log::DEBUG, "TVMedia End Picture Reader"); + threadCheckExit(); + } + + bool todos=true; + while (todos) + { + todos=false; + PictureInfo pictinf; + decoders_lock.Lock(); + std::list::iterator itty=decoders.begin(); + + while (itty!=decoders.end()) { + if ((*itty)->getDecodedPicture(pictinf)) { + todos = true; + osdvector->createPicture(pictinf); + } + + itty++; + } + if (processReceivedPictures()) + { + todos = true; + } + + decoders_lock.Unlock(); + } + //Log::getInstance()->log("OsdVector", Log::DEBUG, "TVMedia Sleep Picture Reader"); + + threadLock(); + threadWaitForSignal(); + threadUnlock(); + //Log::getInstance()->log("OsdVector", Log::DEBUG, "TVMedia Sleep end Picture Reader"); + } +} + +void OsdVector::PictureReader::threadPostStopCleanup() +{ + +} + +void OsdVector::PictureReader::invalidateLoadIndex(LoadIndex index) +{ + pict_lock_incoming.Lock(); + invalid_loadindex.insert(index); + pict_lock_incoming.Unlock(); +} + +void OsdVector::PictureReader::receivePicture(VDR_ResponsePacket *vresp) { pict_lock_incoming.Lock(); pict_incoming.push(vresp); pict_lock_incoming.Unlock(); - //Log::getInstance()->log("OsdVector", Log::DEBUG, "TVMedia Pictures arrived"); - if (picture_update) { - Message* m = new Message(); - // This is incoming from VDR, we do not want to block gui so send a message to ourself to switch to gui thread - m->message=Message::PICTURES_ARRIVED; - m->from=this; - m->to=Command::getInstance(); - picture_update=false; - Command::getInstance()->postMessageFromOuterSpace(m); // inform command about new picture - } + threadSignal(); } -bool OsdVector::processReceivedPictures() +bool OsdVector::PictureReader::processReceivedPictures() { - bool ret=false; + bool decoded = false; + bool valid = true; pict_lock_incoming.Lock(); - int i=0; - long long time1 = getTimeMS(); - long long cur_time =time1; - while (pict_incoming.size() && (time1-cur_time)<100) { + if (pict_incoming.size()) { VDR_ResponsePacket *vresp=pict_incoming.front(); pict_incoming.pop(); + set::iterator setpos = invalid_loadindex.find(vresp->getStreamID()); + if (setpos != invalid_loadindex.end()) { + valid = false; + invalid_loadindex.erase(setpos); + } pict_lock_incoming.Unlock(); + if (!valid) { // we do not want it anymore skip it; + delete vresp; + return true; + } if (vresp->getFlag() != 2) { - setTVMedia(vresp->getStreamID(), vresp->getUserData(), vresp->getUserDataLength()); - ret=true; + UCHAR *userdata=vresp->getUserData(); + ULONG length=vresp->getUserDataLength(); + std::list::iterator itty=decoders.begin(); + while (itty!=decoders.end()) { + if ((*itty)->decodePicture(vresp->getStreamID(), + userdata, length)){ + decoded=true; + break; + } + itty++; + } + if (!decoded ){ + free(vresp->getUserData()); + } } - else setTVMedia(vresp->getStreamID(), NULL, 0); + //else osd->informPicture(vresp->getStreamID(), 0); delete vresp; - cur_time = getTimeMS(); - pict_lock_incoming.Lock(); + } else { + pict_lock_incoming.Unlock(); } - if (pict_incoming.size()==0) picture_update=true; - pict_lock_incoming.Unlock(); - if (!picture_update) { - Message* m = new Message(); - // We have remaing pictures! send a message to ourself to switch to gui thread - m->message=Message::PICTURES_ARRIVED; - m->from=this; - m->to=Command::getInstance(); - picture_update=false; - Command::getInstance()->postMessageNoLock(m); // inform command about new picture - } - return ret; + return decoded; } diff --git a/osdvector.h b/osdvector.h index 6221ae5..57d449b 100644 --- a/osdvector.h +++ b/osdvector.h @@ -23,6 +23,7 @@ #include "osd.h" #include "mutex.h" #include "colour.h" +#include #include #include #include @@ -237,57 +238,132 @@ class OsdVector : public Osd virtual void removeStyleRef(unsigned int ref); virtual void getScreenSize(int &width, int &height)=0; - // should be called from command thread - bool processReceivedPictures(); + // should be only called from command thread + void informPicture(LoadIndex index, ImageIndex i_index); + - void receivePicture(VDR_ResponsePacket *vresp); int charSet() {return 2;}; //UTF-8 + class PictureDecoder; + struct PictureInfo + { + enum PictType { + RGBAMemBlock, + EGLImage + }; + PictType type; + ULONG width; + ULONG height; + LoadIndex lindex; + const void * image; + void *reference; + PictureDecoder* decoder; + }; + + + class PictureReader; + + class PictureDecoder + { + public: + PictureDecoder(PictureReader * treader) {reader=treader;}; + + // its is always guaranted, that after getDecodedPicture a call to decodePicture follows, if the return value was true; + virtual bool decodePicture(LoadIndex index, unsigned char * buffer, unsigned int length)=0; + + virtual bool getDecodedPicture(struct PictureInfo& pict_inf)=0; + virtual void freeReference(void * ref)=0; + + protected: + PictureReader * reader; + }; + + class PictureReader: public Thread_TYPE { + public: + + ~PictureReader(); + + void init(); + void addDecoder(PictureDecoder*); + + void shutdown(); + + + bool processReceivedPictures(); + + // should be called from command thread + void receivePicture(VDR_ResponsePacket *vresp); + + void invalidateLoadIndex(LoadIndex index); + + + + + protected: + + void threadMethod(); + void threadPostStopCleanup(); + + Mutex pict_lock_incoming; //locks + Mutex decoders_lock; + std::queue pict_incoming; + std::list decoders; + set invalid_loadindex; + + bool picture_update; + + }; + + PictureReader *getPictReader() { return &reader;}; + + protected: + PictureReader reader; + void incImageRef(ImageIndex index); - unsigned int getImageRef(ImageIndex index); + int getImageRef(ImageIndex index); virtual void destroyImageRef(ImageIndex index)=0; void incLoadIndexRef(LoadIndex index); - unsigned int getLoadIndexRef(LoadIndex index); + int getLoadIndexRef(LoadIndex index); virtual ImageIndex createJpeg(const char* fileName, int *width,int *height)=0; virtual ImageIndex createMonoBitmap(void *base,int width,int height)=0; virtual ImageIndex createImagePalette(int width,int height,const unsigned char *image_data,const unsigned int*palette_data)=0; - virtual ImageIndex createPicture(unsigned char *data, unsigned int length)=0; + virtual void createPicture(struct PictureInfo& pict_inf)=0; virtual LoadIndex loadTVMedia(TVMediaInfo& tvmedia); - map images_ref; + map images_ref; map monobitmaps; map jpegs; map tvmedias; - void setTVMedia(LoadIndex index, unsigned char * buffer, unsigned int length); - Mutex pict_lock_incoming; //locks - std::queue pict_incoming; - bool picture_update; - map loadindex_ref; + + + map loadindex_ref; map tvmedias_load; map tvmedias_load_inv; map tvmedias_loaded; + + void incStyleRef(unsigned int index); - unsigned int getStyleRef(ImageIndex index); + int getStyleRef(ImageIndex index); virtual void destroyStyleRef(unsigned int index)=0; map,unsigned int> styles; - map styles_ref; + map styles_ref; virtual unsigned int createStyleRef(const DrawStyle &c)=0; virtual unsigned int createColorRef(const Colour &c)=0; diff --git a/vdr.cc b/vdr.cc index 1a3fbac..4d5fb9e 100644 --- a/vdr.cc +++ b/vdr.cc @@ -40,7 +40,7 @@ #include "movieinfo.h" #include "seriesinfo.h" -#define VOMP_PROTOCOLL_VERSION 0x00000300 +#define VOMP_PROTOCOLL_VERSION 0x00000302 VDR* VDR::instance = NULL; //prepare a request @@ -617,7 +617,7 @@ bool VDR_PacketReceiver::call(void* userTag, bool & deleteme) VDR_ResponsePacket* vresp = (VDR_ResponsePacket*)userTag; Log::getInstance()->log("VDR", Log::DEBUG, "TVMedia Pictures arrived VDR"); OsdVector *osd=dynamic_cast(Osd::getInstance()); - if (osd) osd->receivePicture(vresp); + if (osd) osd->getPictReader()->receivePicture(vresp); else delete vresp; //nonsense deleteme=false; return true; diff --git a/vdr.h b/vdr.h index f322051..cd46f18 100644 --- a/vdr.h +++ b/vdr.h @@ -209,6 +209,7 @@ class VDR : public Thread_TYPE, public EventDispatcher, public MediaProvider, pu MovieInfo *getScraperMovieInfo(int movieID); SeriesInfo *getScraperSeriesInfo(int seriesID, int episodeID); ULONG loadTVMedia(TVMediaInfo& tvmedia); + void invalidateTVMedia(ULONG loadindex); I18n::lang_code_list getLanguageList(); -- 2.39.5