Skip to content
This repository was archived by the owner on Apr 8, 2024. It is now read-only.

Commit 54fd7f0

Browse files
authored
Merge pull request #80 from wml-frc/image_stream
MJPEG Image stream
2 parents 266935c + fc75580 commit 54fd7f0

File tree

12 files changed

+525
-17
lines changed

12 files changed

+525
-17
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2017 J. Pery
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
#include "MJPEGWriter.h"
2+
#include <fstream>
3+
void
4+
MJPEGWriter::Listener()
5+
{
6+
7+
// send http header
8+
std::string header;
9+
header += "HTTP/1.0 200 OK\r\n";
10+
header += "Cache-Control: no-cache\r\n";
11+
header += "Pragma: no-cache\r\n";
12+
header += "Connection: close\r\n";
13+
header += "Content-Type: multipart/x-mixed-replace; boundary=mjpegstream\r\n\r\n";
14+
const int header_size = header.size();
15+
char* header_data = (char*)header.data();
16+
fd_set rread;
17+
SOCKET maxfd;
18+
this->open();
19+
pthread_mutex_unlock(&mutex_writer);
20+
while (true)
21+
{
22+
rread = master;
23+
struct timeval to = { 0, timeout };
24+
maxfd = sock + 1;
25+
if (sock == INVALID_SOCKET){
26+
return;
27+
}
28+
int sel = select(maxfd, &rread, NULL, NULL, &to);
29+
if (sel > 0) {
30+
for (int s = 0; s < maxfd; s++)
31+
{
32+
if (FD_ISSET(s, &rread) && s == sock)
33+
{
34+
int addrlen = sizeof(SOCKADDR);
35+
SOCKADDR_IN address = { 0 };
36+
SOCKET client = accept(sock, (SOCKADDR*)&address, (socklen_t*)&addrlen);
37+
if (client == SOCKET_ERROR)
38+
{
39+
CJ_CORE_PRINT_ERROR("Error. Couldn't accept connection on sock: " + std::to_string(sock));
40+
return;
41+
}
42+
maxfd = (maxfd>client ? maxfd : client);
43+
pthread_mutex_lock(&mutex_cout);
44+
CJ_CORE_PRINT_INFO("New Client" + std::to_string(client));
45+
char headers[4096] = "\0";
46+
int readBytes = _read(client, headers);
47+
CJ_CORE_PRINT_TRACE(headers);
48+
pthread_mutex_unlock(&mutex_cout);
49+
pthread_mutex_lock(&mutex_client);
50+
_write(client, header_data, header_size);
51+
clients.push_back(client);
52+
pthread_mutex_unlock(&mutex_client);
53+
}
54+
}
55+
}
56+
usleep(1000);
57+
}
58+
}
59+
60+
void
61+
MJPEGWriter::Writer()
62+
{
63+
pthread_mutex_lock(&mutex_writer);
64+
pthread_mutex_unlock(&mutex_writer);
65+
const int milis2wait = 16666;
66+
while (this->isOpened())
67+
{
68+
pthread_mutex_lock(&mutex_client);
69+
int num_connected_clients = clients.size();
70+
pthread_mutex_unlock(&mutex_client);
71+
if (!num_connected_clients) {
72+
usleep(milis2wait);
73+
continue;
74+
}
75+
pthread_t threads[NUM_CONNECTIONS];
76+
int count = 0;
77+
78+
std::vector<uchar> outbuf;
79+
std::vector<int> params;
80+
params.push_back(cv::IMWRITE_JPEG_QUALITY);
81+
params.push_back(quality);
82+
pthread_mutex_lock(&mutex_writer);
83+
imencode(".jpg", lastFrame, outbuf, params);
84+
pthread_mutex_unlock(&mutex_writer);
85+
int outlen = outbuf.size();
86+
87+
pthread_mutex_lock(&mutex_client);
88+
std::vector<int>::iterator begin = clients.begin();
89+
std::vector<int>::iterator end = clients.end();
90+
pthread_mutex_unlock(&mutex_client);
91+
std::vector<clientPayload*> payloads;
92+
for (std::vector<int>::iterator it = begin; it != end; ++it, ++count)
93+
{
94+
if (count > NUM_CONNECTIONS)
95+
break;
96+
struct clientPayload *cp = new clientPayload({ (MJPEGWriter*)this, { outbuf.data(), outlen, *it } });
97+
payloads.push_back(cp);
98+
pthread_create(&threads[count], NULL, &MJPEGWriter::clientWrite_Helper, cp);
99+
}
100+
for (; count > 0; count--)
101+
{
102+
pthread_join(threads[count-1], NULL);
103+
delete payloads.at(count-1);
104+
}
105+
usleep(milis2wait);
106+
}
107+
}
108+
109+
void
110+
MJPEGWriter::ClientWrite(clientFrame & cf)
111+
{
112+
std::stringstream head;
113+
head << "--mjpegstream\r\nContent-Type: image/jpeg\r\nContent-Length: " << cf.outlen << "\r\n\r\n";
114+
std::string string_head = head.str();
115+
pthread_mutex_lock(&mutex_client);
116+
_write(cf.client, (char*) string_head.c_str(), string_head.size());
117+
int n = _write(cf.client, (char*)(cf.outbuf), cf.outlen);
118+
if (n < cf.outlen)
119+
{
120+
std::vector<int>::iterator it;
121+
it = find (clients.begin(), clients.end(), cf.client);
122+
if (it != clients.end())
123+
{
124+
CJ_CORE_PRINT_ERROR("Kill Client Error: " + std::to_string(cf.client));
125+
clients.erase(std::remove(clients.begin(), clients.end(), cf.client));
126+
::shutdown(cf.client, 2);
127+
}
128+
}
129+
pthread_mutex_unlock(&mutex_client);
130+
pthread_exit(NULL);
131+
}
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
#ifndef MJPEG_WRITER_H
2+
#define MJPEG_WRITER_H
3+
4+
#define PORT unsigned short
5+
#define SOCKET int
6+
#define HOSTENT struct hostent
7+
#define SOCKADDR struct sockaddr
8+
#define SOCKADDR_IN struct sockaddr_in
9+
#define ADDRPOINTER unsigned int*
10+
#define INVALID_SOCKET -1
11+
#define SOCKET_ERROR -1
12+
#define TIMEOUT_M 200000
13+
#define NUM_CONNECTIONS 10
14+
15+
#include "common_headers.h"
16+
#include "UDP_TransferNT.h"
17+
#include <sys/signal.h>
18+
#include <pthread.h>
19+
20+
struct clientFrame {
21+
uchar* outbuf;
22+
int outlen;
23+
int client;
24+
};
25+
26+
struct clientPayload {
27+
void* context;
28+
clientFrame cf;
29+
};
30+
31+
class MJPEGWriter{
32+
SOCKET sock;
33+
fd_set master;
34+
int timeout;
35+
int quality; // jpeg compression [1..100]
36+
std::vector<int> clients;
37+
pthread_t thread_listen, thread_write;
38+
pthread_mutex_t mutex_client = PTHREAD_MUTEX_INITIALIZER;
39+
pthread_mutex_t mutex_cout = PTHREAD_MUTEX_INITIALIZER;
40+
pthread_mutex_t mutex_writer = PTHREAD_MUTEX_INITIALIZER;
41+
cv::Mat lastFrame;
42+
int port;
43+
44+
int _write(int sock, char *s, int len)
45+
{
46+
if (len < 1) { len = strlen(s); }
47+
{
48+
try
49+
{
50+
int retval = ::send(sock, s, len, 0x4000);
51+
return retval;
52+
}
53+
catch (int e)
54+
{
55+
CJ_CORE_PRINT_ERROR("An exception occurred. Exception Nr. " + std::to_string(e));
56+
}
57+
}
58+
return -1;
59+
}
60+
61+
int _read(int socket, char* buffer)
62+
{
63+
int result;
64+
result = recv(socket, buffer, 4096, MSG_PEEK);
65+
if (result < 0 )
66+
{
67+
CJ_CORE_PRINT_ERROR("An exception occurred. Exception Nr. " + std::to_string(result));
68+
return result;
69+
}
70+
std::string s = buffer;
71+
buffer = (char*) s.substr(0, (int) result).c_str();
72+
return result;
73+
}
74+
75+
static void* listen_Helper(void* context)
76+
{
77+
((MJPEGWriter *)context)->Listener();
78+
return NULL;
79+
}
80+
81+
static void* writer_Helper(void* context)
82+
{
83+
((MJPEGWriter *)context)->Writer();
84+
return NULL;
85+
}
86+
87+
static void* clientWrite_Helper(void* payload)
88+
{
89+
void* ctx = ((clientPayload *)payload)->context;
90+
struct clientFrame cf = ((clientPayload *)payload)->cf;
91+
((MJPEGWriter *)ctx)->ClientWrite(cf);
92+
return NULL;
93+
}
94+
95+
public:
96+
97+
MJPEGWriter(int port = 0)
98+
: sock(INVALID_SOCKET)
99+
, timeout(TIMEOUT_M)
100+
, quality(90)
101+
, port(port)
102+
{
103+
signal(SIGPIPE, SIG_IGN);
104+
FD_ZERO(&master);
105+
// if (port)
106+
// open(port);
107+
}
108+
109+
~MJPEGWriter()
110+
{
111+
release();
112+
}
113+
114+
bool release()
115+
{
116+
if (sock != INVALID_SOCKET)
117+
shutdown(sock, 2);
118+
sock = (INVALID_SOCKET);
119+
return false;
120+
}
121+
122+
bool open()
123+
{
124+
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
125+
126+
SOCKADDR_IN address;
127+
address.sin_addr.s_addr = INADDR_ANY;
128+
address.sin_family = AF_INET;
129+
address.sin_port = htons(port);
130+
if (::bind(sock, (SOCKADDR*)&address, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
131+
{
132+
CJ_CORE_PRINT_ERROR("Error, could not bind to socket: " + std::to_string(sock));
133+
return release();
134+
}
135+
if (listen(sock, NUM_CONNECTIONS) == SOCKET_ERROR)
136+
{
137+
CJ_CORE_PRINT_ERROR("Error, could not listen on sock: " + std::to_string(sock));
138+
return release();
139+
}
140+
FD_SET(sock, &master);
141+
return true;
142+
}
143+
144+
bool isOpened()
145+
{
146+
return sock != INVALID_SOCKET;
147+
}
148+
149+
void start(){
150+
pthread_mutex_lock(&mutex_writer);
151+
pthread_create(&thread_listen, NULL, this->listen_Helper, this);
152+
pthread_create(&thread_write, NULL, this->writer_Helper, this);
153+
}
154+
155+
void stop(){
156+
this->release();
157+
pthread_join(thread_listen, NULL);
158+
pthread_join(thread_write, NULL);
159+
}
160+
161+
void write(cv::Mat frame){
162+
pthread_mutex_lock(&mutex_writer);
163+
if(!frame.empty()){
164+
lastFrame.release();
165+
lastFrame = frame.clone();
166+
}
167+
pthread_mutex_unlock(&mutex_writer);
168+
}
169+
170+
private:
171+
void Listener();
172+
void Writer();
173+
void ClientWrite(clientFrame &cf);
174+
};
175+
176+
#endif

Core/common/libs/UDP_TransferNT

Core/common/main/include/Platform/PlatformDetection.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
#elif defined(__linux__)
88
#define CJ_PLATFORM_LINUX
99

10-
#elif defined (__APPLE__) || defined(__MACH__)
10+
#elif defined(__APPLE__) || defined(__MACH__)
1111
#include <TargetConditionals.h>
1212

1313
#if TARGET_IPHONE_SIMULATOR == 1

Core/common/main/include/common_headers.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#ifndef COMMON_HEADERS_H
22
#define COMMON_HEADERS_H
33

4+
#include "Platform/PlatformDetection.h"
5+
46
// System common headers
57
#include <iostream>
68
#include <string>

Core/src/latest/Makefile

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,17 +65,27 @@ UDP_TRANSFERNT_LIB_HEADERS = -I$(UDP_TRANSFERNT_LIB_LOC)/include
6565
SPDLOG_LIB_LOC = $(COMMON_LOC)/libs/spdlog
6666
SPDLOG_LIB_HEADERS = -I$(SPDLOG_LIB_LOC)/include
6767

68+
#
69+
# MJPEGWriter
70+
#
71+
MJPEG_WRITER_LOC = $(COMMON_LOC)/libs/MJPEGWriter
72+
MJPEG_WRITER_SRCS = $(call rwildcard,$(MJPEG_WRITER_LOC)/main/,*.cpp)
73+
MJPEG_WRITER_SRC_DIR = $(MJPEG_WRITER_LOC)/main/cpp
74+
75+
MJPEG_WRITER_HEADERS = -I$(MJPEG_WRITER_LOC)/main/include
76+
6877

6978
# Obejcts from cpp files
7079
ENTRY_OBJ := $(patsubst $(ENTRY_DIR)/%.cpp, $(OUTDIR)/%.o, $(ENTRY_SRC))
7180
COPROC_OBJ := $(patsubst $(COPROC_SRC_DIR)/%.cpp, $(OUTDIR)/%.o, $(COPROC_SRCS))
7281
CJ_VISION_OBJ := $(patsubst $(CJ_VISION_SRC_DIR)/%.cpp, $(OUTDIR)/%.o, $(CJ_VISION_SRCS))
7382
COMMON_OBJ := $(patsubst $(COMMON_SRC_DIR)/%.cpp, $(OUTDIR)/%.o, $(COMMON_SRCS))
83+
MJPEG_WRITER_OBJ := $(patsubst $(MJPEG_WRITER_SRC_DIR)/%.cpp, $(OUTDIR)/%.o, $(MJPEG_WRITER_SRCS))
7484

7585
# All
76-
ALL_HEADERS = $(CJ_VISION_HEADERS) $(COMMON_HEADERS) $(UDP_TRANSFERNT_LIB_HEADERS) $(SPDLOG_LIB_HEADERS) $(COPROC_HEADERS)
77-
ALL_OBJ = $(ENTRY_OBJ) $(CJ_VISION_OBJ) $(COMMON_OBJ) $(COPROC_OBJ)
78-
ALL_SRCS = $(ENTRY_SRC) $(CJ_VISION_SRCS) $(COMMON_SRCS) $(COPROC_SRCS)
86+
ALL_HEADERS = $(CJ_VISION_HEADERS) $(COMMON_HEADERS) $(UDP_TRANSFERNT_LIB_HEADERS) $(SPDLOG_LIB_HEADERS) $(COPROC_HEADERS) $(MJPEG_WRITER_HEADERS)
87+
ALL_OBJ = $(ENTRY_OBJ) $(CJ_VISION_OBJ) $(COMMON_OBJ) $(COPROC_OBJ) $(MJPEG_WRITER_OBJ)
88+
ALL_SRCS = $(ENTRY_SRC) $(CJ_VISION_SRCS) $(COMMON_SRCS) $(COPROC_SRCS) $(MJPEG_WRITER_SRCS)
7989

8090
# Compile script
8191
MK_OUT = mkdir -p $@; rm -rf $@
@@ -120,5 +130,11 @@ $(OUTDIR)/%.o: $(COMMON_SRC_DIR)/%.cpp
120130
$(MK_OUT)
121131
$(COMPILE_OBJ)
122132

133+
# MJPEG Writer
134+
$(OUTDIR)/%.o: $(MJPEG_WRITER_SRC_DIR)/%.cpp
135+
@echo Building objects
136+
$(MK_OUT)
137+
$(COMPILE_OBJ)
138+
123139
clean:
124140
rm -rf ${OUTDIR}

0 commit comments

Comments
 (0)