From 5497fbf577bed537da498899ff254561316c5618 Mon Sep 17 00:00:00 2001 From: "T.van der Zwan" Date: Thu, 20 Aug 2015 09:51:44 +0200 Subject: [PATCH] Finished the amlogic grabber for the 'WeTek Play' Former-commit-id: e459cdfe6273ad2bfee92d2d190801ebdc691a5c --- HyperionConfig.h.in | 3 + include/grabber/AmlogicWrapper.h | 4 +- include/utils/ColorBgr.h | 63 +++++++++++++++ libsrc/grabber/amlogic/AmlogicGrabber.cpp | 99 ++++++++++++++++++----- libsrc/grabber/amlogic/AmlogicGrabber.h | 12 ++- libsrc/grabber/amlogic/AmlogicWrapper.cpp | 9 ++- libsrc/utils/CMakeLists.txt | 2 + libsrc/utils/ColorBgr.cpp | 11 +++ src/hyperion-aml/hyperion-aml.cpp | 31 ++++--- src/hyperiond/CMakeLists.txt | 4 + src/hyperiond/hyperiond.cpp | 98 ++++++++++++++-------- 11 files changed, 265 insertions(+), 71 deletions(-) create mode 100644 include/utils/ColorBgr.h create mode 100644 libsrc/utils/ColorBgr.cpp diff --git a/HyperionConfig.h.in b/HyperionConfig.h.in index 7644e7d6..f5bd3ce1 100644 --- a/HyperionConfig.h.in +++ b/HyperionConfig.h.in @@ -21,6 +21,9 @@ // Define to enable the framebuffer grabber #cmakedefine ENABLE_FB +// Define to enable the amlogic grabber +#cmakedefine ENABLE_AMLOGIC + // Define to enable the osx grabber #cmakedefine ENABLE_OSX diff --git a/include/grabber/AmlogicWrapper.h b/include/grabber/AmlogicWrapper.h index 8147a8d6..0ce70b80 100644 --- a/include/grabber/AmlogicWrapper.h +++ b/include/grabber/AmlogicWrapper.h @@ -6,8 +6,8 @@ // Utils includes #include +#include #include -#include #include #include @@ -80,7 +80,7 @@ private: QTimer _timer; /// The image used for grabbing frames - Image _image; + Image _image; /// The actual grabber AmlogicGrabber * _frameGrabber; /// The processor for transforming images to led colors diff --git a/include/utils/ColorBgr.h b/include/utils/ColorBgr.h new file mode 100644 index 00000000..a92e1270 --- /dev/null +++ b/include/utils/ColorBgr.h @@ -0,0 +1,63 @@ +#pragma once + +// STL includes +#include +#include + +struct ColorBgr; + +/// +/// Plain-Old-Data structure containing the red-green-blue color specification. Size of the +/// structure is exactly 3-bytes for easy writing to led-device +/// +struct ColorBgr +{ + /// The blue color channel + uint8_t blue; + /// The green color channel + uint8_t green; + /// The red color channel + uint8_t red; + + /// 'Black' RgbColor (0, 0, 0) + static ColorBgr BLACK; + /// 'Red' RgbColor (255, 0, 0) + static ColorBgr RED; + /// 'Green' RgbColor (0, 255, 0) + static ColorBgr GREEN; + /// 'Blue' RgbColor (0, 0, 255) + static ColorBgr BLUE; + /// 'Yellow' RgbColor (255, 255, 0) + static ColorBgr YELLOW; + /// 'White' RgbColor (255, 255, 255) + static ColorBgr WHITE; +}; + +/// Assert to ensure that the size of the structure is 'only' 3 bytes +static_assert(sizeof(ColorBgr) == 3, "Incorrect size of ColorRgb"); + +/// +/// Stream operator to write ColorRgb to an outputstream (format "'{'[red]','[green]','[blue]'}'") +/// +/// @param os The output stream +/// @param color The color to write +/// @return The output stream (with the color written to it) +/// +inline std::ostream& operator<<(std::ostream& os, const ColorBgr& color) +{ + os << "{" << unsigned(color.red) << "," << unsigned(color.green) << "," << unsigned(color.blue) << "}"; + return os; +} + + +/// Compare operator to check if a color is 'smaller' than another color +inline bool operator<(const ColorBgr & lhs, const ColorBgr & rhs) +{ + return (lhs.red < rhs.red) && (lhs.green < rhs.green) && (lhs.blue < rhs.blue); +} + +/// Compare operator to check if a color is 'smaller' than or 'equal' to another color +inline bool operator<=(const ColorBgr & lhs, const ColorBgr & rhs) +{ + return (lhs.red <= rhs.red) && (lhs.green <= rhs.green) && (lhs.blue <= rhs.blue); +} diff --git a/libsrc/grabber/amlogic/AmlogicGrabber.cpp b/libsrc/grabber/amlogic/AmlogicGrabber.cpp index 33d01bc5..dd727217 100644 --- a/libsrc/grabber/amlogic/AmlogicGrabber.cpp +++ b/libsrc/grabber/amlogic/AmlogicGrabber.cpp @@ -1,5 +1,6 @@ // STL includes +#include #include #include @@ -14,16 +15,22 @@ // Local includes #include "AmlogicGrabber.h" -// Flags copied from 'linux/amlogic/amports/amvideocap.h' at https://github.com/codesnake/linux-amlogic/ +// Flags copied from 'include/linux/amlogic/amports/amvideocap.h' at https://github.com/codesnake/linux-amlogic #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) +#define AMVIDEOCAP_IOW_SET_WANTFRAME_WIDTH _IOW(AMVIDEOCAP_IOC_MAGIC, 0x02, int) +#define AMVIDEOCAP_IOW_SET_WANTFRAME_HEIGHT _IOW(AMVIDEOCAP_IOC_MAGIC, 0x03, int) + +// Flags copied from 'include/linux/amlogic/amports/amvstream.h' at https://github.com/codesnake/linux-amlogic +#define AMSTREAM_IOC_MAGIC 'S' +#define AMSTREAM_IOC_GET_VIDEO_DISABLE _IOR(AMSTREAM_IOC_MAGIC, 0x48, unsigned long) AmlogicGrabber::AmlogicGrabber(const unsigned width, const unsigned height) : - _width(width), - _height(height), + // Minimum required width or height is 160 + _width(std::max(160u, width)), + _height(std::max(160u, height)), _amlogicCaptureDev(-1) { + std::cout << "[" << __PRETTY_FUNCTION__ << "] constructed(" << _width << "x" << _height << ")" << std::endl; } AmlogicGrabber::~AmlogicGrabber() @@ -54,40 +61,90 @@ void AmlogicGrabber::setVideoMode(const VideoMode videoMode) } } -void AmlogicGrabber::grabFrame(Image & image) +bool AmlogicGrabber::isVideoPlaying() +{ + const std::string videoDevice = "/dev/amvideo"; + + // Open the video device + int video_fd = open(videoDevice.c_str(), O_RDONLY); + if (video_fd < 0) + { + std::cerr << "Failed to open video device(" << videoDevice << "): " << strerror(errno) << std::endl; + return false; + } + + // Check the video disabled flag + int videoDisabled; + if (ioctl(video_fd, AMSTREAM_IOC_GET_VIDEO_DISABLE, &videoDisabled) == -1) + { + std::cerr << "Failed to retrieve video state from device: " << strerror(errno) << std::endl; + close(video_fd); + return false; + } + + // Make sure to close the device after use + close(video_fd); + + return videoDisabled == 0; +} + +int AmlogicGrabber::grabFrame(Image & image) { // resize the given image if needed if (image.width() != _width || image.height() != _height) { image.resize(_width, _height); } - - _amlogicCaptureDev = open("/dev/amvideocap0", O_RDONLY, 0); + + // Make sure video is playing, else there is nothing to grab + if (!isVideoPlaying()) + { + return -1; + } + + + // If the device is not open, attempt to open it if (_amlogicCaptureDev == -1) { - std::cerr << "[" << __PRETTY_FUNCTION__ << "] Failed to open the AMLOGIC device (" << errno << ")" << std::endl; - return; + _amlogicCaptureDev = open("/dev/amvideocap0", O_RDONLY, 0); + + // If the device is still not open, there is something wrong + if (_amlogicCaptureDev == -1) + { + std::cerr << "[" << __PRETTY_FUNCTION__ << "] Failed to open the AMLOGIC device (" << errno << "): " << strerror(errno) << std::endl; + return -1; + } } - - if (ioctl(_amlogicCaptureDev, AMVIDEOCAP_IOW_SET_WANTFRAME_WIDTH, _width) == -1 || - ioctl(_amlogicCaptureDev, AMVIDEOCAP_IOW_SET_WANTFRAME_HEIGHT, _height) == -1) + + + if (ioctl(_amlogicCaptureDev, AMVIDEOCAP_IOW_SET_WANTFRAME_WIDTH, _width) == -1 || + ioctl(_amlogicCaptureDev, AMVIDEOCAP_IOW_SET_WANTFRAME_HEIGHT, _height) == -1) { // Failed to configure frame width - std::cerr << "[" << __PRETTY_FUNCTION__ << "] Failed to configure capture size (" << errno << ")" << std::endl; - return; + std::cerr << "[" << __PRETTY_FUNCTION__ << "] Failed to configure capture size (" << errno << "): " << strerror(errno) << std::endl; + return -1; } - - std::cout << "AMLOGIC grabber created (size " << _width << "x" << _height << ")" << std::endl; + // Read the snapshot into the memory void * image_ptr = image.memptr(); - const size_t bytesToRead = _width * _height * sizeof(ColorRgb); - const size_t bytesRead = pread(_amlogicCaptureDev, image_ptr, bytesToRead, 0); - if (bytesToRead != bytesRead) + const ssize_t bytesToRead = _width * _height * sizeof(ColorBgr); + + const ssize_t bytesRead = pread(_amlogicCaptureDev, image_ptr, bytesToRead, 0); + if (bytesRead == -1) + { + std::cerr << "[" << __PRETTY_FUNCTION__ << "] Read of device failed (erno=" << errno << "): " << strerror(errno) << std::endl; + return -1; + } + else if (bytesToRead != bytesRead) { // Read of snapshot failed std::cerr << "[" << __PRETTY_FUNCTION__ << "] Capture failed to grab entire image [bytesToRead(" << bytesToRead << ") != bytesRead(" << bytesRead << ")]" << std::endl; + return -1; } - + + // For now we always close the device again close(_amlogicCaptureDev); _amlogicCaptureDev = -1; + + return 0; } diff --git a/libsrc/grabber/amlogic/AmlogicGrabber.h b/libsrc/grabber/amlogic/AmlogicGrabber.h index e39b3859..05694853 100644 --- a/libsrc/grabber/amlogic/AmlogicGrabber.h +++ b/libsrc/grabber/amlogic/AmlogicGrabber.h @@ -5,7 +5,7 @@ // Utils includes #include -#include +#include #include /// @@ -37,16 +37,22 @@ public: /// /// @param[out] image The snapped screenshot (should be initialized with correct width and /// height) + /// @return Zero on success else negative /// - void grabFrame(Image & image); + int grabFrame(Image & image); + /** + * Returns true if video is playing over the amlogic chip + * @return True if video is playing else false + */ + bool isVideoPlaying(); private: /// With of the captured snapshot [pixels] const unsigned _width; /// Height of the captured snapshot [pixels] const unsigned _height; - + /** The snapshot/capture device of the amlogic video chip */ int _amlogicCaptureDev; }; diff --git a/libsrc/grabber/amlogic/AmlogicWrapper.cpp b/libsrc/grabber/amlogic/AmlogicWrapper.cpp index 6d022852..1dca6764 100644 --- a/libsrc/grabber/amlogic/AmlogicWrapper.cpp +++ b/libsrc/grabber/amlogic/AmlogicWrapper.cpp @@ -15,7 +15,7 @@ AmlogicWrapper::AmlogicWrapper(const unsigned grabWidth, const unsigned grabHeight, const unsigned updateRate_Hz, Hyperion * hyperion) : _updateInterval_ms(1000/updateRate_Hz), _timeout_ms(2 * _updateInterval_ms), - _priority(1000), + _priority(999), _timer(), _image(grabWidth, grabHeight), _frameGrabber(new AmlogicGrabber(grabWidth, grabHeight)), @@ -49,12 +49,17 @@ void AmlogicWrapper::start() void AmlogicWrapper::action() { // Grab frame into the allocated image - _frameGrabber->grabFrame(_image); + if (_frameGrabber->grabFrame(_image) < 0) + { + // Frame grab failed, maybe nothing playing or .... + return; + } _processor->process(_image, _ledColors); _hyperion->setColors(_priority, _ledColors, _timeout_ms); } + void AmlogicWrapper::stop() { // Stop the timer, effectivly stopping the process diff --git a/libsrc/utils/CMakeLists.txt b/libsrc/utils/CMakeLists.txt index 5549ce74..1aad10cb 100644 --- a/libsrc/utils/CMakeLists.txt +++ b/libsrc/utils/CMakeLists.txt @@ -6,6 +6,8 @@ SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/utils) add_library(hyperion-utils ${CURRENT_HEADER_DIR}/ColorArgb.h ${CURRENT_SOURCE_DIR}/ColorArgb.cpp + ${CURRENT_HEADER_DIR}/ColorBgr.h + ${CURRENT_SOURCE_DIR}/ColorBgr.cpp ${CURRENT_HEADER_DIR}/ColorRgb.h ${CURRENT_SOURCE_DIR}/ColorRgb.cpp ${CURRENT_HEADER_DIR}/ColorRgba.h diff --git a/libsrc/utils/ColorBgr.cpp b/libsrc/utils/ColorBgr.cpp new file mode 100644 index 00000000..3167925e --- /dev/null +++ b/libsrc/utils/ColorBgr.cpp @@ -0,0 +1,11 @@ + +// Local includes +#include + +ColorBgr ColorBgr::BLACK = { 0, 0, 0 }; +ColorBgr ColorBgr::RED = { 0, 0, 255 }; +ColorBgr ColorBgr::GREEN = { 0, 255, 0 }; +ColorBgr ColorBgr::BLUE = { 255, 0, 0 }; +ColorBgr ColorBgr::YELLOW= { 0, 255, 255 }; +ColorBgr ColorBgr::WHITE = { 255, 255, 255 }; + diff --git a/src/hyperion-aml/hyperion-aml.cpp b/src/hyperion-aml/hyperion-aml.cpp index b9ad29b6..13da6686 100644 --- a/src/hyperion-aml/hyperion-aml.cpp +++ b/src/hyperion-aml/hyperion-aml.cpp @@ -12,11 +12,12 @@ using namespace vlofgren; // save the image as screenshot -void saveScreenshot(const char * filename, const Image & image) +void saveScreenshot(const char * filename, const Image & image) { - // store as PNG - QImage pngImage((const uint8_t *) image.memptr(), image.width(), image.height(), 3*image.width(), QImage::Format_RGB888); - pngImage.save(filename); + // store as PNG + QImage pngImage((const uint8_t *) image.memptr(), image.width(), image.height(), 3*image.width(), QImage::Format_RGB888); + pngImage = pngImage.rgbSwapped(); + pngImage.save(filename); } int main(int argc, char ** argv) @@ -39,11 +40,11 @@ int main(int argc, char ** argv) SwitchParameter<> & argHelp = parameters.add> ('h', "help", "Show this help message and exit"); // set defaults - argWidth.setDefault(64); - argHeight.setDefault(64); + argWidth.setDefault(160); + argHeight.setDefault(160); argAddress.setDefault("127.0.0.1:19445"); argPriority.setDefault(800); - + // parse all options optionParser.parse(argc, const_cast(argv)); @@ -54,19 +55,29 @@ int main(int argc, char ** argv) return 0; } + int width = argWidth.getValue(); + int height = argHeight.getValue(); + if (width < 160 || height < 60) + { + std::cout << "Minimum width and height is 160" << std::endl; + width = std::max(160, width); + height = std::max(160, height); + } if (argScreenshot.isSet()) { + // Create the grabber - AmlogicGrabber amlGrabber(argWidth.getValue(), argHeight.getValue()); - + AmlogicGrabber amlGrabber(width, height); + // Capture a single screenshot and finish - Image screenshot; + Image screenshot; amlGrabber.grabFrame(screenshot); saveScreenshot("screenshot.png", screenshot); } else { // TODO[TvdZ]: Implement the proto-client mechanisme + std::cerr << "The PROTO-interface has not been implemented yet" << std::endl; } } diff --git a/src/hyperiond/CMakeLists.txt b/src/hyperiond/CMakeLists.txt index 9f4f1188..70c2381b 100644 --- a/src/hyperiond/CMakeLists.txt +++ b/src/hyperiond/CMakeLists.txt @@ -26,6 +26,10 @@ if (ENABLE_V4L2) target_link_libraries(hyperiond v4l2-grabber) endif (ENABLE_V4L2) +if (ENABLE_AMLOGIC) + target_link_libraries(hyperiond amlogic-grabber) +endif (ENABLE_AMLOGIC) + if (ENABLE_PROTOBUF) target_link_libraries(hyperiond protoserver) endif (ENABLE_PROTOBUF) diff --git a/src/hyperiond/hyperiond.cpp b/src/hyperiond/hyperiond.cpp index 7c89c94e..ead62d80 100644 --- a/src/hyperiond/hyperiond.cpp +++ b/src/hyperiond/hyperiond.cpp @@ -31,6 +31,10 @@ #include #endif +#ifdef ENABLE_AMLOGIC +#include +#endif + #ifdef ENABLE_OSX // OSX grabber includes #include @@ -123,7 +127,7 @@ int main(int argc, char** argv) const std::string effectName = effectConfig["effect"].asString(); const unsigned duration_ms = effectConfig["duration_ms"].asUInt(); const int priority = 0; - + hyperion.setColor(priority+1, ColorRgb::BLACK, duration_ms, false); if (effectConfig.isMember("args")) @@ -236,7 +240,37 @@ int main(int argc, char** argv) std::cerr << "The v4l2 grabber can not be instantiated, becuse it has been left out from the build" << std::endl; } #endif - + +#ifdef ENABLE_AMLOGIC + // Construct and start the framebuffer grabber if the configuration is present + AmlogicWrapper * amlGrabber = nullptr; + if (config.isMember("amlgrabber")) + { + const Json::Value & grabberConfig = config["amlgrabber"]; + amlGrabber = new AmlogicWrapper( + grabberConfig["width"].asUInt(), + grabberConfig["height"].asUInt(), + grabberConfig["frequency_Hz"].asUInt(), + &hyperion); + + if (xbmcVideoChecker != nullptr) + { + QObject::connect(xbmcVideoChecker, SIGNAL(grabbingMode(GrabbingMode)), amlGrabber, SLOT(setGrabbingMode(GrabbingMode))); + QObject::connect(xbmcVideoChecker, SIGNAL(videoMode(VideoMode)), amlGrabber, SLOT(setVideoMode(VideoMode))); + } + + amlGrabber->start(); + std::cout << "AMLOGIC grabber created and started" << std::endl; + } +#else +#if !defined(ENABLE_DISPMANX) && !defined(ENABLE_OSX) + if (config.isMember("framegrabber")) + { + std::cerr << "The framebuffer grabber can not be instantiated, becuse it has been left out from the build" << std::endl; + } +#endif +#endif + #ifdef ENABLE_FB // Construct and start the framebuffer grabber if the configuration is present FramebufferWrapper * fbGrabber = nullptr; @@ -260,42 +294,40 @@ int main(int argc, char** argv) std::cout << "Framebuffer grabber created and started" << std::endl; } #else -#if !defined(ENABLE_DISPMANX) && !defined(ENABLE_OSX) - if (config.isMember("framegrabber")) + if (config.isMember("amlgrabber")) { - std::cerr << "The framebuffer grabber can not be instantiated, becuse it has been left out from the build" << std::endl; + std::cerr << "The AMLOGIC grabber can not be instantiated, because it has been left out from the build" << std::endl; } #endif -#endif - + #ifdef ENABLE_OSX - // Construct and start the osx grabber if the configuration is present - OsxWrapper * osxGrabber = nullptr; - if (config.isMember("framegrabber")) - { - const Json::Value & grabberConfig = config["framegrabber"]; - osxGrabber = new OsxWrapper( - grabberConfig.get("display", 0).asUInt(), - grabberConfig["width"].asUInt(), - grabberConfig["height"].asUInt(), - grabberConfig["frequency_Hz"].asUInt(), - &hyperion); - - if (xbmcVideoChecker != nullptr) - { - QObject::connect(xbmcVideoChecker, SIGNAL(grabbingMode(GrabbingMode)), osxGrabber, SLOT(setGrabbingMode(GrabbingMode))); - QObject::connect(xbmcVideoChecker, SIGNAL(videoMode(VideoMode)), osxGrabber, SLOT(setVideoMode(VideoMode))); - } - - osxGrabber->start(); - std::cout << "OSX grabber created and started" << std::endl; - } + // Construct and start the osx grabber if the configuration is present + OsxWrapper * osxGrabber = nullptr; + if (config.isMember("framegrabber")) + { + const Json::Value & grabberConfig = config["framegrabber"]; + osxGrabber = new OsxWrapper( + grabberConfig.get("display", 0).asUInt(), + grabberConfig["width"].asUInt(), + grabberConfig["height"].asUInt(), + grabberConfig["frequency_Hz"].asUInt(), + &hyperion); + + if (xbmcVideoChecker != nullptr) + { + QObject::connect(xbmcVideoChecker, SIGNAL(grabbingMode(GrabbingMode)), osxGrabber, SLOT(setGrabbingMode(GrabbingMode))); + QObject::connect(xbmcVideoChecker, SIGNAL(videoMode(VideoMode)), osxGrabber, SLOT(setVideoMode(VideoMode))); + } + + osxGrabber->start(); + std::cout << "OSX grabber created and started" << std::endl; + } #else #if !defined(ENABLE_DISPMANX) && !defined(ENABLE_FB) - if (config.isMember("framegrabber")) - { - std::cerr << "The osx grabber can not be instantiated, becuse it has been left out from the build" << std::endl; - } + if (config.isMember("framegrabber")) + { + std::cerr << "The osx grabber can not be instantiated, becuse it has been left out from the build" << std::endl; + } #endif #endif @@ -340,7 +372,7 @@ int main(int argc, char** argv) delete fbGrabber; #endif #ifdef ENABLE_OSX - delete osxGrabber; + delete osxGrabber; #endif #ifdef ENABLE_V4L2 delete v4l2Grabber;