1
0
Fork 0

Feature for requesting canvas images per HTTP as described in http://wiki.flightgear.org/Read_canvas_image_by_HTTP

This commit is contained in:
ThomasS 2018-05-03 10:25:34 +02:00
parent 2a31893862
commit 2cbd660dc9

View file

@ -27,6 +27,9 @@
#include <osgUtil/SceneView> #include <osgUtil/SceneView>
#include <osgViewer/Viewer> #include <osgViewer/Viewer>
#include <Canvas/canvas_mgr.hxx>
#include <simgear/canvas/Canvas.hxx>
#include <simgear/threads/SGQueue.hxx> #include <simgear/threads/SGQueue.hxx>
#include <simgear/structure/Singleton.hxx> #include <simgear/structure/Singleton.hxx>
#include <Main/globals.hxx> #include <Main/globals.hxx>
@ -39,6 +42,8 @@ using std::string;
using std::vector; using std::vector;
using std::list; using std::list;
namespace sc = simgear::canvas;
namespace flightgear { namespace flightgear {
namespace http { namespace http {
@ -328,6 +333,114 @@ private:
ScreenshotCallback * _screenshotCallback; 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<CanvasMgr*> (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<osg::Image> 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<OpenThreads::Mutex> lock(_lock);
_compressedData = s;
// allow destructor
currenttask = NULL;
}
string getCanvasImage() {
string reply;
{
// called from the main loop
OpenThreads::ScopedLock<OpenThreads::Mutex> 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) ScreenshotUriHandler::ScreenshotUriHandler(const char * uri)
: URIHandler(uri) : URIHandler(uri)
{ {
@ -339,7 +452,8 @@ ScreenshotUriHandler::~ScreenshotUriHandler()
//ImageCompressorSingleton::instance()->join(); //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" #define BOUNDARY "--fgfs-screenshot-boundary"
bool ScreenshotUriHandler::handleGetRequest(const HTTPRequest & request, HTTPResponse & response, Connection * connection) 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()); 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> screenshotRequest; SGSharedPtr<ScreenshotRequest> screenshotRequest;
SGSharedPtr<CanvasImageRequest> canvasimageRequest;
try { try {
SG_LOG(SG_NETWORK, SG_DEBUG, "new ScreenshotRequest("<<window<<","<<type<<"," << stream << ")"); SG_LOG(SG_NETWORK, SG_DEBUG, "new ScreenshotRequest("<<window<<","<<type<<"," << stream << "," << canvasindex <<")");
if (canvasindex == -1)
screenshotRequest = new ScreenshotRequest(window, type, stream); screenshotRequest = new ScreenshotRequest(window, type, stream);
else
canvasimageRequest = new CanvasImageRequest(window, type, canvasindex, stream);
} }
catch (sg_format_exception & ex) catch (sg_format_exception & ex)
{ {
@ -385,14 +507,17 @@ bool ScreenshotUriHandler::handleGetRequest(const HTTPRequest & request, HTTPRes
} }
connection->put(KEY, screenshotRequest); if (canvasindex == -1)
connection->put(KEY_SCREENSHOT, screenshotRequest);
else
connection->put(KEY_CANVASIMAGE, canvasimageRequest);
return false; // call me again thru poll return false; // call me again thru poll
} }
bool ScreenshotUriHandler::poll(Connection * connection) bool ScreenshotUriHandler::poll(Connection * connection)
{ {
SGSharedPtr<ConnectionData> data = connection->get(KEY_SCREENSHOT);
SGSharedPtr<ConnectionData> data = connection->get(KEY); if (data) {
ScreenshotRequest * screenshotRequest = dynamic_cast<ScreenshotRequest*>(data.get()); ScreenshotRequest * screenshotRequest = dynamic_cast<ScreenshotRequest*>(data.get());
if ( NULL == screenshotRequest) return true; // Should not happen, kill the connection if ( NULL == screenshotRequest) return true; // Should not happen, kill the connection
@ -421,7 +546,45 @@ bool ScreenshotUriHandler::poll(Connection * connection)
} }
// single screenshot, send terminating chunk // single screenshot, send terminating chunk
connection->remove(KEY); connection->remove(KEY_SCREENSHOT);
connection->write("", 0);
return true; // done.
} // Screenshot
// CanvasImage
data = connection->get(KEY_CANVASIMAGE);
CanvasImageRequest * canvasimageRequest = dynamic_cast<CanvasImageRequest*> (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();
}
const string & canvasimage = canvasimageRequest->getCanvasImage();
if (canvasimage.empty()) {
SG_LOG(SG_NETWORK, SG_INFO, "No canvasimage available.");
return false; // not ready yet, call again.
}
SG_LOG(SG_NETWORK, SG_DEBUG, "CanvasImage is ready, size=" << canvasimage.size());
if (canvasimageRequest->isStream()) {
string s(BOUNDARY "\r\nContent-Type: image/");
s.append(canvasimageRequest->getType()).append("\r\nContent-Length:");
s += boost::lexical_cast<string>(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 canvasimage, send terminating chunk
connection->remove(KEY_CANVASIMAGE);
connection->write("", 0); connection->write("", 0);
return true; // done. return true; // done.
} }