diff --git a/src/Network/http/ScreenshotUriHandler.cxx b/src/Network/http/ScreenshotUriHandler.cxx index c072c303b..f2e6c24ed 100644 --- a/src/Network/http/ScreenshotUriHandler.cxx +++ b/src/Network/http/ScreenshotUriHandler.cxx @@ -27,6 +27,9 @@ #include #include +#include +#include + #include #include #include
@@ -39,6 +42,8 @@ using std::string; using std::vector; using std::list; +namespace sc = simgear::canvas; + namespace flightgear { namespace http { @@ -328,6 +333,114 @@ private: ScreenshotCallback * _screenshotCallback; }; +/** + */ +class CanvasImageRequest : public ConnectionData, public simgear::canvas::CanvasImageReadyListener, StringReadyListener { +public: + ImageCompressionTask *currenttask=NULL; + sc::CanvasPtr canvas; + int connected = 0; + + CanvasImageRequest(const string & window, const string & type, int canvasindex, bool stream) + : _type(type), _stream(stream) { + SG_LOG(SG_NETWORK, SG_DEBUG, "CanvasImageRequest:"); + + if (NULL == osgDB::Registry::instance()->getReaderWriterForExtension(_type)) + throw sg_format_exception("Unsupported image type: " + type, type); + + CanvasMgr* canvas_mgr = static_cast (globals->get_subsystem("Canvas")); + if (!canvas_mgr) { + SG_LOG(SG_NETWORK, SG_WARN, "CanvasImage:CanvasMgr not found"); + } else { + canvas = canvas_mgr->getCanvas(canvasindex); + if (!canvas) { + throw sg_error("CanvasImage:Canvas not found for index " + std::to_string(canvasindex)); + } else { + SG_LOG(SG_NETWORK, SG_DEBUG, "CanvasImage:Canvas found for index " << canvasindex); + //SG_LOG(SG_NETWORK, SG_DEBUG, "CanvasImageRequest: found camera " << camera << ", width=" << canvas->getSizeX() << ", height=%d\n" << canvas->getSizeY()); + + SGConstPropertyNode_ptr canvasnode = canvas->getProps(); + if (canvasnode) { + const char *canvasname = canvasnode->getStringValue("name"); + if (canvasname) { + SG_LOG(SG_NETWORK, SG_INFO, "CanvasImageRequest: node=" << canvasnode->getDisplayName().c_str() << ", canvasname =" << canvasname); + } + } + //Looping until success is no option + connected = canvas->subscribe(this); + } + } + } + + // Assumption: when unsubscribe returns,there might just be a compressor thread running, + // causing a crash when the deconstructor finishes. Rare, but might happen. Just wait to be sure. + virtual ~CanvasImageRequest() { + if (currenttask){ + SG_LOG(SG_NETWORK, SG_INFO, "canvasimage task running"); +#ifdef _WIN32 + Sleep(15000); +#else + sleep(15); +#endif + } + + if (canvas && connected){ + canvas->unsubscribe(this); + } + } + + virtual void imageReady(osg::ref_ptr rawImage) { + SG_LOG(SG_NETWORK, SG_DEBUG, "CanvasImage:imageReady"); + // called from a rendering thread, not from the main loop + ImageCompressionTask task; + currenttask = &task; + task.image = rawImage; + task.format = _type; + task.stringReadyListener = this; + ImageCompressorSingleton::instance()->addTask(task); + } + + void requestCanvasImage() { + connected = canvas->subscribe(this); + } + + mutable OpenThreads::Mutex _lock; + + virtual void stringReady(const string & s) { + SG_LOG(SG_NETWORK, SG_DEBUG, "CanvasImage:stringReady"); + + // called from the compressor thread + OpenThreads::ScopedLock lock(_lock); + _compressedData = s; + // allow destructor + currenttask = NULL; + } + + string getCanvasImage() { + string reply; + { + // called from the main loop + OpenThreads::ScopedLock lock(_lock); + reply = _compressedData; + _compressedData.clear(); + } + return reply; + } + + bool isStream() const { + return _stream; + } + + const string & getType() const { + return _type; + } + +private: + string _type; + bool _stream; + string _compressedData; +}; + ScreenshotUriHandler::ScreenshotUriHandler(const char * uri) : URIHandler(uri) { @@ -339,7 +452,8 @@ ScreenshotUriHandler::~ScreenshotUriHandler() //ImageCompressorSingleton::instance()->join(); } -const static string KEY("ScreenshotUriHandler::ScreenshotRequest"); +const static string KEY_SCREENSHOT("ScreenshotUriHandler::ScreenshotRequest"); +const static string KEY_CANVASIMAGE("ScreenshotUriHandler::CanvasImageRequest"); #define BOUNDARY "--fgfs-screenshot-boundary" bool ScreenshotUriHandler::handleGetRequest(const HTTPRequest & request, HTTPResponse & response, Connection * connection) @@ -355,10 +469,18 @@ bool ScreenshotUriHandler::handleGetRequest(const HTTPRequest & request, HTTPRes bool stream = (false == request.RequestVariables.get("stream").empty()); + int canvasindex = -1; + string s_canvasindex = request.RequestVariables.get("canvasindex"); + if (!s_canvasindex.empty()) canvasindex = atoi(s_canvasindex.c_str()); + SGSharedPtr screenshotRequest; + SGSharedPtr canvasimageRequest; try { - SG_LOG(SG_NETWORK, SG_DEBUG, "new ScreenshotRequest("<put(KEY, screenshotRequest); + if (canvasindex == -1) + connection->put(KEY_SCREENSHOT, screenshotRequest); + else + connection->put(KEY_CANVASIMAGE, canvasimageRequest); return false; // call me again thru poll } bool ScreenshotUriHandler::poll(Connection * connection) { + SGSharedPtr data = connection->get(KEY_SCREENSHOT); + if (data) { + ScreenshotRequest * screenshotRequest = dynamic_cast(data.get()); + if ( NULL == screenshotRequest) return true; // Should not happen, kill the connection - SGSharedPtr data = connection->get(KEY); - ScreenshotRequest * screenshotRequest = dynamic_cast(data.get()); - if ( NULL == screenshotRequest) return true; // Should not happen, kill the connection + const string & screenshot = screenshotRequest->getScreenshot(); + if (screenshot.empty()) { + SG_LOG(SG_NETWORK, SG_DEBUG, "No screenshot available."); + return false; // not ready yet, call again. + } - const string & screenshot = screenshotRequest->getScreenshot(); - if (screenshot.empty()) { - SG_LOG(SG_NETWORK, SG_DEBUG, "No screenshot available."); - return false; // not ready yet, call again. + SG_LOG(SG_NETWORK, SG_DEBUG, "Screenshot is ready, size=" << screenshot.size()); + + if (screenshotRequest->isStream()) { + string s( BOUNDARY "\r\nContent-Type: image/"); + s.append(screenshotRequest->getType()).append("\r\nContent-Length:"); + s += boost::lexical_cast(screenshot.size()); + s += "\r\n\r\n"; + connection->write(s.c_str(), s.length()); + } + + connection->write(screenshot.data(), screenshot.size()); + + if (screenshotRequest->isStream()) { + screenshotRequest->requestScreenshot(); + // continue until user closes connection + return false; + } + + // single screenshot, send terminating chunk + connection->remove(KEY_SCREENSHOT); + connection->write("", 0); + return true; // done. + } // Screenshot + + // CanvasImage + data = connection->get(KEY_CANVASIMAGE); + CanvasImageRequest * canvasimageRequest = dynamic_cast (data.get()); + if (NULL == canvasimageRequest) return true; // Should not happen, kill the connection + + if (!canvasimageRequest->connected) { + SG_LOG(SG_NETWORK, SG_INFO, "CanvasImageRequest: not connected. Resubscribing"); + canvasimageRequest->requestCanvasImage(); } - SG_LOG(SG_NETWORK, SG_DEBUG, "Screenshot is ready, size=" << screenshot.size()); - - if (screenshotRequest->isStream()) { - string s( BOUNDARY "\r\nContent-Type: image/"); - s.append(screenshotRequest->getType()).append("\r\nContent-Length:"); - s += boost::lexical_cast(screenshot.size()); - s += "\r\n\r\n"; - connection->write(s.c_str(), s.length()); + const string & canvasimage = canvasimageRequest->getCanvasImage(); + if (canvasimage.empty()) { + SG_LOG(SG_NETWORK, SG_INFO, "No canvasimage available."); + return false; // not ready yet, call again. } - connection->write(screenshot.data(), screenshot.size()); + SG_LOG(SG_NETWORK, SG_DEBUG, "CanvasImage is ready, size=" << canvasimage.size()); - if (screenshotRequest->isStream()) { - screenshotRequest->requestScreenshot(); - // continue until user closes connection - return false; + if (canvasimageRequest->isStream()) { + string s(BOUNDARY "\r\nContent-Type: image/"); + s.append(canvasimageRequest->getType()).append("\r\nContent-Length:"); + s += boost::lexical_cast(canvasimage.size()); + s += "\r\n\r\n"; + connection->write(s.c_str(), s.length()); + } + connection->write(canvasimage.data(), canvasimage.size()); + if (canvasimageRequest->isStream()) { + canvasimageRequest->requestCanvasImage(); + // continue until user closes connection + return false; } - // single screenshot, send terminating chunk - connection->remove(KEY); + // single canvasimage, send terminating chunk + connection->remove(KEY_CANVASIMAGE); connection->write("", 0); return true; // done. }