/* * ambithread.c * * Copyright (C) 2013 - Christian Völlinger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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, see . */ #include #include "softhdservice.h" #include "ambithread.h" #include "config.h" //*************************************************************************** // Class cAmbiThread //*************************************************************************** //*************************************************************************** // Object //*************************************************************************** cAmbiThread::cAmbiThread() { loopActive = false; image = 0; imageSize = 0; imageWidth = 0; imageHeight = 0; cineBarsHor = 0; cineBarsVer = 0; } cAmbiThread::~cAmbiThread() { bob.close(); } //*************************************************************************** // Stop Thread //*************************************************************************** void cAmbiThread::Stop() { loopActive = false; waitCondition.Broadcast(); // wakeup the thread Cancel(3); // wait up to 3 seconds for thread was stopping } //*************************************************************************** // Action //*************************************************************************** void cAmbiThread::Action() { MsTime wait = 0; cMutexLock lock(&mutex); tell(0, "boblight Thread started (pid=%d)", getpid()); loopActive = true; bob.open(); while (loopActive && Running()) { MsTime start = msNow(); if(softhddeviceNotDetached() == fail) { bob.close(); waitCondition.TimedWait(mutex, 1000); continue; } // Softhddevice is not detached, work... if(bob.ping() == success) { if(cfg.dirty > 0) { cfg.dirty = 0; bob.sendOptions(); } switch(cfg.viewMode) { case vmAtmo: if (grabImage() == success) { detectCineBars(); putData(); MsTime elapsed = msNow() - start; wait = 1000 / cfg.frequence - elapsed; tell(3, "sleeping %ldms (%d Hz)", wait, cfg.frequence); } else { wait = 10000; // retry softhd grab every 10 seconds } break; case vmBlack: case vmFixedCol: putData(); wait = 500; // less load on fixed color or black break; case vmDetached: bob.close(); wait = 1000; break; default: break; } } else { // Connection lost, reconnect wait = 10000; switch(cfg.viewMode) { case vmDetached: bob.close(); break; default: bob.close(); bob.open(); wait = 5000; } } waitCondition.TimedWait(mutex, wait); // wait time in ms } bob.close(); loopActive = false; tell(0, "boblight thread ended (pid=%d)", getpid()); } int cAmbiThread::softhddeviceNotDetached() { cPlugin* softHdPlugin = cPluginManager::GetPlugin("softhddevice"); if(!softHdPlugin) { error("Can't find softhddevice"); return fail; } int reply_code = 0; cString reply_msg; reply_msg = softHdPlugin->SVDRPCommand("STAT", "", reply_code); if(910 == reply_code) { tell(1, "Softhddevice NOT detached: %d", reply_code); return success; } else { tell(1, "Softhddevice detached: %d", reply_code); return fail; } } //*************************************************************************** // Grab Image //*************************************************************************** int cAmbiThread::grabImage() { SoftHDDevice_AtmoGrabService_v1_1_t req; free(image); image = 0; cPlugin* softHdPlugin = cPluginManager::GetPlugin("softhddevice"); int softHdGrabService = (softHdPlugin && softHdPlugin->Service(ATMO1_GRAB_SERVICE, 0)); if (!softHdGrabService) return error("Can't find softhddevice %s, aborting grab, retrying in 10 seconds!", softHdPlugin ? "service" : "plugin"); // grab image at sofhddevice req.width = -64; //warum? steht hier => http://projects.vdr-developer.org/projects/plg-softhddevice/repository/revisions/master/entry/video.c#L7372 req.height = 64; req.img = 0; if (!softHdPlugin->Service(ATMO1_GRAB_SERVICE, &req) || !req.img) return fail; tell(3, "Got image with %dx%d pixel; %d bytes", req.width, req.height, req.size); image = (Pixel*)req.img; imageSize = req.size; imageWidth = req.width; imageHeight = req.height; return success; } //*************************************************************************** // Detect Cine Bars //*************************************************************************** int cAmbiThread::detectCineBars() { const int threshold = 3; // threshold for black level of cine bars Pixel* p; int off; // check horizontal bars if (cfg.detectCineBars == cbHorizontal || cfg.detectCineBars == cbBoth) { for (off = 0; off < imageHeight/5; off++) // cinebar height max 1/5 of the screen height { int above = 0; for (int x = 0; x < imageWidth; x++) { p = &image[off*imageWidth + x]; if (p->r > threshold || p->g > threshold || p->b > threshold) above++; p = &image[((imageHeight-1)-off)*imageWidth + x]; if (p->r > threshold || p->g > threshold || p->b > threshold) above++; } if (above > imageWidth/8) // max 1/8 failed pixel break; } if (cineBarsHor != off) { static int last = 0; static int count = 0; if (off != last) { last = off; count = 0; } if (count++ >= cfg.frequence) { count = 0; cineBarsHor = off; tell(0, "Switch horizontal cine bars to %d", cineBarsHor); } } } // check vertical bars if (cfg.detectCineBars == cbVertical || cfg.detectCineBars == cbBoth) { for (off = 0; off < imageWidth/5; off++) // cinebar height max 1/5 of the screen width { int above = 0; for (int y = 0; y < imageHeight; y++) { p = &image[y*imageWidth + off]; if (p->r > threshold || p->g > threshold || p->b > threshold) above++; p = &image[y*imageWidth + ((imageWidth-1)-off)]; if (p->r > threshold || p->g > threshold || p->b > threshold) above++; } if (above > imageHeight/6) // max 1/6 failed pixel break; } if (cineBarsVer != off) { static int last = 0; static int count = 0; if (off != last) { last = off; count = 0; } if (count++ >= cfg.frequence) { count = 0; cineBarsVer = off; tell(0, "Switch vertical cine bars to %d", cineBarsVer); } } } return done; } //*************************************************************************** // Put Data //*************************************************************************** int cAmbiThread::putData() { if (cfg.viewMode == vmFixedCol) { int pFixedCol[3] = {cfg.fixedR, cfg.fixedG, cfg.fixedB}; bob.writePix(pFixedCol); } else if (cfg.viewMode == vmBlack) { int pFixedCol[3] = {0,0,0}; bob.writePix(pFixedCol); } else if(cfg.viewMode == vmAtmo) { int row = 0; Pixel pixel = {0,0,0,0}; Pixel* p = &pixel; for (int y = 0; y < imageHeight; y++) { // skip horizontal cinebars if(y < cineBarsHor) continue; if(y > imageHeight - cineBarsHor) continue; int rgb[3]; row = imageWidth * y; for (int x = 0; x < imageWidth; x++) { // skip vertical cinebars if(x < cineBarsVer) continue; if(x > imageWidth - cineBarsVer) continue; p = &image[row + x]; rgb[0] = p->r; rgb[1] = p->g; rgb[2] = p->b; bob.writeColor(rgb, x - cineBarsVer, y - cineBarsHor); } } bob.setScanRange(imageWidth - (2*cineBarsVer), imageHeight - (2*cineBarsHor)); } bob.send(); return success; }