diff --git a/deploy/hyperion.tar.gz.REMOVED.git-id b/deploy/hyperion.tar.gz.REMOVED.git-id index 75bbb7c6..c842cf6c 100644 --- a/deploy/hyperion.tar.gz.REMOVED.git-id +++ b/deploy/hyperion.tar.gz.REMOVED.git-id @@ -1 +1 @@ -c358364291c2377c26ef2912042fae6d7fe50d08 \ No newline at end of file +42debced0ba50d4c1801b25ae3ce1a546ec7a94e \ No newline at end of file diff --git a/include/dispmanx-grabber/DispmanxWrapper.h b/include/dispmanx-grabber/DispmanxWrapper.h index 9fa45f7d..f0156740 100644 --- a/include/dispmanx-grabber/DispmanxWrapper.h +++ b/include/dispmanx-grabber/DispmanxWrapper.h @@ -9,6 +9,7 @@ #include #include #include +#include // Forward class declaration class DispmanxFrameGrabber; @@ -61,6 +62,12 @@ public slots: /// void setGrabbingMode(const GrabbingMode mode); + /// + /// Set the video mode (2D/3D) + /// @param[in] mode The new video mode + /// + void setVideoMode(const VideoMode videoMode); + private: /// The update rate [Hz] const int _updateInterval_ms; diff --git a/include/utils/Image.h b/include/utils/Image.h index 6c847372..48625bbb 100644 --- a/include/utils/Image.h +++ b/include/utils/Image.h @@ -118,6 +118,22 @@ public: return _pixels[toIndex(x,y)]; } + /// Resize the image + /// @param width The width of the image + /// @param height The height of the image + void resize(const unsigned width, const unsigned height) + { + if ((width*height) > (_endOfPixels-_pixels)) + { + delete[] _pixels; + _pixels = new Pixel_T[width*height + 1]; + _endOfPixels = _pixels + width*height; + } + + _width = width; + _height = height; + } + /// /// Copies another image into this image. The images should have exactly the same size. /// @@ -165,9 +181,9 @@ private: private: /// The width of the image - const unsigned _width; + unsigned _width; /// The height of the image - const unsigned _height; + unsigned _height; /// The pixels of the image Pixel_T* _pixels; diff --git a/include/utils/VideoMode.h b/include/utils/VideoMode.h new file mode 100644 index 00000000..16c75b37 --- /dev/null +++ b/include/utils/VideoMode.h @@ -0,0 +1,11 @@ +#pragma once + +/** + * Enumeration of the possible modes in which video can be playing (2D, 3D) + */ +enum VideoMode +{ + VIDEO_2D, + VIDEO_3DSBS, + VIDEO_3DTAB +}; diff --git a/include/xbmcvideochecker/XBMCVideoChecker.h b/include/xbmcvideochecker/XBMCVideoChecker.h index aa6a27ee..97e1bf1a 100644 --- a/include/xbmcvideochecker/XBMCVideoChecker.h +++ b/include/xbmcvideochecker/XBMCVideoChecker.h @@ -16,6 +16,7 @@ // Utils includes #include +#include /// /// This class will check if XBMC is playing something. When it does not, this class will send all black data to Hyperion. @@ -33,13 +34,14 @@ public: /// /// @param address Network address of the XBMC instance /// @param port Port number to use (XBMC default = 9090) - /// @param interval The interval at which XBMC is polled /// @param grabVideo Whether or not to grab when the XBMC video player is playing /// @param grabPhoto Whether or not to grab when the XBMC photo player is playing /// @param grabAudio Whether or not to grab when the XBMC audio player is playing /// @param grabMenu Whether or not to grab when nothing is playing (in XBMC menu) + /// @param grabScreensaver Whether or not to grab when the XBMC screensaver is activated + /// @param enable3DDetection Wheter or not to enable the detection of 3D movies playing /// - XBMCVideoChecker(const std::string & address, uint16_t port, uint64_t interval, bool grabVideo, bool grabPhoto, bool grabAudio, bool grabMenu); + XBMCVideoChecker(const std::string & address, uint16_t port, bool grabVideo, bool grabPhoto, bool grabAudio, bool grabMenu, bool grabScreensaver, bool enable3DDetection); /// /// Start polling XBMC @@ -47,19 +49,34 @@ public: void start(); signals: + /// Signal emitted when the grabbing mode changes void grabbingMode(GrabbingMode grabbingMode); -private slots: - /// - /// Send a request to XBMC - /// - void sendRequest(); + /// Signal emitted when a 3D movie is detected + void videoMode(VideoMode videoMode); - /// +private slots: /// Receive a reply from XBMC - /// void receiveReply(); + /// Called when connected to XBMC + void connected(); + + /// Called when disconnected from XBMC + void disconnected(); + + /// Called when a connection error was encountered + void connectionError(QAbstractSocket::SocketError error); + +private: + /// Set the grabbing mode + void setGrabbingMode(GrabbingMode grabbingMode); + + void setScreensaverMode(bool isOnScreensaver); + + /// Set the video mode + void setVideoMode(VideoMode videoMode); + private: /// The network address of the XBMC instance const QString _address; @@ -67,27 +84,42 @@ private: /// The port number of XBMC const uint16_t _port; - /// The JSON-RPC request message - const QByteArray _request; + /// The JSON-RPC message to check the active player + const QString _activePlayerRequest; - /// The timer that schedules XBMC queries - QTimer _timer; + /// The JSON-RPC message to check the currently playing file + const QString _currentPlayingItemRequest; + + /// The JSON-RPC message to check the screensaver + const QString _checkScreensaverRequest; /// The QT TCP Socket with connection to XBMC QTcpSocket _socket; /// Flag indicating whether or not to grab when the XBMC video player is playing - bool _grabVideo; + const bool _grabVideo; /// Flag indicating whether or not to grab when the XBMC photo player is playing - bool _grabPhoto; + const bool _grabPhoto; /// Flag indicating whether or not to grab when the XBMC audio player is playing - bool _grabAudio; + const bool _grabAudio; /// Flag indicating whether or not to grab when XBMC is playing nothing (in menu) - bool _grabMenu; + const bool _grabMenu; - /// Previous emitted grab state - GrabbingMode _previousMode; + /// Flag inidcating whether or not to grab when the XBMC screensaver is activated + const bool _grabScreensaver; + + /// Flag indicating wheter or not to enable the detection of 3D movies playing + const bool _enable3DDetection; + + /// Flag indicating if XBMC is on screensaver + bool _previousScreensaverMode; + + /// Previous emitted grab mode + GrabbingMode _previousGrabbingMode; + + /// Previous emitted video mode + VideoMode _previousVideoMode; }; diff --git a/libsrc/dispmanx-grabber/DispmanxFrameGrabber.cpp b/libsrc/dispmanx-grabber/DispmanxFrameGrabber.cpp index 4681e8ff..1f6516a4 100644 --- a/libsrc/dispmanx-grabber/DispmanxFrameGrabber.cpp +++ b/libsrc/dispmanx-grabber/DispmanxFrameGrabber.cpp @@ -61,10 +61,29 @@ void DispmanxFrameGrabber::setFlags(const int vc_flags) _vc_flags = vc_flags; } +void DispmanxFrameGrabber::setVideoMode(const VideoMode videoMode) +{ + switch (videoMode) { + case VIDEO_3DSBS: + vc_dispmanx_rect_set(&_rectangle, 0, 0, _width/2, _height); + break; + case VIDEO_3DTAB: + vc_dispmanx_rect_set(&_rectangle, 0, 0, _width, _height/2); + break; + case VIDEO_2D: + default: + vc_dispmanx_rect_set(&_rectangle, 0, 0, _width, _height); + break; + } +} + void DispmanxFrameGrabber::grabFrame(Image & image) { - // Sanity check of the given image size - assert(image.width() == _width && image.height() == _height); + // resize the given image if needed + if (image.width() != unsigned(_rectangle.width) || image.height() != unsigned(_rectangle.height)) + { + image.resize(_rectangle.width, _rectangle.height); + } // Open the connection to the display _vc_display = vc_dispmanx_display_open(0); @@ -74,7 +93,7 @@ void DispmanxFrameGrabber::grabFrame(Image & image) // Read the snapshot into the memory void* image_ptr = image.memptr(); - const unsigned destPitch = _width * sizeof(ColorRgba); + const unsigned destPitch = _rectangle.width * sizeof(ColorRgba); vc_dispmanx_resource_read_data(_vc_resource, &_rectangle, image_ptr, destPitch); // Close the displaye diff --git a/libsrc/dispmanx-grabber/DispmanxFrameGrabber.h b/libsrc/dispmanx-grabber/DispmanxFrameGrabber.h index f7a0b07f..f0aeeda8 100644 --- a/libsrc/dispmanx-grabber/DispmanxFrameGrabber.h +++ b/libsrc/dispmanx-grabber/DispmanxFrameGrabber.h @@ -10,6 +10,7 @@ // Utils includes #include #include +#include /// /// The DispmanxFrameGrabber is used for creating snapshots of the display (screenshots) with a @@ -34,6 +35,12 @@ public: /// void setFlags(const int vc_flags); + /// + /// Set the video mode (2D/3D) + /// @param[in] mode The new video mode + /// + void setVideoMode(const VideoMode videoMode); + /// /// Captures a single snapshot of the display and writes the data to the given image. The /// provided image should have the same dimensions as the configured values (_width and @@ -58,8 +65,7 @@ private: int _vc_flags; /// With of the captured snapshot [pixels] - unsigned _width; + const unsigned _width; /// Height of the captured snapshot [pixels] - unsigned _height; - + const unsigned _height; }; diff --git a/libsrc/dispmanx-grabber/DispmanxWrapper.cpp b/libsrc/dispmanx-grabber/DispmanxWrapper.cpp index 9fe1aee9..5ffb2a48 100644 --- a/libsrc/dispmanx-grabber/DispmanxWrapper.cpp +++ b/libsrc/dispmanx-grabber/DispmanxWrapper.cpp @@ -81,3 +81,8 @@ void DispmanxWrapper::setGrabbingMode(const GrabbingMode mode) break; } } + +void DispmanxWrapper::setVideoMode(const VideoMode mode) +{ + _frameGrabber->setVideoMode(mode); +} diff --git a/libsrc/hyperion/hyperion.schema.json b/libsrc/hyperion/hyperion.schema.json index 44733252..bd059aa4 100644 --- a/libsrc/hyperion/hyperion.schema.json +++ b/libsrc/hyperion/hyperion.schema.json @@ -255,6 +255,14 @@ "grabMenu" : { "type" : "boolean", "required" : true + }, + "grabScreensaver" : { + "type" : "boolean", + "required" : false + }, + "enable3DDetection" : { + "type" : "boolean", + "required" : false } }, "additionalProperties" : false diff --git a/libsrc/xbmcvideochecker/XBMCVideoChecker.cpp b/libsrc/xbmcvideochecker/XBMCVideoChecker.cpp index 2247c129..af755f52 100644 --- a/libsrc/xbmcvideochecker/XBMCVideoChecker.cpp +++ b/libsrc/xbmcvideochecker/XBMCVideoChecker.cpp @@ -1,52 +1,50 @@ // Qt includes #include +#include +#include #include -XBMCVideoChecker::XBMCVideoChecker(const std::string & address, uint16_t port, uint64_t interval_ms, bool grabVideo, bool grabPhoto, bool grabAudio, bool grabMenu) : +// Request player example: +// {"id":666,"jsonrpc":"2.0","method":"Player.GetActivePlayers"} +// {"id":666,"jsonrpc":"2.0","result":[{"playerid":1,"type":"video"}]} + +// Request playing item example: +// {"id":667,"jsonrpc":"2.0","method":"Player.GetItem","params":{"playerid":1,"properties":["file"]}} +// {"id":667,"jsonrpc":"2.0","result":{"item":{"file":"smb://xbmc:xbmc@192.168.53.12/video/Movies/Avatar (2009)/Avatar.mkv","label":"Avatar","type":"unknown"}}} + +// Request if screensaver is on +// {"id":668,"jsonrpc":"2.0","method":"XBMC.GetInfoBooleans","params":{"booleans":["System.ScreenSaverActive"]}} +// {"id":668,"jsonrpc":"2.0","result":{"System.ScreenSaverActive":false}} + +XBMCVideoChecker::XBMCVideoChecker(const std::string & address, uint16_t port, bool grabVideo, bool grabPhoto, bool grabAudio, bool grabMenu, bool grabScreensaver, bool enable3DDetection) : QObject(), _address(QString::fromStdString(address)), _port(port), - _request(R"({"jsonrpc":"2.0","method":"Player.GetActivePlayers","id":666})"), - _timer(), + _activePlayerRequest(R"({"id":666,"jsonrpc":"2.0","method":"Player.GetActivePlayers"})"), + _currentPlayingItemRequest(R"({"id":667,"jsonrpc":"2.0","method":"Player.GetItem","params":{"playerid":%1,"properties":["file"]}})"), + _checkScreensaverRequest(R"({"id":668,"jsonrpc":"2.0","method":"XBMC.GetInfoBooleans","params":{"booleans":["System.ScreenSaverActive"]}})"), _socket(), _grabVideo(grabVideo), _grabPhoto(grabPhoto), _grabAudio(grabAudio), _grabMenu(grabMenu), - _previousMode(GRABBINGMODE_INVALID) + _grabScreensaver(grabScreensaver), + _enable3DDetection(enable3DDetection), + _previousScreensaverMode(false), + _previousGrabbingMode(GRABBINGMODE_INVALID), + _previousVideoMode(VIDEO_2D) { - // setup timer - _timer.setSingleShot(false); - _timer.setInterval(interval_ms); - connect(&_timer, SIGNAL(timeout()), this, SLOT(sendRequest())); - // setup socket connect(&_socket, SIGNAL(readyRead()), this, SLOT(receiveReply())); + connect(&_socket, SIGNAL(disconnected()), this, SLOT(disconnected())); + connect(&_socket, SIGNAL(connected()), this, SLOT(connected())); + connect(&_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(connectionError(QAbstractSocket::SocketError))); } void XBMCVideoChecker::start() { - _timer.start(); -} - -void XBMCVideoChecker::sendRequest() -{ - switch (_socket.state()) - { - case QTcpSocket::UnconnectedState: - // not connected. try to connect - std::cout << "Connecting to " << _address.toStdString() << ":" << _port << " to check XBMC player status" << std::endl; - _socket.connectToHost(_address, _port); - break; - case QTcpSocket::ConnectedState: - // write the request on the socket - _socket.write(_request); - break; - default: - // whatever. let's check again at the next timer tick - break; - } + disconnected(); } void XBMCVideoChecker::receiveReply() @@ -54,62 +52,195 @@ void XBMCVideoChecker::receiveReply() // expect that the reply is received as a single message. Probably oke considering the size of the expected reply QString reply(_socket.readAll()); - // check if the resply is a reply to one of my requests - if (!reply.contains("\"id\":666")) + std::cout << "Message from XBMC: " << reply.toStdString() << std::endl; + + if (reply.contains("\"method\":\"Player.OnPlay\"")) { - // probably not. Leave this mreply as is and don't act on it + // send a request for the current player state + _socket.write(_activePlayerRequest.toUtf8()); return; } + else if (reply.contains("\"method\":\"Player.OnStop\"")) + { + // the player has stopped + setGrabbingMode(_grabMenu ? GRABBINGMODE_MENU : GRABBINGMODE_OFF); + setVideoMode(VIDEO_2D); + } + else if (reply.contains("\"method\":\"GUI.OnScreensaverActivated\"")) + { + setScreensaverMode(!_grabScreensaver); + } + else if (reply.contains("\"method\":\"GUI.OnScreensaverDeactivated\"")) + { + setScreensaverMode(false); + } + else if (reply.contains("\"id\":666")) + { + // Result of Player.GetActivePlayers - GrabbingMode newMode = GRABBINGMODE_INVALID; - if (reply.contains("video")) - { - // video is playing - newMode = _grabVideo ? GRABBINGMODE_VIDEO : GRABBINGMODE_OFF; - } - else if (reply.contains("picture")) - { - // picture viewer is playing - newMode = _grabPhoto ? GRABBINGMODE_PHOTO : GRABBINGMODE_OFF; - } - else if (reply.contains("audio")) - { - // audio is playing - newMode = _grabAudio ? GRABBINGMODE_AUDIO : GRABBINGMODE_OFF; - } - else - { - // Nothing is playing. - newMode = _grabMenu ? GRABBINGMODE_MENU : GRABBINGMODE_OFF; - } + // always start a new video in 2D mode + emit videoMode(VIDEO_2D); - // emit new state if applicable - if (newMode != _previousMode && newMode != GRABBINGMODE_INVALID) - { - switch (newMode) + if (reply.contains("video")) { - case GRABBINGMODE_VIDEO: - std::cout << "XBMC checker: switching to VIDEO mode" << std::endl; - break; - case GRABBINGMODE_PHOTO: - std::cout << "XBMC checker: switching to PHOTO mode" << std::endl; - break; - case GRABBINGMODE_AUDIO: - std::cout << "XBMC checker: switching to AUDIO mode" << std::endl; - break; - case GRABBINGMODE_MENU: - std::cout << "XBMC checker: switching to MENU mode" << std::endl; - break; - case GRABBINGMODE_OFF: - std::cout << "XBMC checker: switching to OFF mode" << std::endl; - break; - case GRABBINGMODE_INVALID: - std::cout << "XBMC checker: switching to INVALID mode" << std::endl; - break; - } + // video is playing + setGrabbingMode(_grabVideo ? GRABBINGMODE_VIDEO : GRABBINGMODE_OFF); - emit grabbingMode(newMode); - _previousMode = newMode; + // we need to get the filename + // first retrieve the playerid + QString key = "\"playerid\":"; + QRegExp regex(key + "(\\d+)"); + int pos = regex.indexIn(reply); + if (pos > 0) + { + // now request info of the playing item + QStringRef idText(&reply, pos + key.length(), regex.matchedLength() - key.length()); + _socket.write(_currentPlayingItemRequest.arg(idText.toString()).toUtf8()); + } + } + else if (reply.contains("picture")) + { + // picture viewer is playing + setGrabbingMode(_grabPhoto ? GRABBINGMODE_PHOTO : GRABBINGMODE_OFF); + } + else if (reply.contains("audio")) + { + // audio is playing + setGrabbingMode(_grabAudio ? GRABBINGMODE_AUDIO : GRABBINGMODE_OFF); + } + else + { + // Nothing is playing. + setGrabbingMode(_grabMenu ? GRABBINGMODE_MENU : GRABBINGMODE_OFF); + } + } + else if (reply.contains("\"id\":667")) + { + // result of Player.GetItem + // TODO: what if the filename contains a '"'. In Json this should have been escaped + QRegExp regex("\"file\":\"((?!\").)*\""); + int pos = regex.indexIn(reply); + if (pos > 0) + { + QStringRef filename = QStringRef(&reply, pos+8, regex.matchedLength()-9); + if (filename.contains("3DSBS", Qt::CaseInsensitive) || filename.contains("HSBS", Qt::CaseInsensitive)) + { + setVideoMode(VIDEO_3DSBS); + } + else if (filename.contains("3DTAB", Qt::CaseInsensitive) || filename.contains("HTAB", Qt::CaseInsensitive)) + { + setVideoMode(VIDEO_3DTAB); + } + else + { + setVideoMode(VIDEO_2D); + } + } + } + else if (reply.contains("\"id\":668")) + { + // result of System.ScreenSaverActive + bool active = reply.contains("\"System.ScreenSaverActive\":true"); + setScreensaverMode(!_grabScreensaver && active); } } +void XBMCVideoChecker::connected() +{ + std::cout << "XBMC Connected" << std::endl; + + // send a request for the current player state + _socket.write(_activePlayerRequest.toUtf8()); + _socket.write(_checkScreensaverRequest.toUtf8()); +} + +void XBMCVideoChecker::disconnected() +{ + std::cout << "XBMC Disconnected" << std::endl; + + // try to connect + _socket.connectToHost(_address, _port); +} + +void XBMCVideoChecker::connectionError(QAbstractSocket::SocketError) +{ + std::cout << "XBMC Connection error" << std::endl; + + // try to connect again in 1 second + QTimer::singleShot(1000, this, SLOT(disconnected())); +} + +void XBMCVideoChecker::setGrabbingMode(GrabbingMode newGrabbingMode) +{ + if (newGrabbingMode == _previousGrabbingMode) + { + // no change + return; + } + + switch (newGrabbingMode) + { + case GRABBINGMODE_VIDEO: + std::cout << "XBMC checker: switching to VIDEO mode" << std::endl; + break; + case GRABBINGMODE_PHOTO: + std::cout << "XBMC checker: switching to PHOTO mode" << std::endl; + break; + case GRABBINGMODE_AUDIO: + std::cout << "XBMC checker: switching to AUDIO mode" << std::endl; + break; + case GRABBINGMODE_MENU: + std::cout << "XBMC checker: switching to MENU mode" << std::endl; + break; + case GRABBINGMODE_OFF: + std::cout << "XBMC checker: switching to OFF mode" << std::endl; + break; + case GRABBINGMODE_INVALID: + std::cout << "XBMC checker: switching to INVALID mode" << std::endl; + break; + } + + // only emit the new state when we want to grab in screensaver mode or when the screensaver is deactivated + if (!_previousScreensaverMode) + { + emit grabbingMode(newGrabbingMode); + } + _previousGrabbingMode = newGrabbingMode; +} + +void XBMCVideoChecker::setScreensaverMode(bool isOnScreensaver) +{ + if (isOnScreensaver == _previousScreensaverMode) + { + // no change + return; + } + + emit grabbingMode(isOnScreensaver ? GRABBINGMODE_OFF : _previousGrabbingMode); + _previousScreensaverMode = isOnScreensaver; +} + +void XBMCVideoChecker::setVideoMode(VideoMode newVideoMode) +{ + if (newVideoMode == _previousVideoMode) + { + // no change + return; + } + + switch (newVideoMode) + { + case VIDEO_2D: + std::cout << "XBMC checker: switching to 2D mode" << std::endl; + break; + case VIDEO_3DSBS: + std::cout << "XBMC checker: switching to 3D SBS mode" << std::endl; + break; + case VIDEO_3DTAB: + std::cout << "XBMC checker: switching to 3D TAB mode" << std::endl; + break; + } + + emit videoMode(newVideoMode); + _previousVideoMode = newVideoMode; +} diff --git a/src/hyperiond/hyperiond.cpp b/src/hyperiond/hyperiond.cpp index 3f46c712..3b234ce2 100644 --- a/src/hyperiond/hyperiond.cpp +++ b/src/hyperiond/hyperiond.cpp @@ -123,11 +123,12 @@ int main(int argc, char** argv) xbmcVideoChecker = new XBMCVideoChecker( videoCheckerConfig["xbmcAddress"].asString(), videoCheckerConfig["xbmcTcpPort"].asUInt(), - 1000, videoCheckerConfig["grabVideo"].asBool(), videoCheckerConfig["grabPictures"].asBool(), videoCheckerConfig["grabAudio"].asBool(), - videoCheckerConfig["grabMenu"].asBool()); + videoCheckerConfig["grabMenu"].asBool(), + videoCheckerConfig.get("grabScreensaver", true).asBool(), + videoCheckerConfig.get("enable3DDetection", true).asBool()); xbmcVideoChecker->start(); std::cout << "XBMC video checker created and started" << std::endl; @@ -148,6 +149,7 @@ int main(int argc, char** argv) if (xbmcVideoChecker != nullptr) { QObject::connect(xbmcVideoChecker, SIGNAL(grabbingMode(GrabbingMode)), dispmanx, SLOT(setGrabbingMode(GrabbingMode))); + QObject::connect(xbmcVideoChecker, SIGNAL(videoMode(VideoMode)), dispmanx, SLOT(setVideoMode(VideoMode))); } dispmanx->start(); diff --git a/test/TestRs232HighSpeed.cpp b/test/TestRs232HighSpeed.cpp index 95707ca4..5b3f02d6 100644 --- a/test/TestRs232HighSpeed.cpp +++ b/test/TestRs232HighSpeed.cpp @@ -12,8 +12,8 @@ public: // empty } - int write(const std::vector &ledValues) {} - int switchOff() {}; + int write(const std::vector &ledValues) { return 0; } + int switchOff() { return 0; } void writeTestSequence() {