/*
* 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;
xBarHeight = 0;
yBarWidth = 0;
lastxBarHeight = 0;
lastyBarWidth = 0;
barsChanged = true;
softHdPlugin = cPluginManager::GetPlugin("softhddevice");
int softHdGrabService = (softHdPlugin && softHdPlugin->Service(ATMO1_GRAB_SERVICE, 0));
if (!softHdGrabService)
error("Can't find softhddevice %s!", softHdPlugin ? "service" : "plugin");
}
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;
MsTime lastPing = 0;
MsTime lastBoarderDetect = 0;
int lastPingResult = na;
cMutexLock lock(&mutex);
tell(0, "boblight Thread started (pid=%d)", getpid());
loopActive = true;
bob.open();
while (loopActive && Running())
{
MsTime start = msNow();
if(ShutdownHandler.IsUserInactive() || softhddeviceNotDetached() == fail)
{
bob.close();
waitCondition.TimedWait(mutex, 1000);
continue;
}
// Reduce load, just ping every second
if(start - lastPing > 1000) {
lastPing = start;
lastPingResult = bob.ping();
}
// Softhddevice is not detached, work...
if(lastPingResult == success) {
if(cfg.dirty > 0) {
cfg.dirty = 0;
bob.sendOptions();
}
switch(cfg.viewMode)
{
case vmAtmo:
if (grabImage() == success)
{
if(start - lastBoarderDetect > 5000) {
lastBoarderDetect = start;
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()
{
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;
// grab image at sofhddevice
req.width = 64;
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()
{
/*
Annahme: Wenn im mittleren oberen und unterem Drittel des Bildes alle aufeinander folgenden Pixel schwarz sind
haben wir einen Horizontalen Balken.
|0|x x|0|
|y|0 0|y|
|y|0 0|y|
|0|x x|0|
*/
if (cfg.detectCineBars == cbNone) {
return done;
}
Pixel* p;
const int xOffset = imageWidth / 4;
const int yOffset = imageHeight / 4;
int tempxBarHeight = 0;
int tempyBarWidth = 0;
if (cfg.detectCineBars == cbHorizontal || cfg.detectCineBars == cbBoth) {
// check for xBar
for (int y = 0; y < yOffset; ++y) {
int row = imageWidth * y;
int xBarCount = 0;
for (int x = xOffset; x < imageWidth - xOffset; ++x) {
p = &image[row + x];
if(p->isBlack(cfg.cineBarsThreshold)) {
++xBarCount;
}
}
if(xBarCount == imageWidth - (2*xOffset)){
++tempxBarHeight;
}
else {
break;
}
}
}
if (cfg.detectCineBars == cbVertical || cfg.detectCineBars == cbBoth) {
// check for yBar
for (int x = 0; x < xOffset; ++x) {
int yBarCount = 0;
for (int y = yOffset; y < imageHeight - yOffset; ++y) {
int row = imageWidth * y;
p = &image[row + x];
if(p->isBlack(cfg.cineBarsThreshold)) {
++yBarCount;
}
}
if(yBarCount == imageHeight - (2*yOffset) ){
++tempyBarWidth;
}
else {
break;
}
}
}
if (tempxBarHeight != lastxBarHeight || tempyBarWidth != lastyBarWidth) {
barsChanged = true;
xBarHeight = tempxBarHeight;
yBarWidth = tempyBarWidth;
}
lastxBarHeight = tempxBarHeight;
lastyBarWidth = tempyBarWidth;
if(barsChanged) tell(1, "V2 Black border detection horBar: %d verBar: %d", xBarHeight, yBarWidth);
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* p;
for (int y = 0; y < imageHeight; y++) {
// skip horizontal cinebars
if(y < xBarHeight || y > imageHeight - xBarHeight) continue;
int rgb[3];
row = imageWidth * y;
for (int x = 0; x < imageWidth; x++) {
// skip vertical cinebars
if(x < yBarWidth || x > imageWidth - yBarWidth) continue;
p = &image[row + x];
rgb[0] = p->r;
rgb[1] = p->g;
rgb[2] = p->b;
bob.writeColor(rgb, x - yBarWidth, y - xBarHeight);
}
}
if (barsChanged) {
bob.setScanRange(imageWidth - (2*yBarWidth), imageHeight - (2*xBarHeight));
barsChanged = false;
}
}
bob.send();
return success;
}