Merge branch 'robot_control' into develop

This commit is contained in:
2014-02-05 15:07:38 -04:30
5 changed files with 513 additions and 130 deletions

View File

@@ -0,0 +1,37 @@
package ve.ucv.ciens.ccg.networkdata;
import java.io.Serializable;
public class MotorEvent implements Serializable{
private static final long serialVersionUID = 9989L;
public enum motor_t {NONE, MOTOR_A, MOTOR_B, MOTOR_C, MOTOR_AC};
private motor_t motor;
private byte power;
public MotorEvent(){
motor = motor_t.NONE;
power = 0;
}
public void setMotor(motor_t motor){
this.motor = motor;
}
public void setPower(byte power) throws IllegalArgumentException{
if(power > 100 || power < -100){
throw new IllegalArgumentException("Motor power must be a number between -100 and 100");
}else{
this.power = power;
}
}
public motor_t getMotor(){
return this.motor;
}
public byte getPower(){
return this.power;
}
}

View File

@@ -28,7 +28,12 @@ import ve.ucv.ciens.ccg.nxtar.utils.Size;
import com.badlogic.gdx.Application; import com.badlogic.gdx.Application;
import com.badlogic.gdx.ApplicationListener; import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.FPSLogger; import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.controllers.Controller;
import com.badlogic.gdx.controllers.ControllerListener;
import com.badlogic.gdx.controllers.Controllers;
import com.badlogic.gdx.controllers.PovDirection;
import com.badlogic.gdx.controllers.mappings.Ouya;
import com.badlogic.gdx.graphics.GL10; import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.graphics.Pixmap;
@@ -38,23 +43,37 @@ import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.Sprite; import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
public class NxtARCore implements ApplicationListener, NetworkConnectionListener { public class NxtARCore implements ApplicationListener, NetworkConnectionListener, InputProcessor, ControllerListener{
private static final String TAG = "NXTAR_CORE_MAIN"; private static final String TAG = "NXTAR_CORE_MAIN";
private static final String CLASS_NAME = NxtARCore.class.getSimpleName(); private static final String CLASS_NAME = NxtARCore.class.getSimpleName();
private OrthographicCamera camera; private OrthographicCamera camera;
private OrthographicCamera pixelPerfectCamera;
private SpriteBatch batch; private SpriteBatch batch;
private Texture texture; private Texture texture;
private Texture buttonTexture;
private Sprite sprite; private Sprite sprite;
private Sprite motorA;
private Sprite motorB;
private Sprite motorC;
private Sprite motorD;
private Toaster toaster; private Toaster toaster;
private MulticastEnabler mcastEnabler; private MulticastEnabler mcastEnabler;
private FPSLogger fps;
private BitmapFont font; private BitmapFont font;
private int connections; private int connections;
private float fontX;
private float fontY;
private Pixmap image;
private Vector3 win2world;
private Vector2 touchPointWorldCoords;
private boolean[] motorButtonsTouched;
private int[] motorButtonsPointers;
private VideoFrameMonitor frameMonitor; private VideoFrameMonitor frameMonitor;
private ServiceDiscoveryThread udpThread; private ServiceDiscoveryThread serviceDiscoveryThread;
private VideoStreamingThread videoThread; private VideoStreamingThread videoThread;
private RobotControlThread robotThread; private RobotControlThread robotThread;
@@ -66,41 +85,59 @@ public class NxtARCore implements ApplicationListener, NetworkConnectionListener
this.mcastEnabler = (MulticastEnabler)concreteApp; this.mcastEnabler = (MulticastEnabler)concreteApp;
}catch(ClassCastException cc){ }catch(ClassCastException cc){
Gdx.app.debug(TAG, CLASS_NAME + ".Main() :: concreteApp does not implement any of the required interfaces."); Gdx.app.debug(TAG, CLASS_NAME + ".Main() :: concreteApp does not implement any of the required interfaces.");
System.exit(ProjectConstants.EXIT_FAILURE); Gdx.app.exit();
} }
} }
@Override @Override
public void create(){ public void create(){
float w = Gdx.graphics.getWidth(); image = null;
float h = Gdx.graphics.getHeight(); win2world = new Vector3(0.0f, 0.0f, 0.0f);
touchPointWorldCoords = new Vector2();
motorButtonsTouched = new boolean[4];
motorButtonsTouched[0] = false;
motorButtonsTouched[1] = false;
motorButtonsTouched[2] = false;
motorButtonsTouched[3] = false;
motorButtonsPointers = new int[4];
motorButtonsPointers[0] = -1;
motorButtonsPointers[1] = -1;
motorButtonsPointers[2] = -1;
motorButtonsPointers[3] = -1;
Gdx.input.setInputProcessor(this);
Controllers.addListener(this);
fps = new FPSLogger();
font = new BitmapFont(); font = new BitmapFont();
Gdx.app.setLogLevel(Application.LOG_DEBUG); font.setColor(1.0f, 1.0f, 0.0f, 1.0f);
if(!Ouya.runningOnOuya){
font.setScale(1.0f);
}else{
font.setScale(2.5f);
}
camera = new OrthographicCamera(1, h/w); //Gdx.app.setLogLevel(Application.LOG_INFO);
Gdx.app.setLogLevel(Application.LOG_NONE);
pixelPerfectCamera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
camera = new OrthographicCamera(1.0f, Gdx.graphics.getHeight() / Gdx.graphics.getWidth());
batch = new SpriteBatch(); batch = new SpriteBatch();
texture = new Texture(Gdx.files.internal("data/libgdx.png")); fontX = -((Gdx.graphics.getWidth() * ProjectConstants.OVERSCAN) / 2) + 10;
texture.setFilter(TextureFilter.Linear, TextureFilter.Linear); fontY = ((Gdx.graphics.getHeight() * ProjectConstants.OVERSCAN) / 2) - 10;
if(!Ouya.runningOnOuya) setUpButtons();
TextureRegion region = new TextureRegion(texture, 0, 0, 512, 275);
sprite = new Sprite(region);
sprite.setSize(0.9f, 0.9f * sprite.getHeight() / sprite.getWidth());
sprite.setOrigin(sprite.getWidth()/2, sprite.getHeight()/2);
sprite.setPosition(-sprite.getWidth()/2, -sprite.getHeight()/2);
Gdx.app.debug(TAG, CLASS_NAME + ".create() :: Creating network threads"); Gdx.app.debug(TAG, CLASS_NAME + ".create() :: Creating network threads");
frameMonitor = VideoFrameMonitor.getInstance(); frameMonitor = VideoFrameMonitor.getInstance();
mcastEnabler.enableMulticast(); serviceDiscoveryThread = ServiceDiscoveryThread.getInstance();
udpThread = ServiceDiscoveryThread.getInstance(); videoThread = VideoStreamingThread.getInstance()/*.setToaster(toaster)*/;
videoThread = VideoStreamingThread.getInstance().setToaster(toaster);
//robotThread = RobotControlThread.getInstance().setToaster(toaster); //robotThread = RobotControlThread.getInstance().setToaster(toaster);
udpThread.start(); mcastEnabler.enableMulticast();
serviceDiscoveryThread.start();
videoThread.start(); videoThread.start();
videoThread.startStreaming(); videoThread.startStreaming();
//robotThread.start(); //robotThread.start();
@@ -109,56 +146,83 @@ public class NxtARCore implements ApplicationListener, NetworkConnectionListener
@Override @Override
public void dispose() { public void dispose() {
batch.dispose(); batch.dispose();
texture.dispose(); if(texture != null)
texture.dispose();
if(buttonTexture != null)
buttonTexture.dispose();
font.dispose(); font.dispose();
image.dispose();
videoThread.finish();
} }
@Override @Override
public void render(){ public void render(){
Pixmap image;
Pixmap temp; Pixmap temp;
byte[] frame; byte[] frame;
Size dimensions; Size dimensions = null;
Gdx.gl.glClearColor(1, 1, 1, 1); Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT); Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
frame = frameMonitor.getCurrentFrame(); frame = frameMonitor.getCurrentFrame();
if(frame != null){ if(frame != null){
texture.dispose();
dimensions = frameMonitor.getFrameDimensions(); dimensions = frameMonitor.getFrameDimensions();
temp = new Pixmap(frame, 0, dimensions.getWidth() * dimensions.getHeight()); temp = new Pixmap(frame, 0, dimensions.getWidth() * dimensions.getHeight());
image = new Pixmap(1024, 512, temp.getFormat()); if(image == null){
try{
image = new Pixmap(getOptimalTextureSize(dimensions.getWidth()), getOptimalTextureSize(dimensions.getHeight()), temp.getFormat());
}catch(ImageTooBigException e){
toaster.showLongToast("Cannot display received frame.\n" + e.getMessage());
Gdx.app.exit();
return;
}
}
image.drawPixmap(temp, 0, 0); image.drawPixmap(temp, 0, 0);
temp.dispose();
texture = new Texture(image); texture = new Texture(image);
texture.setFilter(TextureFilter.Linear, TextureFilter.Linear); texture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
TextureRegion region = new TextureRegion(texture, 0, 0, dimensions.getWidth(), dimensions.getHeight()); TextureRegion region = new TextureRegion(texture, 0, 0, dimensions.getWidth(), dimensions.getHeight());
sprite = new Sprite(region); sprite = new Sprite(region);
sprite.setSize(0.9f, 0.9f * sprite.getWidth() / sprite.getHeight()); sprite.setOrigin(sprite.getWidth() / 2, sprite.getHeight() / 2);
sprite.setOrigin(sprite.getWidth()/2, sprite.getHeight()/2); if(!Ouya.runningOnOuya){
sprite.setPosition(-sprite.getWidth()/2, -sprite.getHeight()/2); sprite.setSize(1.0f, sprite.getHeight() / sprite.getWidth() );
sprite.rotate90(true); sprite.rotate90(true);
sprite.translate(-sprite.getWidth() / 2, 0.5f - sprite.getHeight());
}else{
float xSize = Gdx.graphics.getHeight() * (dimensions.getWidth() / dimensions.getHeight());
sprite.setSize(xSize * ProjectConstants.OVERSCAN, Gdx.graphics.getHeight() * ProjectConstants.OVERSCAN);
sprite.rotate90(true);
sprite.translate(-sprite.getWidth() / 2, -sprite.getHeight() / 2);
}
batch.setProjectionMatrix(camera.combined); if(!Ouya.runningOnOuya){
batch.setProjectionMatrix(camera.combined);
}else{
batch.setProjectionMatrix(pixelPerfectCamera.combined);
}
batch.begin();{ batch.begin();{
sprite.draw(batch); sprite.draw(batch);
font.setColor(0.0f, 0.0f, 0.0f, 1.0f);
font.setScale(100.0f);
font.draw(batch, String.format("Render FPS: %d", Gdx.graphics.getFramesPerSecond()), 10, 300);
}batch.end(); }batch.end();
Gdx.app.log("Main", String.format("Network FPS: %d", videoThread.getFps()));
Gdx.app.log("Main", String.format("Render FPS: %d", Gdx.graphics.getFramesPerSecond()));
texture.dispose(); texture.dispose();
temp.dispose();
image.dispose();
//fps.log();
} }
batch.setProjectionMatrix(pixelPerfectCamera.combined);
batch.begin();{
if(!Ouya.runningOnOuya){
motorA.draw(batch);
motorB.draw(batch);
motorC.draw(batch);
motorD.draw(batch);
}
font.draw(batch, String.format("Render FPS: %d", Gdx.graphics.getFramesPerSecond()), fontX, fontY);
font.draw(batch, String.format("Network FPS: %d", videoThread.getFps()), fontX, fontY - font.getCapHeight() - 5);
font.draw(batch, String.format("Lost Network FPS: %d", videoThread.getLostFrames()), fontX, fontY - (2 * font.getCapHeight()) - 10);
if(dimensions != null)
font.draw(batch, String.format("Frame size: (%d, %d)", dimensions.getWidth(), dimensions.getHeight()), fontX, fontY - (3 * font.getCapHeight()) - 15);
}batch.end();
} }
@Override @Override
@@ -167,10 +231,14 @@ public class NxtARCore implements ApplicationListener, NetworkConnectionListener
@Override @Override
public void pause(){ public void pause(){
if(videoThread != null)
videoThread.pause();
} }
@Override @Override
public void resume(){ public void resume(){
if(videoThread != null)
videoThread.play();
} }
@Override @Override
@@ -179,8 +247,242 @@ public class NxtARCore implements ApplicationListener, NetworkConnectionListener
connections += 1; connections += 1;
if(connections >= 2){ if(connections >= 2){
Gdx.app.debug(TAG, CLASS_NAME + ".networkStreamConnected() :: Stopping service broadcast."); Gdx.app.debug(TAG, CLASS_NAME + ".networkStreamConnected() :: Stopping service broadcast.");
udpThread.finish(); serviceDiscoveryThread.finish();
mcastEnabler.disableMulticast(); mcastEnabler.disableMulticast();
} }
} }
private int getOptimalTextureSize(int imageSideLength) throws ImageTooBigException{
for(int po2: ProjectConstants.POWERS_OF_2){
if(imageSideLength < po2) return po2;
}
throw new ImageTooBigException("No valid texture size found. Image too large.");
}
private void setUpButtons(){
buttonTexture = new Texture(Gdx.files.internal("data/gfx/gui/PBCrichton_Flat_Button.png"));
buttonTexture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
TextureRegion region = new TextureRegion(buttonTexture, 0, 0, buttonTexture.getWidth(), buttonTexture.getHeight());
motorA = new Sprite(region);
motorA.setSize(motorA.getWidth() * 0.7f, motorA.getHeight() * 0.7f);
motorB = new Sprite(region);
motorB.setSize(motorB.getWidth() * 0.7f, motorB.getHeight() * 0.7f);
motorC = new Sprite(region);
motorC.setSize(motorC.getWidth() * 0.7f, motorC.getHeight() * 0.7f);
motorD = new Sprite(region);
motorD.setSize(motorD.getWidth() * 0.7f, motorD.getHeight() * 0.7f);
motorA.setPosition(-(Gdx.graphics.getWidth() / 2) + 10, -(Gdx.graphics.getHeight() / 2) + motorB.getHeight() + 20);
motorB.setPosition(-(Gdx.graphics.getWidth() / 2) + 20 + (motorA.getWidth() / 2), -(Gdx.graphics.getHeight() / 2) + 10);
motorC.setPosition((Gdx.graphics.getWidth() / 2) - (1.5f * (motorD.getWidth())) - 20, -(Gdx.graphics.getHeight() / 2) + 10);
motorD.setPosition((Gdx.graphics.getWidth() / 2) - motorD.getWidth() - 10, -(Gdx.graphics.getHeight() / 2) + 20 + motorC.getHeight());
}
private class ImageTooBigException extends Exception{
private static final long serialVersionUID = 9989L;
public ImageTooBigException(String msg){
super(msg);
}
}
@Override
public boolean keyDown(int keycode) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean keyUp(int keycode) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean keyTyped(char character) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
if(!Ouya.runningOnOuya){
win2world.set(screenX, screenY, 0.0f);
camera.unproject(win2world);
touchPointWorldCoords.set(win2world.x * Gdx.graphics.getWidth(), win2world.y * Gdx.graphics.getHeight());
Gdx.app.log(TAG, CLASS_NAME + String.format(".touchDown(%d, %d, %d, %d)", screenX, screenY, pointer, button));
Gdx.app.log(TAG, CLASS_NAME + String.format(".touchDown() :: Unprojected touch point: (%f, %f)", touchPointWorldCoords.x, touchPointWorldCoords.y));
if(motorA.getBoundingRectangle().contains(touchPointWorldCoords)){
Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Motor A button pressed");
motorButtonsTouched[0] = true;
motorButtonsPointers[0] = pointer;
}else if(motorB.getBoundingRectangle().contains(touchPointWorldCoords)){
Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Motor B button pressed");
motorButtonsTouched[1] = true;
motorButtonsPointers[1] = pointer;
}else if(motorC.getBoundingRectangle().contains(touchPointWorldCoords)){
Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Motor C button pressed");
motorButtonsTouched[2] = true;
motorButtonsPointers[2] = pointer;
}else if(motorD.getBoundingRectangle().contains(touchPointWorldCoords)){
Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Motor D button pressed");
motorButtonsTouched[3] = true;
motorButtonsPointers[3] = pointer;
}
}
return true;
}
@Override
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
if(!Ouya.runningOnOuya){
win2world.set(screenX, screenY, 0.0f);
camera.unproject(win2world);
touchPointWorldCoords.set(win2world.x * Gdx.graphics.getWidth(), win2world.y * Gdx.graphics.getHeight());
Gdx.app.log(TAG, CLASS_NAME + String.format(".touchUp(%d, %d, %d, %d)", screenX, screenY, pointer, button));
Gdx.app.log(TAG, CLASS_NAME + String.format(".touchUp() :: Unprojected touch point: (%f, %f)", touchPointWorldCoords.x, touchPointWorldCoords.y));
if(motorA.getBoundingRectangle().contains(touchPointWorldCoords)){
Gdx.app.log(TAG, CLASS_NAME + ".touchUp() :: Motor A button released");
motorButtonsPointers[0] = -1;
motorButtonsTouched[0] = false;
}else if(motorB.getBoundingRectangle().contains(touchPointWorldCoords)){
Gdx.app.log(TAG, CLASS_NAME + ".touchUp() :: Motor B button released");
motorButtonsPointers[1] = -1;
motorButtonsTouched[1] = false;
}else if(motorC.getBoundingRectangle().contains(touchPointWorldCoords)){
Gdx.app.log(TAG, CLASS_NAME + ".touchUp() :: Motor C button released");
motorButtonsPointers[2] = -1;
motorButtonsTouched[2] = false;
}else if(motorD.getBoundingRectangle().contains(touchPointWorldCoords)){
Gdx.app.log(TAG, CLASS_NAME + ".touchUp() :: Motor D button released");
motorButtonsPointers[3] = -1;
motorButtonsTouched[3] = false;
}
}
return true;
}
@Override
public boolean touchDragged(int screenX, int screenY, int pointer) {
if(!Ouya.runningOnOuya){
win2world.set(screenX, screenY, 0.0f);
camera.unproject(win2world);
touchPointWorldCoords.set(win2world.x * Gdx.graphics.getWidth(), win2world.y * Gdx.graphics.getHeight());
if(pointer == motorButtonsPointers[0] && !motorA.getBoundingRectangle().contains(touchPointWorldCoords)){
Gdx.app.log(TAG, CLASS_NAME + ".touchDragged() :: Motor A button released");
motorButtonsPointers[0] = -1;
motorButtonsTouched[0] = false;
}else if(pointer == motorButtonsPointers[1] && !motorB.getBoundingRectangle().contains(touchPointWorldCoords)){
Gdx.app.log(TAG, CLASS_NAME + ".touchDragged() :: Motor B button released");
motorButtonsPointers[1] = -1;
motorButtonsTouched[1] = false;
}else if(pointer == motorButtonsPointers[2] && !motorC.getBoundingRectangle().contains(touchPointWorldCoords)){
Gdx.app.log(TAG, CLASS_NAME + ".touchDragged() :: Motor C button released");
motorButtonsPointers[2] = -1;
motorButtonsTouched[2] = false;
}else if(pointer == motorButtonsPointers[3] && !motorD.getBoundingRectangle().contains(touchPointWorldCoords)){
Gdx.app.log(TAG, CLASS_NAME + ".touchDragged() :: Motor D button released");
motorButtonsPointers[3] = -1;
motorButtonsTouched[3] = false;
}
}
return true;
}
@Override
public boolean mouseMoved(int screenX, int screenY) {
Gdx.app.error(TAG, "MOUSE MOVED!");
return false;
}
@Override
public boolean scrolled(int amount) {
// TODO Auto-generated method stub
return false;
}
@Override
public void connected(Controller controller) {
// TODO Auto-generated method stub
}
@Override
public void disconnected(Controller controller) {
// TODO Auto-generated method stub
}
@Override
public boolean buttonDown(Controller controller, int buttonCode) {
if(buttonCode == Ouya.BUTTON_L1){
// Start right motor.
}
if(buttonCode == Ouya.BUTTON_L2){
// Start left motor.
}
if(buttonCode == Ouya.BUTTON_DPAD_LEFT){
// Look left.
}
if(buttonCode == Ouya.BUTTON_DPAD_RIGHT){
// Look right;
}
return false;
}
@Override
public boolean buttonUp(Controller controller, int buttonCode) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean axisMoved(Controller controller, int axisCode, float value) {
if(axisCode == Ouya.AXIS_LEFT_TRIGGER){
}
if(axisCode == Ouya.AXIS_RIGHT_TRIGGER){
// Start
}
return false;
}
@Override
public boolean povMoved(Controller controller, int povCode,
PovDirection value) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean xSliderMoved(Controller controller, int sliderCode,
boolean value) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean ySliderMoved(Controller controller, int sliderCode,
boolean value) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean accelerometerMoved(Controller controller,
int accelerometerCode, Vector3 value) {
// TODO Auto-generated method stub
return false;
};
} }

View File

@@ -46,7 +46,7 @@ public class RobotControlThread extends Thread {
Gdx.app.error(TAG, CLASS_NAME + ".RobotControlThread() :: Error creating server: " + io.getMessage(), io); Gdx.app.error(TAG, CLASS_NAME + ".RobotControlThread() :: Error creating server: " + io.getMessage(), io);
} }
} }
private static class SingletonHolder{ private static class SingletonHolder{
public static final RobotControlThread INSTANCE = new RobotControlThread(); public static final RobotControlThread INSTANCE = new RobotControlThread();
} }

View File

@@ -18,15 +18,12 @@ package ve.ucv.ciens.ccg.nxtar.network;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.DatagramPacket; import java.net.DatagramPacket;
import java.net.DatagramSocket; import java.net.DatagramSocket;
import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import ve.ucv.ciens.ccg.networkdata.VideoFrameDataMessage; import ve.ucv.ciens.ccg.networkdata.VideoFrameDataMessage;
import ve.ucv.ciens.ccg.nxtar.interfaces.NetworkConnectionListener; import ve.ucv.ciens.ccg.nxtar.interfaces.NetworkConnectionListener;
import ve.ucv.ciens.ccg.nxtar.interfaces.Toaster;
import ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants; import ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
@@ -36,44 +33,50 @@ public class VideoStreamingThread extends Thread {
private static final String TAG = "NXTAR_CORE_VIDEOTHREAD"; private static final String TAG = "NXTAR_CORE_VIDEOTHREAD";
private static final String CLASS_NAME = VideoStreamingThread.class.getSimpleName(); private static final String CLASS_NAME = VideoStreamingThread.class.getSimpleName();
private enum ProtocolState_t {WAIT_FOR_START, SEND_CONTINUE, RECEIVE_DATA, SEND_ACK_NEXT, SEND_ACK_WAIT, PAUSED, END_STREAM}; //private enum ProtocolState_t {WAIT_FOR_START, SEND_CONTINUE, RECEIVE_DATA, SEND_ACK_NEXT, SEND_ACK_WAIT, PAUSED, END_STREAM};
private NetworkConnectionListener netListener; private NetworkConnectionListener netListener;
private ServerSocket server; //private ServerSocket server;
private DatagramSocket socket; private DatagramSocket socket;
private Toaster toaster; //private Toaster toaster;
private ProtocolState_t protocolState; //private ProtocolState_t protocolState;
private boolean protocolStarted; private boolean protocolStarted;
private boolean pauseProtocol; /*private boolean pauseProtocol;
private boolean endProtocol; private boolean endProtocol;*/
private boolean done; private boolean done;
private boolean pause;
private Object protocolPauseMonitor; private Object protocolPauseMonitor;
private Socket client; private Socket client;
private ObjectInputStream reader; //private ObjectInputStream reader;
private ObjectOutputStream writer; //private ObjectOutputStream writer;
private VideoFrameMonitor frameMonitor; private VideoFrameMonitor frameMonitor;
private long then; private long then;
private long now; private long now;
private long delta; private long delta;
private int fps; private int fps;
private int lostFramesPerSecond;
private int lostFrames;
private Object pauseMonitor;
private VideoStreamingThread(){ private VideoStreamingThread(){
super(THREAD_NAME); super(THREAD_NAME);
pauseMonitor = new Object();
fps = 0; fps = 0;
lostFramesPerSecond = 0;
netListener = null; netListener = null;
toaster = null; //toaster = null;
protocolStarted = false; protocolStarted = false;
endProtocol = false; /*endProtocol = false;
pauseProtocol = false; pauseProtocol = false;*/
done = false; done = false;
protocolState = ProtocolState_t.WAIT_FOR_START; //protocolState = ProtocolState_t.WAIT_FOR_START;
protocolPauseMonitor = new Object(); protocolPauseMonitor = new Object();
frameMonitor = VideoFrameMonitor.getInstance(); frameMonitor = VideoFrameMonitor.getInstance();
try{ try{
server = new ServerSocket(ProjectConstants.SERVER_TCP_PORT_1); //server = new ServerSocket(ProjectConstants.SERVER_TCP_PORT_1);
socket = new DatagramSocket(ProjectConstants.SERVER_TCP_PORT_2); socket = new DatagramSocket(ProjectConstants.SERVER_TCP_PORT_1);
}catch(IOException io){ }catch(IOException io){
Gdx.app.error(TAG, CLASS_NAME + ".VideoStreamingThread() :: Error creating server: " + io.getMessage(), io); Gdx.app.error(TAG, CLASS_NAME + ".VideoStreamingThread() :: Error creating server: " + io.getMessage(), io);
} }
@@ -87,26 +90,26 @@ public class VideoStreamingThread extends Thread {
return SingletonHolder.INSTANCE; return SingletonHolder.INSTANCE;
} }
public VideoStreamingThread setToaster(Toaster toaster){ /*public VideoStreamingThread setToaster(Toaster toaster){
this.toaster = toaster; this.toaster = toaster;
return this; return this;
} }*/
public void addNetworkConnectionListener(NetworkConnectionListener listener){ public void addNetworkConnectionListener(NetworkConnectionListener listener){
netListener = listener; netListener = listener;
} }
private void toast(String message){ /*private void toast(String message){
if(toaster != null) if(toaster != null)
toaster.showShortToast(message); toaster.showShortToast(message);
} }*/
public void startStreaming(){ public void startStreaming(){
if(!protocolStarted){ if(!protocolStarted){
Gdx.app.debug(TAG, CLASS_NAME + ".startStreaming() :: Requesting protocol start."); Gdx.app.debug(TAG, CLASS_NAME + ".startStreaming() :: Requesting protocol start.");
synchronized(protocolPauseMonitor){ synchronized(protocolPauseMonitor){
protocolStarted = true; protocolStarted = true;
protocolState = ProtocolState_t.SEND_CONTINUE; //protocolState = ProtocolState_t.SEND_CONTINUE;
protocolPauseMonitor.notifyAll(); protocolPauseMonitor.notifyAll();
} }
} }
@@ -115,7 +118,7 @@ public class VideoStreamingThread extends Thread {
public void pauseStreaming(){ public void pauseStreaming(){
if(protocolStarted){ if(protocolStarted){
Gdx.app.debug(TAG, CLASS_NAME + ".pauseStreaming() :: Requesting protocol pause."); Gdx.app.debug(TAG, CLASS_NAME + ".pauseStreaming() :: Requesting protocol pause.");
pauseProtocol = true; //pauseProtocol = true;
}else }else
return; return;
} }
@@ -124,7 +127,7 @@ public class VideoStreamingThread extends Thread {
if(protocolStarted){ if(protocolStarted){
Gdx.app.debug(TAG, CLASS_NAME + ".resumeStreaming() :: Requesting protocol resume."); Gdx.app.debug(TAG, CLASS_NAME + ".resumeStreaming() :: Requesting protocol resume.");
synchronized(protocolPauseMonitor){ synchronized(protocolPauseMonitor){
pauseProtocol = false; //pauseProtocol = false;
protocolPauseMonitor.notifyAll(); protocolPauseMonitor.notifyAll();
} }
}else }else
@@ -134,7 +137,7 @@ public class VideoStreamingThread extends Thread {
public void finishStreaming(){ public void finishStreaming(){
if(protocolStarted){ if(protocolStarted){
Gdx.app.debug(TAG, CLASS_NAME + ".finishStreaming() :: Requesting protocol end."); Gdx.app.debug(TAG, CLASS_NAME + ".finishStreaming() :: Requesting protocol end.");
endProtocol = true; //endProtocol = true;
}else }else
return; return;
} }
@@ -312,7 +315,7 @@ public class VideoStreamingThread extends Thread {
Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Thread finished."); Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Thread finished.");
}*/ }*/
private void receiveImage(){ /*private void receiveImage(){
Object tmpMessage; Object tmpMessage;
VideoFrameDataMessage dataMessage; VideoFrameDataMessage dataMessage;
@@ -337,7 +340,7 @@ public class VideoStreamingThread extends Thread {
}else{ }else{
Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Received something unknown."); Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Received something unknown.");
} }
} }*/
private int byteArray2Int(byte[] array){ private int byteArray2Int(byte[] array){
int number = 0; int number = 0;
@@ -349,63 +352,69 @@ public class VideoStreamingThread extends Thread {
private void receiveUdp(){ private void receiveUdp(){
try{ try{
int intSize; int intSize;
byte[] size = new byte[4]; byte[] size = new byte[4];
byte[] data; byte[] data;
DatagramPacket packet; DatagramPacket packet;
VideoFrameDataMessage dataMessage; VideoFrameDataMessage dataMessage;
Object tmpMessage; Object tmpMessage;
Gdx.app.debug(TAG, CLASS_NAME + ".receiveUdp() :: Reading message size from socket."); Gdx.app.debug(TAG, CLASS_NAME + ".receiveUdp() :: Reading message size from socket.");
try{ try{
packet = new DatagramPacket(size, size.length); packet = new DatagramPacket(size, size.length);
socket.receive(packet); socket.receive(packet);
}catch(IOException io){ }catch(IOException io){
Gdx.app.error(TAG, CLASS_NAME + ".receiveUdp() :: IOException receiving size " + io.getMessage()); Gdx.app.error(TAG, CLASS_NAME + ".receiveUdp() :: IOException receiving size " + io.getMessage());
return; lostFramesPerSecond += 1;
} return;
}
Gdx.app.debug(TAG, CLASS_NAME + ".receiveUdp() :: Creating buffers.");
intSize = byteArray2Int(size); Gdx.app.debug(TAG, CLASS_NAME + ".receiveUdp() :: Creating buffers.");
data = new byte[intSize]; intSize = byteArray2Int(size);
data = new byte[intSize];
Gdx.app.debug(TAG, CLASS_NAME + ".receiveUdp() :: Reading message from socket.");
try{ Gdx.app.debug(TAG, CLASS_NAME + ".receiveUdp() :: Reading message from socket.");
packet = new DatagramPacket(data, data.length); try{
socket.receive(packet); packet = new DatagramPacket(data, data.length);
}catch(IOException io){ socket.receive(packet);
Gdx.app.error(TAG, CLASS_NAME + ".receiveUdp() :: IOException receiving data " + io.getMessage()); }catch(IOException io){
return; Gdx.app.error(TAG, CLASS_NAME + ".receiveUdp() :: IOException receiving data " + io.getMessage());
} lostFramesPerSecond += 1;
return;
ByteArrayInputStream bais = new ByteArrayInputStream(data); }
Gdx.app.debug(TAG, CLASS_NAME + ".receiveUdp() :: Saving message in monitor."); ByteArrayInputStream bais = new ByteArrayInputStream(data);
try{
ObjectInputStream ois = new ObjectInputStream(bais); Gdx.app.debug(TAG, CLASS_NAME + ".receiveUdp() :: Saving message in monitor.");
tmpMessage = ois.readObject(); try{
ObjectInputStream ois = new ObjectInputStream(bais);
if(tmpMessage instanceof VideoFrameDataMessage){ tmpMessage = ois.readObject();
Gdx.app.debug(TAG, CLASS_NAME + ".receiveUdp() :: Received a data message.");
dataMessage = (VideoFrameDataMessage) tmpMessage; if(tmpMessage instanceof VideoFrameDataMessage){
Gdx.app.debug(TAG, CLASS_NAME + ".receiveUdp() :: Received a data message.");
Gdx.app.debug(TAG, CLASS_NAME + ".receiveUdp() :: Received frame dimensions are: " + dataMessage = (VideoFrameDataMessage) tmpMessage;
Integer.toString(dataMessage.imageWidth) + "x" + Integer.toString(dataMessage.imageHeight));
frameMonitor.setFrameDimensions(dataMessage.imageWidth, dataMessage.imageHeight); Gdx.app.debug(TAG, CLASS_NAME + ".receiveUdp() :: Received frame dimensions are: " +
frameMonitor.setNewFrame(dataMessage.data); Integer.toString(dataMessage.imageWidth) + "x" + Integer.toString(dataMessage.imageHeight));
frameMonitor.setFrameDimensions(dataMessage.imageWidth, dataMessage.imageHeight);
}else{ frameMonitor.setNewFrame(dataMessage.data);
Gdx.app.debug(TAG, CLASS_NAME + ".receiveUdp() :: Received something unknown.");
}else{
Gdx.app.debug(TAG, CLASS_NAME + ".receiveUdp() :: Received something unknown.");
lostFramesPerSecond += 1;
}
}catch(IOException io){
Gdx.app.error(TAG, CLASS_NAME + ".receiveUdp() :: IOException received deserializing message " + io.getMessage());
lostFramesPerSecond += 1;
return;
}catch(ClassNotFoundException cn){
Gdx.app.error(TAG, CLASS_NAME + ".receiveUdp() :: ClassNotFoundException received " + cn.getMessage());
lostFramesPerSecond += 1;
return;
} }
}catch(IOException io){
Gdx.app.error(TAG, CLASS_NAME + ".receiveUdp() :: IOException received deserializing message " + io.getMessage());
return;
}catch(ClassNotFoundException cn){
Gdx.app.error(TAG, CLASS_NAME + ".receiveUdp() :: ClassNotFoundException received " + cn.getMessage());
return;
}
}catch(Exception e){ }catch(Exception e){
Gdx.app.error(TAG, CLASS_NAME + ".receiveUdp() :: Exception received " + e.getMessage()); Gdx.app.error(TAG, CLASS_NAME + ".receiveUdp() :: Exception received " + e.getMessage());
lostFramesPerSecond += 1;
return; return;
} }
} }
@@ -413,31 +422,42 @@ public class VideoStreamingThread extends Thread {
public int getFps(){ public int getFps(){
return fps; return fps;
} }
public int getLostFrames(){
return lostFrames;
}
@Override @Override
public void run(){ public void run(){
int frames = 0; int frames = 0;
lostFrames = 0;
// Listen on the server socket until a client successfully connects. // Listen on the server socket until a client successfully connects.
do{ /*do{
try{ try{
Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Listening for client."); Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Listening for client.");
client = server.accept(); client = server.accept();
if(netListener != null) if(netListener != null)
netListener.networkStreamConnected(THREAD_NAME); netListener.networkStreamConnected(THREAD_NAME);
writer = new ObjectOutputStream(client.getOutputStream()); //writer = new ObjectOutputStream(client.getOutputStream());
reader = new ObjectInputStream(client.getInputStream()); reader = new ObjectInputStream(client.getInputStream());
toast("Client connected"); toast("Client connected");
}catch(IOException io){ }catch(IOException io){
Gdx.app.error(TAG, CLASS_NAME + ".run() :: Error accepting client: " + io.getMessage(), io); Gdx.app.error(TAG, CLASS_NAME + ".run() :: Error accepting client: " + io.getMessage(), io);
client = null; client = null;
} }
}while(client != null && !client.isConnected()); }while(client != null && !client.isConnected());*/
then = System.currentTimeMillis(); then = System.currentTimeMillis();
while(!done){ while(!done){
//receiveImage(); synchronized (pauseMonitor) {
while(pause){
try{ pauseMonitor.wait(); }catch(InterruptedException ie){ }
}
}
Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Receiving."); Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Receiving.");
if(netListener != null)
netListener.networkStreamConnected(THREAD_NAME);
receiveUdp(); receiveUdp();
frames++; frames++;
now = System.currentTimeMillis(); now = System.currentTimeMillis();
@@ -445,6 +465,8 @@ public class VideoStreamingThread extends Thread {
if(delta >= 1000){ if(delta >= 1000){
fps = frames; fps = frames;
frames = 0; frames = 0;
lostFrames = lostFramesPerSecond;
lostFramesPerSecond = 0;
then = now; then = now;
delta = 0; delta = 0;
} }
@@ -459,4 +481,16 @@ public class VideoStreamingThread extends Thread {
Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Thread finished."); Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Thread finished.");
} }
public void pause(){
synchronized (pauseMonitor){
pause = true;
}
}
public void play(){
synchronized (pauseMonitor){
pause = false;
pauseMonitor.notifyAll();
}
}
} }

View File

@@ -15,6 +15,8 @@
*/ */
package ve.ucv.ciens.ccg.nxtar.utils; package ve.ucv.ciens.ccg.nxtar.utils;
import com.badlogic.gdx.controllers.mappings.Ouya;
public abstract class ProjectConstants { public abstract class ProjectConstants {
public static final int SERVER_UDP_PORT = 8889; public static final int SERVER_UDP_PORT = 8889;
public static final int SERVER_TCP_PORT_1 = 9989; public static final int SERVER_TCP_PORT_1 = 9989;
@@ -24,4 +26,12 @@ public abstract class ProjectConstants {
public static final int EXIT_FAILURE = 1; public static final int EXIT_FAILURE = 1;
public static final boolean DEBUG = true; public static final boolean DEBUG = true;
public static final int[] POWERS_OF_2 = {64, 128, 256, 512, 1024, 2048};
public static final float OVERSCAN;
static{
OVERSCAN = Ouya.runningOnOuya ? 0.9f : 1.0f;
}
} }