diff --git a/packages/addons/service/multimedia/boblightd/package.mk b/packages/addons/service/multimedia/boblightd/package.mk index 97a780ede4..eca704d598 100644 --- a/packages/addons/service/multimedia/boblightd/package.mk +++ b/packages/addons/service/multimedia/boblightd/package.mk @@ -63,6 +63,7 @@ addon() { mkdir -p $ADDON_BUILD/$PKG_ADDON_ID/bin cp -P $PKG_BUILD/.$TARGET_NAME/src/boblightd $ADDON_BUILD/$PKG_ADDON_ID/bin cp -P $PKG_BUILD/.$TARGET_NAME/src/boblight-constant $ADDON_BUILD/$PKG_ADDON_ID/bin + cp -P $PKG_BUILD/.$TARGET_NAME/src/boblight-aml $ADDON_BUILD/$PKG_ADDON_ID/bin if [ "$DISPLAYSERVER" = "x11" ] ; then cp -P $PKG_BUILD/.$TARGET_NAME/src/boblight-X11 $ADDON_BUILD/$PKG_ADDON_ID/bin fi diff --git a/packages/addons/service/multimedia/boblightd/patches/boblightd-2.0.5-add_aml_client.patch b/packages/addons/service/multimedia/boblightd/patches/boblightd-2.0.5-add_aml_client.patch new file mode 100644 index 0000000000..9051c2011c --- /dev/null +++ b/packages/addons/service/multimedia/boblightd/patches/boblightd-2.0.5-add_aml_client.patch @@ -0,0 +1,632 @@ +diff --git a/src/Makefile.am b/src/Makefile.am +index 9ba5381..c5a4dc6 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -7,6 +7,7 @@ AM_CFLAGS =\ + -g + + bin_PROGRAMS = boblightd \ ++ boblight-aml \ + boblight-constant + + +@@ -19,6 +20,11 @@ endif + + endif + ++boblight_aml_SOURCES = clients/boblight-aml/boblight-aml.cpp ++boblight_aml_SOURCES += clients/boblight-aml/flagmanager-aml.cpp ++boblight_aml_SOURCES += clients/flagmanager.cpp ++boblight_aml_SOURCES += util/misc.cpp ++ + boblight_v4l_SOURCES = \ + clients/boblight-v4l/boblight-v4l.cpp \ + clients/boblight-v4l/flagmanager-v4l.cpp \ +diff -urPp src/clients/boblight-aml.cpp src/clients/boblight-aml/boblight-aml.cpp +--- /dev/null Thu Jan 1 00:00:00 1970 ++++ b/src/clients/boblight-aml/boblight-aml.cpp Thu Jan 15 10:24:16 2015 +@@ -0,0 +1,491 @@ ++/* ++ * boblight ++ * Copyright (C) Bob 2009 ++ * ++ * boblight 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 3 of the License, or ++ * (at your option) any later version. ++ * ++ * boblight 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 . ++ */ ++ ++#define BOBLIGHT_DLOPEN ++#include "lib/boblight.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "config.h" ++#include "util/misc.h" ++#include "util/timeutils.h" ++#include "flagmanager-aml.h" ++ ++using namespace std; ++ ++//from linux/amlogic/amports/amvideocap.h ++#define AMVIDEOCAP_IOC_MAGIC 'V' ++#define AMVIDEOCAP_IOW_SET_WANTFRAME_WIDTH _IOW(AMVIDEOCAP_IOC_MAGIC, 0x02, int) ++#define AMVIDEOCAP_IOW_SET_WANTFRAME_HEIGHT _IOW(AMVIDEOCAP_IOC_MAGIC, 0x03, int) ++ ++ ++// helper class - tries to load the "movie" settings from the script.xbmc.boblight addon ++// and pass them to the boblight-aml client ++class CBoblightAddonSettings ++{ ++ public: ++ CBoblightAddonSettings() : m_bobdisable(false), m_settingsLoaded(false) ++ { ++ m_settingsLoaded = loadBoblightAddonSettings(); ++ } ++ ++ std::string getBoblightClientCmdLine() ++ { ++ std::string cmdLine = ""; ++ //convert bool string to lowercase ++ transform(m_interpolation.begin(), m_interpolation.end(), m_interpolation.begin(), ::tolower); ++ ++ cmdLine += "-s " + m_ip + ":" + m_port; ++ cmdLine += " -o autospeed=" + m_autospeed; ++ cmdLine += " -o interpolation=" + m_interpolation; ++ cmdLine += " -o saturation=" + m_saturation; ++ cmdLine += " -o speed=" + m_speed; ++ cmdLine += " -o threshold=" + m_threshold; ++ cmdLine += " -o value=" + m_value; ++ return cmdLine; ++ } ++ ++ bool m_bobdisable; ++ bool m_settingsLoaded; ++ std::string m_ip; ++ std::string m_port; ++ std::string m_autospeed; ++ std::string m_interpolation; ++ std::string m_saturation; ++ std::string m_speed; ++ std::string m_threshold; ++ std::string m_value; ++ ++ private: ++ #define SETTINGS_ATTR_BOBDISABLE "bobdisable" ++ #define SETTINGS_ATTR_IP "hostip" ++ #define SETTINGS_ATTR_PORT "hostport" ++ #define SETTINGS_ATTR_AUTOSPEED "movie_autospeed" ++ #define SETTINGS_ATTR_INTERPOLATION "movie_interpolation" ++ #define SETTINGS_ATTR_SATURATION "movie_saturation" ++ #define SETTINGS_ATTR_SPEED "movie_speed" ++ #define SETTINGS_ATTR_THRESHOLD "movie_threshold" ++ #define SETTINGS_ATTR_VALUE "movie_value" ++ #define KODI_HOME_ENV_VAR "HOME" ++ ++ bool loadBoblightAddonSettings() ++ { ++ bool ret = false; ++ char *kodiHome = getenv(KODI_HOME_ENV_VAR); ++ //fallback to custom settings file in case boblight addon is not installed ++ std::string settingsFile = "/storage/boblight-aml.xml"; ++ ++ if (kodiHome != NULL) ++ { ++ settingsFile = std::string(kodiHome) + "/.kodi/userdata/addon_data/script.xbmc.boblight/settings.xml"; ++ } ++ ++ FILE *fd = fopen(settingsFile.c_str(), "r"); ++ ++ if (fd != NULL) ++ { ++ fseek(fd, 0, SEEK_END); ++ size_t fileSize = ftell(fd); ++ fseek(fd, 0, SEEK_SET); ++ if (fileSize > 0) ++ { ++ if (fileSize > 32000)//read 16k max - there shouldn't be a bigger settings.xml from boblight [tm] ++ fileSize = 32000; ++ char *xmlBuffer = new char[fileSize]; ++ size_t readCount = fread(xmlBuffer, fileSize, 1, fd); ++ fclose(fd); ++ ++ if (readCount == 1) ++ { ++ parseBoblightSettings(std::string(xmlBuffer)); ++ ret = true; ++ } ++ else ++ { ++ fprintf(stderr, "Failed reading boblight addon settings.xml"); ++ } ++ delete[] xmlBuffer; ++ } ++ } ++ return ret; ++ } ++ ++ void parseBoblightSettings(std::string xmlBuffer) ++ { ++ std::string settings_bobdisable_str; ++ settings_bobdisable_str = getValueFromXmlBuffer(xmlBuffer, SETTINGS_ATTR_BOBDISABLE); ++ if (settings_bobdisable_str == "true" || settings_bobdisable_str == "True") ++ m_bobdisable = true; ++ ++ m_ip = getValueFromXmlBuffer(xmlBuffer, SETTINGS_ATTR_IP); ++ m_port = getValueFromXmlBuffer(xmlBuffer, SETTINGS_ATTR_PORT); ++ m_autospeed = getValueFromXmlBuffer(xmlBuffer, SETTINGS_ATTR_AUTOSPEED); ++ m_interpolation = getValueFromXmlBuffer(xmlBuffer, SETTINGS_ATTR_INTERPOLATION); ++ m_saturation = getValueFromXmlBuffer(xmlBuffer, SETTINGS_ATTR_SATURATION); ++ m_speed = getValueFromXmlBuffer(xmlBuffer, SETTINGS_ATTR_SPEED); ++ m_threshold = getValueFromXmlBuffer(xmlBuffer, SETTINGS_ATTR_THRESHOLD); ++ m_value = getValueFromXmlBuffer(xmlBuffer, SETTINGS_ATTR_VALUE); ++ } ++ ++ std::string getValueFromXmlBuffer(const std::string &xmlBuffer, const char* xmlAttribute) ++ { ++ size_t strPos = 0; ++ std::string valueStr; ++ ++ // each line in the xml looks like this: ++ // ++ // find the attribute ++ if ((strPos = xmlBuffer.find(xmlAttribute)) != std::string::npos) ++ { ++ size_t strPos2 = 0; ++ // from movie_value" value="1.000006" /> look for "value" ++ if ((strPos2 = xmlBuffer.find("value", strPos)) != std::string::npos) ++ { ++ size_t strPos3 = 0; ++ // from value="1.000006" /> look for "=" ++ if ((strPos3 = xmlBuffer.find("=", strPos2)) != std::string::npos) ++ { ++ //extract the value - strPos3 points to ="1.000006" ++ int valueOffset = 1; //skip the "=" ++ if (xmlBuffer[strPos3 + valueOffset] == '"') ++ valueOffset++;//skip " if needed ++ int strLen = 0; ++ do ++ { ++ // value stops with " or space ++ if (xmlBuffer[strPos3 + valueOffset + strLen] == '"' || ++ xmlBuffer[strPos3 + valueOffset + strLen] == ' ') ++ break; ++ strLen++; ++ } while (strLen < 20);// no insane xml garbage ... ++ ++ valueStr = xmlBuffer.substr(strPos3 + valueOffset, strLen); ++ } ++ } ++ } ++ return valueStr; ++ } ++}; ++ ++struct aml_snapshot_t { ++ unsigned int dst_width; ++ unsigned int dst_height; ++ unsigned int dst_stride; ++ unsigned int dst_size; ++ void *dst_vaddr; ++}; ++ ++volatile bool g_stop = false; ++CFlagManagerAML g_flagmanager; ++/********************************************************* ++ *********************************************************/ ++static void SignalHandler(int signum) ++{ ++ if (signum == SIGTERM) ++ { ++ fprintf(stderr, "caught SIGTERM\n"); ++ g_stop = true; ++ } ++ else if (signum == SIGINT) ++ { ++ fprintf(stderr, "caught SIGTERM\n"); ++ g_stop = true; ++ } ++} ++ ++#define VIDEO_PATH "/dev/amvideo" ++#define AMSTREAM_IOC_MAGIC 'S' ++#define AMSTREAM_IOC_GET_VIDEO_DISABLE _IOR(AMSTREAM_IOC_MAGIC, 0x48, unsigned long) ++static int amvideo_utils_video_playing() ++{ ++ int video_fd; ++ int video_disable; ++ ++ video_fd = open(VIDEO_PATH, O_RDWR); ++ if (video_fd < 0) { ++ return -1; ++ } ++ ++ ioctl(video_fd, AMSTREAM_IOC_GET_VIDEO_DISABLE, &video_disable); ++ if (video_disable) ++ { ++ close(video_fd); ++ return 1; ++ } ++ ++ close(video_fd); ++ ++// fprintf(stderr, "pos x %d y %d w %d h %d\n",snapshot.src_x, snapshot.src_y,snapshot.src_width,snapshot.src_height); ++ return 0; ++} ++ ++static int capture_frame(int fd, aml_snapshot_t &snapshot) ++{ ++ int ret = 0; ++ ++ ssize_t readResult = pread(fd, snapshot.dst_vaddr, snapshot.dst_size, 0); ++ ++ if (readResult < snapshot.dst_size) ++ { ++ fprintf(stderr, "frame read returned %d\n", readResult); ++ } ++ //fprintf(stderr, "requ: %d read %d \n", snapshot.dst_size, readResult); ++ fprintf(stderr, "."); ++ return ret; ++} ++ ++static int configure_capture(int fd, aml_snapshot_t &snapshot) ++{ ++ int ret = 0; ++ int ioctlret = 0; ++ ++ if ((ioctlret = ioctl(fd, AMVIDEOCAP_IOW_SET_WANTFRAME_WIDTH, snapshot.dst_width)) != 0) ++ { ++ ret = 2; ++ fprintf(stderr, "Error setting frame width (ret: %d errno: %d)\n", ioctlret, errno); ++ } ++ ++ ++ if ((ioctlret = ioctl(fd, AMVIDEOCAP_IOW_SET_WANTFRAME_HEIGHT, snapshot.dst_height)) != 0) ++ { ++ ret = 3; ++ fprintf(stderr, "Error setting frame height (ret: %d errno: %d)\n", ioctlret, errno); ++ } ++ ++ return ret; ++} ++ ++static void frameToboblight(void *boblight, uint8_t* outputptr, int w, int h, int stride) ++{ ++ if (!boblight) ++ { ++ fprintf(stderr, "no boblight\n"); ++ return; ++ } ++ if (!outputptr) ++ { ++ fprintf(stderr, "no outputptr\n"); ++ return; ++ } ++ //read out pixels and hand them to libboblight ++ uint8_t* buffptr; ++ for (int y = h; y > 0; y--) { ++ buffptr = outputptr + stride * y; ++ for (int x = 0; x < w; x++) { ++ int rgb[3]; ++ rgb[2] = *(buffptr++); ++ rgb[1] = *(buffptr++); ++ rgb[0] = *(buffptr++); ++ ++ //fprintf(stdout, "frameToboblight: x(%d), y(%d)\n", x, y); ++ ++ boblight_addpixelxy(boblight, x, y, rgb); ++ } ++ } ++} ++ ++static int Run(void* boblight) ++{ ++ int snapshot_fd = -1; ++ aml_snapshot_t aml_snapshot = {0}; ++ int lastPriority = 255; ++ ++ aml_snapshot.dst_width = 160; ++ aml_snapshot.dst_height = 160; ++ ++ // calc stride, size and alloc mem ++ aml_snapshot.dst_stride = aml_snapshot.dst_width * 3; ++ aml_snapshot.dst_size = aml_snapshot.dst_stride * aml_snapshot.dst_height; ++ aml_snapshot.dst_vaddr = calloc(aml_snapshot.dst_size, 1); ++ ++ fprintf(stdout, "Connection to boblightd config: width(%d), height(%d)\n", ++ aml_snapshot.dst_width, aml_snapshot.dst_height); ++ //tell libboblight how big our image is ++ boblight_setscanrange(boblight, (int)aml_snapshot.dst_width, (int)aml_snapshot.dst_height); ++ ++ while(!g_stop) ++ { ++ int64_t bgn = GetTimeUs(); ++ ++ if (snapshot_fd == -1) { ++ snapshot_fd = open(g_flagmanager.m_device.c_str(), O_RDWR, 0); ++ ++ if (snapshot_fd == -1) { ++ sleep(1); ++ continue; ++ } else { ++ fprintf(stdout, "snapshot_fd(%d) \n", snapshot_fd); ++ } ++ } ++ ++ // match source ratio if possible ++ if (amvideo_utils_video_playing() != 0) { ++ if ( lastPriority != 255) ++ { ++ boblight_setpriority(boblight, 255); ++ lastPriority = 255; ++ } ++ sleep(1); ++ continue; ++ } ++ ++ if (configure_capture(snapshot_fd, aml_snapshot) == 0) ++ { ++ if (capture_frame(snapshot_fd, aml_snapshot) == 0) ++ { ++ // image to boblight convert. ++ frameToboblight(boblight, (uint8_t*)aml_snapshot.dst_vaddr, ++ aml_snapshot.dst_width, aml_snapshot.dst_height, aml_snapshot.dst_stride); ++ ++ if (lastPriority != g_flagmanager.m_priority) ++ { ++ boblight_setpriority(boblight, g_flagmanager.m_priority); ++ lastPriority = g_flagmanager.m_priority; ++ } ++ if (!boblight_sendrgb(boblight, 1, NULL)) ++ { ++ // some error happened, probably connection broken, so bitch and try again ++ PrintError(boblight_geterror(boblight)); ++ boblight_destroy(boblight); ++ continue; ++ } ++ } ++ else ++ { ++ fprintf(stdout, "nap time\n"); ++ sleep(1); ++ } ++ } ++ int64_t end = GetTimeUs(); ++ float calc_time_ms = (float)(end - bgn) / 1000.0; ++ // throttle to 100ms max cycle rate ++ calc_time_ms -= 100.0; ++ if ((int)calc_time_ms < 0) ++ usleep((int)(-calc_time_ms * 1000)); ++ } ++ ++ // last image is black ++ boblight_setpriority(boblight, 255); ++ boblight_destroy(boblight); ++ close(snapshot_fd); ++ return 0; ++} ++ ++/********************************************************* ++ *********************************************************/ ++int main(int argc, char *argv[]) ++{ ++ //load the boblight lib, if it fails we get a char* from dlerror() ++ const char* boblight_error = boblight_loadlibrary(NULL); ++ if (boblight_error) ++ { ++ PrintError(boblight_error); ++ return 1; ++ } ++ ++ //try to parse the flags and bitch to stderr if there's an error ++ try { ++ g_flagmanager.ParseFlags(argc, argv); ++ } ++ catch (string error) { ++ PrintError(error); ++ g_flagmanager.PrintHelpMessage(); ++ return 1; ++ } ++ ++ if (g_flagmanager.m_printhelp) { ++ g_flagmanager.PrintHelpMessage(); ++ return 1; ++ } ++ ++ if (g_flagmanager.m_printboblightoptions) { ++ g_flagmanager.PrintBoblightOptions(); ++ return 1; ++ } ++ ++ // check if we only should generate a cmdline based ++ // on settings from possible found boblight addon ++ if (g_flagmanager.generateCmdLine) ++ { ++ CBoblightAddonSettings settings; ++ string cmdLine = "-p 100"; //default cmdline just contains priority 100 ++ ++ if (settings.m_settingsLoaded) ++ cmdLine += " " + settings.getBoblightClientCmdLine(); ++ fprintf(stdout, "%s", cmdLine.c_str()); ++ return 0;//exit ++ } ++ ++ fprintf(stderr, "Using device: %s \n", g_flagmanager.m_device.c_str()); ++ ++ //set up signal handlers ++ signal(SIGINT, SignalHandler); ++ signal(SIGTERM, SignalHandler); ++ ++ //keep running until we want to quit ++ while(!g_stop) { ++ //init boblight ++ void* boblight = boblight_init(); ++ ++ fprintf(stdout, "Connecting to boblightd(%p)\n", boblight); ++ ++ //try to connect, if we can't then bitch to stderr and destroy boblight ++ if (!boblight_connect(boblight, g_flagmanager.m_address, g_flagmanager.m_port, 5000000) || ++ !boblight_setpriority(boblight, 255)) { ++ PrintError(boblight_geterror(boblight)); ++ fprintf(stdout, "Waiting 10 seconds before trying again\n"); ++ boblight_destroy(boblight); ++ sleep(2); ++ continue; ++ } ++ ++ fprintf(stdout, "Connection to boblightd opened\n"); ++ ++ //try to parse the boblight flags and bitch to stderr if we can't ++ try { ++ g_flagmanager.ParseBoblightOptions(boblight); ++ } ++ catch (string error) { ++ PrintError(error); ++ return 1; ++ } ++ ++ try { ++ Run(boblight); ++ } ++ catch (string error) { ++ PrintError(error); ++ boblight_destroy(boblight); ++ return 1; ++ } ++ } ++ fprintf(stdout, "Exiting\n"); ++} +diff -urPp src/clients/flagmanager-aml.cpp src/clients/boblight-aml/flagmanager-aml.cpp +--- /dev/null Thu Jan 1 00:00:00 1970 ++++ b/src/clients/boblight-aml/flagmanager-aml.cpp Thu Jan 15 10:21:40 2015 +@@ -0,0 +1,68 @@ ++/* ++ * boblight ++ * Copyright (C) Bob 2009 ++ * ++ * boblight 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 3 of the License, or ++ * (at your option) any later version. ++ * ++ * boblight 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 "flagmanager-aml.h" ++#include "util/misc.h" ++#include "config.h" ++ ++#define DEFAULT_CAPTURE_DEVICE "/dev/amvideocap0" ++ ++using namespace std; ++ ++CFlagManagerAML::CFlagManagerAML() ++{ ++ // extend the flags -d -> device ++ // -g -> only generate cmdline from possible found boblight addon settings.xml ++ m_flags += "d:g"; ++ m_device = DEFAULT_CAPTURE_DEVICE; ++ generateCmdLine = false; ++} ++ ++void CFlagManagerAML::ParseFlagsExtended(int& argc, char**& argv, int& c, char*& optarg) ++{ ++ if (c == 'd') //devicename ++ { ++ if (optarg) //optional device ++ { ++ m_device = optarg; ++ } ++ } ++ ++ if (c == 'g') //generate cmdline ++ { ++ generateCmdLine = true; ++ } ++} ++ ++void CFlagManagerAML::PrintHelpMessage() ++{ ++ cout << "Usage: boblight-aml\n"; ++ cout << "\n"; ++ cout << " options:\n"; ++ cout << "\n"; ++ cout << " -p priority, from 0 to 255, default is 128\n"; ++ cout << " -s address[:port], set the address and optional port to connect to\n"; ++ cout << " -o add libboblight option, syntax: [light:]option=value\n"; ++ cout << " -l list libboblight options\n"; ++ cout << " -f fork\n"; ++ cout << " -d (defaults to " << m_device << ")\n"; ++ cout << " -g try to find the settings.xml file from boblight addon and return the cmdline to use its options\n"; ++ cout << "\n"; ++} +diff -urPp src/clients/flagmanager-aml.h src/clients/boblight-aml/flagmanager-aml.h +--- /dev/null Thu Jan 1 00:00:00 1970 ++++ b/src/clients/boblight-aml/flagmanager-aml.h Thu Jan 15 10:20:15 2015 +@@ -0,0 +1,36 @@ ++/* ++ * boblight ++ * Copyright (C) Bob 2009 ++ * ++ * boblight 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 3 of the License, or ++ * (at your option) any later version. ++ * ++ * boblight 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 . ++ */ ++ ++#ifndef FLAGMANAGERAML ++#define FLAGMANAGERAML ++ ++#include "clients/flagmanager.h" ++ ++class CFlagManagerAML : public CFlagManager ++{ ++ public: ++ CFlagManagerAML(); ++ void ParseFlagsExtended(int& argc, char**& argv, int& c, char*& optarg); ++ ++ void PrintHelpMessage(); ++ std::string m_device; //device to open for amvideocap ++ bool generateCmdLine; ++ ++}; ++ ++#endif //FLAGMANAGERAML +\ No newline at end of file