Skip to content

Commit 46d005f

Browse files
committed
Merge pull request #456 from JLLeitschuh/fix/ipCameraFrameGrabberHang
Fixes IP Cameras Hanging
2 parents 089ec41 + dbc156d commit 46d005f

File tree

2 files changed

+43
-28
lines changed

2 files changed

+43
-28
lines changed

core/src/main/java/edu/wpi/grip/core/sources/CameraSource.java

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.net.URL;
2222
import java.util.Optional;
2323
import java.util.Properties;
24+
import java.util.concurrent.TimeUnit;
2425
import java.util.concurrent.TimeoutException;
2526
import java.util.logging.Level;
2627
import java.util.logging.Logger;
@@ -36,7 +37,24 @@ public class CameraSource extends Source implements StartStoppable {
3637
* {@link CameraSource.Factory#create(String)}, allowing this to work with basically any network video stream, this
3738
* default path allows the Axis M1011 cameras used in FRC to work when only an IP address is supplied.
3839
*/
39-
public final static String DEFAULT_IP_CAMERA_PATH = "/mjpg/video.mjpg";
40+
public static final String DEFAULT_IP_CAMERA_PATH = "/mjpg/video.mjpg";
41+
private static final int
42+
/**
43+
* Connecting to a device can take the most time.
44+
* This should have a little bit of leeway.
45+
*
46+
* On a fairly decent computer with a great internet connection 7 seconds is more than enough.
47+
* This value has been doubled to ensure that people running computers that may be older
48+
* or have firewalls that will slow down connecting can still use the device.
49+
*/
50+
IP_CAMERA_CONNECTION_TIMEOUT = 14;
51+
private static final int
52+
/**
53+
* Reading from an existing connection shouldn't take that long.
54+
* If it does we should really give up and try to reconnect.
55+
*/
56+
IP_CAMERA_READ_TIMEOUT = 5;
57+
private static final TimeUnit IP_CAMERA_TIMEOUT_UNIT = TimeUnit.SECONDS;
4058

4159
private final static String DEVICE_NUMBER_PROPERTY = "deviceNumber";
4260
private final static String ADDRESS_PROPERTY = "address";
@@ -88,7 +106,11 @@ public FrameGrabber create(String addressProperty) throws MalformedURLException
88106
if (new URL(addressProperty).getPath().length() <= 1) {
89107
addressProperty += DEFAULT_IP_CAMERA_PATH;
90108
}
91-
return new IPCameraFrameGrabber(addressProperty);
109+
return new IPCameraFrameGrabber(
110+
addressProperty,
111+
IP_CAMERA_CONNECTION_TIMEOUT,
112+
IP_CAMERA_READ_TIMEOUT,
113+
IP_CAMERA_TIMEOUT_UNIT);
92114
}
93115
}
94116

core/src/main/java/edu/wpi/grip/core/sources/IPCameraFrameGrabber.java

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@
3333
import javax.imageio.ImageIO;
3434
import java.awt.image.BufferedImage;
3535
import java.io.ByteArrayInputStream;
36+
import java.io.DataInputStream;
37+
import java.io.EOFException;
3638
import java.io.IOException;
37-
import java.io.InputStream;
3839
import java.net.MalformedURLException;
3940
import java.net.URL;
4041
import java.net.URLConnection;
41-
import java.util.List;
42-
import java.util.Map;
42+
import java.util.concurrent.TimeUnit;
4343

4444
import static org.bytedeco.javacpp.opencv_core.*;
4545
import static org.bytedeco.javacpp.opencv_imgcodecs.cvDecodeImage;
@@ -69,37 +69,31 @@ public static void tryLoad() throws Exception {
6969
}
7070
}
7171

72-
private URL url;
72+
private final URL url;
73+
private final int connectionTimeout;
74+
private final int readTimeout;
7375

7476
private URLConnection connection;
75-
private InputStream input;
77+
private DataInputStream input;
7678
private byte[] pixelBuffer = new byte[1024];
77-
private Map<String, List<String>> headerfields;
78-
private String boundryKey;
7979
private IplImage decoded = null;
8080
private FrameConverter<IplImage> converter = new OpenCVFrameConverter.ToIplImage();
8181

82-
public IPCameraFrameGrabber(String urlstr) throws MalformedURLException {
82+
public IPCameraFrameGrabber(String urlstr, int connectionTimeout, int readTimeout, TimeUnit unit) throws MalformedURLException {
83+
super();
8384
url = new URL(urlstr);
85+
this.connectionTimeout = Math.toIntExact(TimeUnit.MILLISECONDS.convert(connectionTimeout, unit));
86+
this.readTimeout = Math.toIntExact(TimeUnit.MILLISECONDS.convert(readTimeout, unit));
8487
}
8588

8689
@Override
8790
public void start() throws Exception {
8891

8992
try {
9093
connection = url.openConnection();
91-
headerfields = connection.getHeaderFields();
92-
if (headerfields.containsKey("Content-Type")) {
93-
List<String> ct = headerfields.get("Content-Type");
94-
for (int i = 0; i < ct.size(); ++i) {
95-
String key = ct.get(i);
96-
int j = key.indexOf("boundary=");
97-
if (j != -1) {
98-
boundryKey = key.substring(j + 9); // FIXME << fragile
99-
}
100-
}
101-
}
102-
input = connection.getInputStream();
94+
connection.setConnectTimeout(connectionTimeout);
95+
connection.setReadTimeout(readTimeout);
96+
input = new DataInputStream(connection.getInputStream());
10397
} catch (IOException e) {
10498
// Make sure we rethrow the IO exception https://github.com/bytedeco/javacv/pull/300
10599
throw new Exception(e.getMessage(), e);
@@ -169,9 +163,9 @@ byte[] readImage() throws IOException {
169163
}
170164
}
171165
// find embedded jpeg in stream
172-
String subheader = sb.toString();
166+
final String subheader = sb.toString();
173167
//log.debug(subheader);
174-
int contentLength = -1;
168+
175169
// if (boundryKey == null)
176170
// {
177171
// Yay! - server was nice and sent content length
@@ -180,18 +174,17 @@ byte[] readImage() throws IOException {
180174

181175
if (c0 < 0) {
182176
//log.info("no content length returning null");
183-
return null;
177+
throw new EOFException("The camera stream ended unexpectedly");
184178
}
185179

186180
c0 += 16;
187-
contentLength = Integer.parseInt(subheader.substring(c0, c1).trim());
181+
final int contentLength = Integer.parseInt(subheader.substring(c0, c1).trim());
188182
//log.debug("Content-Length: " + contentLength);
189183

190184
// adaptive size - careful - don't want a 2G jpeg
191185
ensureBufferCapacity(contentLength);
192186

193-
while (input.available() < contentLength) ;
194-
input.read(pixelBuffer, 0, contentLength);
187+
input.readFully(pixelBuffer, 0, contentLength);
195188
input.read();// \r
196189
input.read();// \n
197190
input.read();// \r

0 commit comments

Comments
 (0)