YARP
Yet Another Robot Platform
MjpegCarrier.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2006-2021 Istituto Italiano di Tecnologia (IIT)
3 * SPDX-FileCopyrightText: 2006-2010 RobotCub Consortium
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include "MjpegCarrier.h"
8#include "MjpegLogComponent.h"
9
10#include <cstdio>
11
12/*
13 On Windows, libjpeg does some slightly odd-ball stuff, including
14 unconditionally defining INT32 to be "long". This needs to
15 be worked around. Work around begins...
16 */
17#if defined(_WIN32)
18#define INT32 long // jpeg's definition
19#define QGLOBAL_H 1
20#endif
21
22#ifdef _MSC_VER
23#pragma warning (push)
24#pragma warning (disable : 4091)
25#endif
26
27extern "C" {
28#include <jpeglib.h>
29}
30
31#ifdef _MSC_VER
32#pragma warning (pop)
33#endif
34
35#if defined(_WIN32)
36#undef INT32
37#undef QGLOBAL_H
38#endif
39/*
40 work around ends.
41 */
42
43#include <yarp/sig/Image.h>
45#include <yarp/os/Name.h>
46#include <yarp/os/Bytes.h>
47#include <yarp/os/Route.h>
48
50
51#include <map>
52
53using namespace yarp::os;
54using namespace yarp::sig;
55using namespace yarp::wire_rep_utils;
56
57static const std::map<int, J_COLOR_SPACE> yarpCode2Mjpeg { {VOCAB_PIXEL_MONO, JCS_GRAYSCALE},
58 {VOCAB_PIXEL_MONO16, JCS_GRAYSCALE},
59 {VOCAB_PIXEL_RGB , JCS_RGB},
60#ifdef LIBJPEG_TURBO_VERSION
61 {VOCAB_PIXEL_RGBA , JCS_EXT_RGBA},
62 {VOCAB_PIXEL_BGRA , JCS_EXT_BGRA},
63 {VOCAB_PIXEL_BGR , JCS_EXT_BGR} };
64#else
65 };
66#endif
67
68static const std::map<int, int> yarpCode2Channels { {VOCAB_PIXEL_MONO, 1},
70 {VOCAB_PIXEL_RGB , 3},
71 {VOCAB_PIXEL_RGBA , 4},
72 {VOCAB_PIXEL_BGRA , 4},
73 {VOCAB_PIXEL_BGR , 3} };
74
75
77{
78 struct jpeg_destination_mgr pub;
79
80 JOCTET *buffer;
82 JOCTET cache[1000000]; // need to make this variable...
83};
84
86
87void send_net_data(JOCTET *data, int len, void *client) {
88 yCTrace(MJPEGCARRIER, "Send %d bytes", len);
89 auto* p = (ConnectionState *)client;
90 constexpr size_t hdr_size = 1000;
91 char hdr[hdr_size];
92 const char *brk = "\r\n";
93 std::snprintf(hdr, hdr_size, "Content-Type: image/jpeg%s\
94Content-Length: %d%s%s", brk, len, brk, brk);
95 Bytes hbuf(hdr,strlen(hdr));
96 p->os().write(hbuf);
97 Bytes buf((char *)data,len);
98 /*
99 // add corruption now and then, for testing.
100 static int ct = 0;
101 ct++;
102 if (ct==50) {
103 yCTrace(MJPEGCARRIER, "Adding corruption");
104 buf.get()[0] = 'z';
105 ct = 0;
106 }
107 */
108 p->os().write(buf);
109 std::snprintf(hdr, hdr_size, "%s--boundarydonotcross%s", brk, brk);
110 Bytes hbuf2(hdr,strlen(hdr));
111 p->os().write(hbuf2);
112
113}
114
115static void init_net_destination(j_compress_ptr cinfo) {
116 yCTrace(MJPEGCARRIER, "Initializing destination");
117 auto dest = (net_destination_ptr)cinfo->dest;
118 dest->buffer = &(dest->cache[0]);
119 dest->bufsize = sizeof(dest->cache);
120 dest->pub.next_output_byte = dest->buffer;
121 dest->pub.free_in_buffer = dest->bufsize;
122}
123
124static boolean empty_net_output_buffer(j_compress_ptr cinfo) {
125 auto dest = (net_destination_ptr)cinfo->dest;
126 yCWarning(MJPEGCARRIER, "Empty buffer - PROBLEM");
127 send_net_data(dest->buffer,dest->bufsize-dest->pub.free_in_buffer,
128 cinfo->client_data);
129 dest->pub.next_output_byte = dest->buffer;
130 dest->pub.free_in_buffer = dest->bufsize;
131 return TRUE;
132}
133
134static void term_net_destination(j_compress_ptr cinfo) {
135 auto dest = (net_destination_ptr)cinfo->dest;
136 yCTrace(MJPEGCARRIER, "Terminating net %d %zd", dest->bufsize,dest->pub.free_in_buffer);
137 send_net_data(dest->buffer,dest->bufsize-dest->pub.free_in_buffer,
138 cinfo->client_data);
139}
140
141void jpeg_net_dest(j_compress_ptr cinfo) {
143
144 //ERREXIT(cinfo, JERR_BUFFER_SIZE);
145
146 /* The destination object is made permanent so that multiple JPEG images
147 * can be written to the same buffer without re-executing jpeg_net_dest.
148 */
149 if (cinfo->dest == nullptr) { /* first time for this JPEG object? */
150 cinfo->dest = (struct jpeg_destination_mgr *)
151 (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
152 sizeof(net_destination_mgr));
153 }
154
155 dest = (net_destination_ptr) cinfo->dest;
156 dest->pub.init_destination = init_net_destination;
157 dest->pub.empty_output_buffer = empty_net_output_buffer;
158 dest->pub.term_destination = term_net_destination;
159}
160
162 WireImage rep;
163 FlexImage *img = rep.checkForImage(writer);
164
165 if (img == nullptr) {
166 return false;
167 }
168 int w = img->width();
169 int h = img->height();
170 int row_stride = img->getRowSize();
171 auto* data = (JOCTET*)img->getRawImage();
172
173 JSAMPROW row_pointer[1];
174
175 struct jpeg_compress_struct cinfo;
176 struct jpeg_error_mgr jerr;
177 cinfo.err = jpeg_std_error(&jerr);
178 cinfo.client_data = &proto;
179 jpeg_create_compress(&cinfo);
180 jpeg_net_dest(&cinfo);
181 cinfo.image_width = w;
182 cinfo.image_height = h;
183#ifndef LIBJPEG_TURBO_VERSION
184 if((img->getPixelCode() == VOCAB_PIXEL_RGBA) ||
185 (img->getPixelCode() == VOCAB_PIXEL_BGRA) ||
186 (img->getPixelCode() == VOCAB_PIXEL_BGR)) {
187 yCTrace(MJPEGCARRIER, "Pixel format not supported, please compile YARP with libjpeg-turbo to support it.");
188 return false;
189 }
190#endif
191 cinfo.in_color_space = yarpCode2Mjpeg.at(img->getPixelCode());
192 cinfo.input_components = yarpCode2Channels.at(img->getPixelCode());
193 jpeg_set_defaults(&cinfo);
194 //jpeg_set_quality(&cinfo, 85, TRUE);
195 yCTrace(MJPEGCARRIER, "Starting to compress...");
196 jpeg_start_compress(&cinfo, TRUE);
197 if(!envelope.empty()) {
198 jpeg_write_marker(&cinfo, JPEG_COM, reinterpret_cast<const JOCTET*>(envelope.c_str()), envelope.length() + 1);
199 envelope.clear();
200 }
201 yCTrace(MJPEGCARRIER, "Done compressing (height %d)", cinfo.image_height);
202 while (cinfo.next_scanline < cinfo.image_height) {
203 yCTrace(MJPEGCARRIER, "Writing row %d...", cinfo.next_scanline);
204 row_pointer[0] = data + cinfo.next_scanline * row_stride;
205 jpeg_write_scanlines(&cinfo, row_pointer, 1);
206 }
207 jpeg_finish_compress(&cinfo);
208 jpeg_destroy_compress(&cinfo);
209
210 return true;
211}
212
214 return false;
215}
216
217
219 Name n(proto.getRoute().getCarrierName() + "://test");
220 std::string pathValue = n.getCarrierModifier("path");
221 std::string target = "GET /?action=stream\n\n";
222 if (pathValue!="") {
223 target = "GET /";
224 target += pathValue;
225 }
226 target += " HTTP/1.1\n";
227 Contact host = proto.getRoute().getToContact();
228 if (host.getHost()!="") {
229 target += "Host: ";
230 target += host.getHost();
231 target += "\r\n";
232 }
233 target += "\n";
234 Bytes b((char*)target.c_str(),target.length());
235 proto.os().write(b);
236 return true;
237}
238
240#ifdef MJPEG_AUTOCOMPRESS
241 return true;
242#else
243 return false;
244#endif
245}
static const std::map< int, int > yarpCode2Channels
static boolean empty_net_output_buffer(j_compress_ptr cinfo)
static const std::map< int, J_COLOR_SPACE > yarpCode2Mjpeg
void jpeg_net_dest(j_compress_ptr cinfo)
net_destination_mgr * net_destination_ptr
static void init_net_destination(j_compress_ptr cinfo)
void send_net_data(JOCTET *data, int len, void *client)
static void term_net_destination(j_compress_ptr cinfo)
const yarp::os::LogComponent & MJPEGCARRIER()
virtual bool autoCompression() const
bool reply(yarp::os::ConnectionState &proto, yarp::os::SizedWriter &writer) override
bool write(yarp::os::ConnectionState &proto, yarp::os::SizedWriter &writer) override
Write a message.
bool sendHeader(yarp::os::ConnectionState &proto) override
Write a header appropriate to the carrier to the connection, followed by any carrier-specific data.
A simple abstraction for a block of bytes.
Definition: Bytes.h:24
The basic state of a connection - route, streams in use, etc.
OutputStream & os()
Shorthand for getOutputStream()
virtual const Route & getRoute() const =0
Get the route associated with this connection.
Represents how to reach a part of a YARP network.
Definition: Contact.h:33
std::string getHost() const
Get the host name associated with this Contact for socket communication.
Definition: Contact.cpp:228
Simple abstraction for a YARP port name.
Definition: Name.h:18
std::string getCarrierModifier(const char *mod, bool *hasModifier=nullptr)
Definition: Name.cpp:44
virtual void write(char ch)
Write a single byte to the stream.
const std::string & getCarrierName() const
Get the carrier type of the route.
Definition: Route.cpp:123
const Contact & getToContact() const
Get the destination contact of the route, if available.
Definition: Route.cpp:113
Minimal requirements for an efficient Writer.
Definition: SizedWriter.h:32
Image class with user control of representation details.
Definition: Image.h:411
size_t width() const
Gets width of image in pixels.
Definition: Image.h:163
size_t getRowSize() const
Size of the underlying image buffer rows.
Definition: Image.h:189
unsigned char * getRawImage() const
Access to the internal image buffer.
Definition: Image.cpp:542
size_t height() const
Gets height of image in pixels.
Definition: Image.h:169
virtual int getPixelCode() const
Gets pixel type identifier.
Definition: Image.cpp:441
yarp::sig::FlexImage * checkForImage(yarp::os::SizedWriter &writer)
Definition: WireImage.cpp:15
#define yCTrace(component,...)
Definition: LogComponent.h:84
#define yCWarning(component,...)
Definition: LogComponent.h:192
@ VOCAB_PIXEL_RGBA
Definition: Image.h:45
@ VOCAB_PIXEL_MONO16
Definition: Image.h:43
@ VOCAB_PIXEL_BGRA
Definition: Image.h:46
@ VOCAB_PIXEL_BGR
Definition: Image.h:49
@ VOCAB_PIXEL_MONO
Definition: Image.h:42
@ VOCAB_PIXEL_RGB
Definition: Image.h:44
An interface to the operating system, including Port based communication.
struct jpeg_destination_mgr pub
JOCTET cache[1000000]