Fawkes API  Fawkes Development Version
image_processor.cpp
1 
2 /***************************************************************************
3  * image_processor.cpp - Web request processor for images/streams
4  *
5  * Created: Wed Feb 05 17:48:34 2014
6  * Copyright 2006-2014 Tim Niemueller [www.niemueller.de]
7  ****************************************************************************/
8 
9 /* This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Library General Public License for more details.
18  *
19  * Read the full text in the LICENSE.GPL file in the doc directory.
20  */
21 
22 #include "image_processor.h"
23 #include "mjpeg_reply.h"
24 #include "jpeg_stream_producer.h"
25 #include <webview/file_reply.h>
26 #include <webview/error_reply.h>
27 
28 #include <core/exception.h>
29 #include <core/threading/thread_collector.h>
30 #include <config/config.h>
31 #include <logging/logger.h>
32 #include <fvutils/ipc/shm_image.h>
33 
34 #include <cstring>
35 #include <cstdlib>
36 #include <string>
37 
38 using namespace fawkes;
39 using namespace firevision;
40 
41 /** @class WebviewImageRequestProcessor "image_processor.h"
42  * Image stream web processor.
43  * This processor provides access to image buffers on the system as
44  * Image streams.
45  * @author Tim Niemueller
46  */
47 
48 /** Constructor.
49  * @param baseurl Base URL where the static processor is mounted
50  * @param config system configuration
51  * @param logger logger
52  * @param thread_col thread collector to use for stream producers
53  */
55  fawkes::Configuration *config,
56  fawkes::Logger *logger,
57  fawkes::ThreadCollector *thread_col)
58 {
59  config_ = config;
60  logger_ = logger;
61  thread_col_ = thread_col;
62  baseurl_ = strdup(baseurl);
63  baseurl_len_ = strlen(baseurl_);
64 }
65 
66 /** Destructor. */
68 {
69  free(baseurl_);
70 #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100) > 40600
71  for (auto &s : streams_) {
72 #else
73  std::map<std::string, fawkes::WebviewJpegStreamProducer *>::iterator si;
74  for (si = streams_.begin(); si != streams_.end(); ++si) {
75  std::pair<const std::string, fawkes::WebviewJpegStreamProducer *> &s = *si;
76 #endif
77  thread_col_->remove(s.second);
78  delete s.second;
79  }
80 }
81 
83 WebviewImageRequestProcessor::get_stream(const std::string &image_id)
84 {
85  if (streams_.find(image_id) == streams_.end()) {
86  try {
87  std::string cfg_prefix = "/webview/images/" + image_id + "/";
88  unsigned int quality = 80;
89  float fps = 15;
90  bool vflip = false;
91  // Read default values if set
92  try {
93  quality = config_->get_uint("/webview/images/default/jpeg-quality");
94  } catch (Exception &e) {} // ignored, use default
95  try {
96  fps = config_->get_float("/webview/images/default/mjpeg-fps");
97  } catch (Exception &e) {} // ignored, use default
98  try {
99  vflip = config_->get_bool("/webview/images/default/jpeg-vflip");
100  } catch (Exception &e) {} // ignored, use default
101  // Set camera-specific values
102  try {
103  quality = config_->get_uint((cfg_prefix + "jpeg-quality").c_str());
104  } catch (Exception &e) {} // ignored, use default
105  try {
106  fps = config_->get_float((cfg_prefix + "mjpeg-fps").c_str());
107  } catch (Exception &e) {} // ignored, use default
108  try {
109  vflip = config_->get_bool((cfg_prefix + "jpeg-vflip").c_str());
110  } catch (Exception &e) {} // ignored, use default
111 
112  WebviewJpegStreamProducer *stream =
113  new WebviewJpegStreamProducer(image_id.c_str(), quality, fps, vflip);
114 
115  thread_col_->add(stream);
116 
117  streams_[image_id] = stream;
118  } catch (Exception &e) {
119  logger_->log_warn("WebImageReqProc", "Failed to open buffer '%s',"
120  " exception follows", image_id.c_str());
121  logger_->log_warn("WebImageReqProc", e);
122  return NULL;
123  }
124  }
125 
126  return streams_[image_id];
127 }
128 
129 
130 WebReply *
132 {
133  if ( strncmp(baseurl_, request->url().c_str(), baseurl_len_) == 0 ) {
134  // It is in our URL prefix range
135  std::string subpath = request->url().substr(baseurl_len_);
136 
137  if (subpath.find("/view/") == 0) {
138  // still image
139 
140  std::string::size_type last_dot = subpath.rfind(".");
141  if (last_dot == std::string::npos) {
142  return new WebErrorPageReply(WebReply::HTTP_NOT_FOUND, "Invalid stream ID");
143  }
144  std::string image_id = subpath.substr(6, last_dot - 6);
145  std::string image_type = subpath.substr(last_dot + 1);
146 
147  WebviewJpegStreamProducer *stream = get_stream(image_id);
148  if (! stream) {
149  return new WebErrorPageReply(WebReply::HTTP_NOT_FOUND, "Stream not found");
150  }
151 
152  if (image_type == "jpeg" || image_type == "jpg") {
154 
155  //logger_->log_debug("WebImageReqProc", "Compressed buffer size: %zu", buf->size());
156  std::string body((char *)buf->data(), buf->size());
157  StaticWebReply *reply = new StaticWebReply(WebReply::HTTP_OK, body);
158  reply->add_header("Content-type", "image/jpeg");
159  reply->set_caching(false);
160  return reply;
161  } else if (image_type == "mjpeg" || image_type == "mjpg") {
162  return new DynamicMJPEGStreamWebReply(stream);
163  } else {
164  return new WebErrorPageReply(WebReply::HTTP_NOT_FOUND, "Unknown image format");
165  }
166  } else if (subpath == "" || subpath == "/") {
167  // list all buffers
168 
169  WebPageReply *r = new WebPageReply("Image Buffers");
170  std::list<SharedMemoryImageBufferMetaData> meta_data =
171  SharedMemoryImageBuffer::list_meta_data();
172 
173  if (meta_data.empty()) {
174  *r += "<p><b>No image buffers found.</b></p>\n";
175  } else {
176  *r += "<h2>Image Buffers</h2>\n";
177  *r += "<table>\n";
178  *r += "<tr><th>Buffer</th><th>Frame</th><th>Colorspace</th>"
179  "<th>Dimensions</th><th>Memory</th><th>View as</th></tr>\n";
180 #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100) > 40600
181  for (auto &m : meta_data) {
182 #else
183  std::list<SharedMemoryImageBufferMetaData>::iterator mi;
184  for (mi = meta_data.begin(); mi != meta_data.end(); ++mi) {
186 #endif
187  r->append_body("<tr><td>%s</td><td>%s</td><td>%s</td>"
188  "<td>%ux%u</td><td>%zu B</td>"
189  "<td><div class=\"actionlist\"><ul><li><a href=\"%s/view/%s.jpg\">JPEG</a></li>"
190  "<li><a href=\"%s/view/%s.mjpeg\">Stream</a></li></ul></div></td>"
191  "</tr>\n", m.image_id.c_str(), m.frame_id.c_str(),
192  colorspace_to_string(m.colorspace), m.width, m.height,
193  m.mem_size, baseurl_, m.image_id.c_str(), baseurl_, m.image_id.c_str());
194  }
195  *r += "</table>\n";
196  }
197 
198  return r;
199 
200  } else {
201  return new WebErrorPageReply(WebReply::HTTP_NOT_FOUND, "Unknown request");
202  }
203  } else {
204  // wrong base url, why the heck are we called!?
205  logger_->log_error("WebImageReqProc", "Called for invalid base url "
206  "(url: %s, baseurl: %s)", request->url().c_str(), baseurl_);
207  return NULL;
208  }
209 }
virtual ~WebviewImageRequestProcessor()
Destructor.
virtual fawkes::WebReply * process_request(const fawkes::WebRequest *request)
Process a request.
Fawkes library namespace.
Thread collector.
const unsigned char * data() const
Get data buffer.
Shared memory image buffer meta data container.
Definition: shm_image.h:135
unsigned int height
Image height.
Definition: shm_image.h:142
Base class for exceptions in Fawkes.
Definition: exception.h:36
size_t size() const
Get buffer size.
std::string image_id
Image buffer ID.
Definition: shm_image.h:138
Dynamic raw file transfer reply.
Definition: mjpeg_reply.h:36
Web request meta data carrier.
Definition: request.h:42
std::string frame_id
Coordinate frame ID.
Definition: shm_image.h:139
Basic page reply.
Definition: page_reply.h:36
Basic web reply.
Definition: reply.h:36
RefPtr<> is a reference-counting shared smartpointer.
Definition: refptr.h:49
void append_body(const char *format,...)
Append to body.
Definition: reply.cpp:224
size_t mem_size
Shared memory buffer size.
Definition: shm_image.h:144
const std::string & url() const
Get URL.
Definition: request.h:69
colorspace_t colorspace
Colorspace.
Definition: shm_image.h:140
WebviewImageRequestProcessor(const char *baseurl, fawkes::Configuration *config, fawkes::Logger *logger, fawkes::ThreadCollector *thread_col)
Constructor.
Interface for configuration handling.
Definition: config.h:67
Static error page reply.
Definition: error_reply.h:33
Static web reply.
Definition: reply.h:133
Interface for logging.
Definition: logger.h:34
RefPtr< Buffer > wait_for_next_frame()
Blocks caller until new thread is available.