Merge "Fix typo"
[smart_dashboard.git] / smartdashboard / src / edu / wpi / first / smartdashboard / gui / elements / VideoStreamViewerExtension.java
1 package edu.wpi.first.smartdashboard.gui.elements;
2
3 import edu.wpi.first.smartdashboard.gui.DashboardPrefs;
4 import edu.wpi.first.smartdashboard.gui.StaticWidget;
5 import edu.wpi.first.smartdashboard.properties.IntegerProperty;
6 import edu.wpi.first.smartdashboard.properties.Property;
7 import edu.wpi.first.smartdashboard.properties.StringProperty;
8
9 import java.awt.Color;
10 import java.awt.Dimension;
11 import java.awt.Graphics;
12 import java.awt.Graphics2D;
13 import java.awt.geom.AffineTransform;
14 import java.awt.image.BufferedImage;
15 import java.io.ByteArrayInputStream;
16 import java.io.ByteArrayOutputStream;
17 import java.io.InputStream;
18 import java.net.URL;
19 import java.net.URLConnection;
20
21 import javax.imageio.ImageIO;
22
23 /**
24  *
25  * @author Greg Granito
26  */
27 public class VideoStreamViewerExtension extends StaticWidget {
28
29     public static final String NAME = "Simple Camera Viewer";
30
31
32     private static final int[] START_BYTES = new int[]{0xFF, 0xD8};
33     private static final int[] END_BYTES = new int[]{0xFF, 0xD9};
34
35     private boolean ipChanged = true;
36     private String ipString = null;
37     private double rotateAngleRad = 0;
38     private long lastFPSCheck = 0;
39     private int lastFPS = 0;
40     private int fpsCounter = 0;
41     public class BGThread extends Thread {
42
43         boolean destroyed = false;
44
45         public BGThread() {
46             super("Camera Viewer Background");
47         }
48
49         long lastRepaint = 0;
50         @Override
51         public void run() {
52             URLConnection connection = null;
53             InputStream stream = null;
54             ByteArrayOutputStream imageBuffer = new ByteArrayOutputStream();
55             while (!destroyed) {
56                 try{
57                     System.out.println("Connecting to camera");
58                     ipChanged = false;
59                     URL url = new URL("http://"+ipString+"/mjpg/video.mjpg");
60                     connection = url.openConnection();
61                     connection.setReadTimeout(250);
62                     stream = connection.getInputStream();
63
64                     while(!destroyed && !ipChanged){
65                         while(System.currentTimeMillis()-lastRepaint<10){
66                             stream.skip(stream.available());
67                             Thread.sleep(1);
68                         }
69                         stream.skip(stream.available());
70
71                         imageBuffer.reset();
72                         for(int i = 0; i<START_BYTES.length;){
73                             int b = stream.read();
74                             if(b==START_BYTES[i])
75                                 i++;
76                             else
77                                 i = 0;
78                         }
79                         for(int i = 0; i<START_BYTES.length;++i)
80                             imageBuffer.write(START_BYTES[i]);
81
82                         for(int i = 0; i<END_BYTES.length;){
83                             int b = stream.read();
84                             imageBuffer.write(b);
85                             if(b==END_BYTES[i])
86                                 i++;
87                             else
88                                 i = 0;
89                         }
90
91                         fpsCounter++;
92                         if(System.currentTimeMillis()-lastFPSCheck>500){
93                             lastFPSCheck = System.currentTimeMillis();
94                             lastFPS = fpsCounter*2;
95                             fpsCounter = 0;
96                         }
97
98                         lastRepaint = System.currentTimeMillis();
99                         ByteArrayInputStream tmpStream = new ByteArrayInputStream(imageBuffer.toByteArray());
100                         imageToDraw = ImageIO.read(tmpStream);
101                         System.out.println(System.currentTimeMillis()-lastRepaint);
102                         repaint();
103                     }
104
105                 } catch(Exception e){
106                     imageToDraw = null;
107                     repaint();
108                     e.printStackTrace();
109                 }
110
111                 if(!ipChanged){
112                     try {
113                         Thread.sleep(500);
114                     } catch (InterruptedException ex) {}
115                 }
116             }
117
118         }
119
120         @Override
121         public void destroy() {
122             destroyed = true;
123         }
124     }
125     private BufferedImage imageToDraw;
126     private BGThread bgThread = new BGThread();
127     public final StringProperty ipProperty = new StringProperty(this, "Camera IP Address or mDNS name", "axis-camera.local");
128     public final IntegerProperty rotateProperty = new IntegerProperty(this, "Degrees Rotation", 0);
129     
130     @Override
131     public void init() {
132         setPreferredSize(new Dimension(100, 100));
133         ipString = ipProperty.getSaveValue();
134         rotateAngleRad = Math.toRadians(rotateProperty.getValue());
135         bgThread.start();
136         revalidate();
137         repaint();
138     }
139
140     @Override
141     public void propertyChanged(Property property) {
142         if (property == ipProperty) {
143             ipString = ipProperty.getSaveValue();
144             ipChanged = true;
145         }
146         if (property == rotateProperty) {
147             rotateAngleRad = Math.toRadians(rotateProperty.getValue());
148         }
149
150     }
151
152     @Override
153     public void disconnect() {
154         bgThread.destroy();
155         super.disconnect();
156     }
157
158     @Override
159     protected void paintComponent(Graphics g) {
160         BufferedImage drawnImage = imageToDraw; 
161         
162         if (drawnImage != null) {
163                 // cast the Graphics context into a Graphics2D
164             Graphics2D g2d = (Graphics2D)g;
165             
166             // get the existing Graphics transform and copy it so that we can perform scaling and rotation
167             AffineTransform origXform = g2d.getTransform();
168             AffineTransform newXform = (AffineTransform)(origXform.clone());
169             
170             // find the center of the original image
171             int origImageWidth = drawnImage.getWidth();
172             int origImageHeight = drawnImage.getHeight();
173             int imageCenterX = origImageWidth/2;
174             int imageCenterY = origImageHeight/2;
175             
176             // perform the desired scaling
177             double panelWidth = getBounds().width;
178             double panelHeight = getBounds().height;
179             double panelCenterX = panelWidth/2.0;
180             double panelCenterY = panelHeight/2.0;
181             double rotatedImageWidth = origImageWidth * Math.abs(Math.cos(rotateAngleRad)) + origImageHeight * Math.abs(Math.sin(rotateAngleRad));
182             double rotatedImageHeight = origImageWidth * Math.abs(Math.sin(rotateAngleRad)) + origImageHeight * Math.abs(Math.cos(rotateAngleRad));         
183                         
184             // compute scaling needed
185             double scale = Math.min(panelWidth / rotatedImageWidth, panelHeight / rotatedImageHeight);
186                       
187             // set the transform before drawing the image
188             // 1 - translate the origin to the center of the panel
189             // 2 - perform the desired rotation (rotation will be about origin)
190             // 3 - perform the desired scaling (will scale centered about origin)
191             newXform.translate(panelCenterX,  panelCenterY);
192             newXform.rotate(rotateAngleRad);
193             newXform.scale(scale, scale);
194             g2d.setTransform(newXform);
195
196             // draw image so that the center of the image is at the "origin"; the transform will take care of the rotation and scaling
197             g2d.drawImage(drawnImage, -imageCenterX, -imageCenterY, null);
198             
199             // restore the original transform
200             g2d.setTransform(origXform);
201             
202             g.setColor(Color.PINK);
203             g.drawString("FPS: "+lastFPS, 10, 10);
204         } else {
205             g.setColor(Color.PINK);
206             g.fillRect(0, 0, getBounds().width, getBounds().height);
207             g.setColor(Color.BLACK);
208             g.drawString("NO CONNECTION", 10, 10);
209         }
210     }
211 }