From 0e51d0ea22fe597428fda8be3ac53192917ee21d Mon Sep 17 00:00:00 2001 From: Miguel Astor Date: Tue, 7 Jan 2014 18:13:55 -0430 Subject: [PATCH] Started fixing the video streaming code. --- LICENSE.txt | 202 ------------ .../networkdata/VideoFrameDataMessage.java | 33 ++ .../VideoStreamingControlMessage.java | 29 ++ src/ve/ucv/ciens/ccg/nxtar/NxtARCore.java | 37 ++- .../ccg/nxtar/network/VideoFrameMonitor.java | 84 +++++ .../nxtar/network/VideoStreamingThread.java | 309 +++++++++++++++++- .../protocols/VideoStreamingProtocol.java | 46 +++ src/ve/ucv/ciens/ccg/nxtar/utils/Size.java | 51 +++ 8 files changed, 572 insertions(+), 219 deletions(-) delete mode 100644 LICENSE.txt create mode 100644 src/ve/ucv/ciens/ccg/networkdata/VideoFrameDataMessage.java create mode 100644 src/ve/ucv/ciens/ccg/networkdata/VideoStreamingControlMessage.java create mode 100644 src/ve/ucv/ciens/ccg/nxtar/network/VideoFrameMonitor.java create mode 100644 src/ve/ucv/ciens/ccg/nxtar/network/protocols/VideoStreamingProtocol.java create mode 100644 src/ve/ucv/ciens/ccg/nxtar/utils/Size.java diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index d645695..0000000 --- a/LICENSE.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/src/ve/ucv/ciens/ccg/networkdata/VideoFrameDataMessage.java b/src/ve/ucv/ciens/ccg/networkdata/VideoFrameDataMessage.java new file mode 100644 index 0000000..bfff587 --- /dev/null +++ b/src/ve/ucv/ciens/ccg/networkdata/VideoFrameDataMessage.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2013 Miguel Angel Astor Romero + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ve.ucv.ciens.ccg.networkdata; + +import java.io.Serializable; + +public final class VideoFrameDataMessage implements Serializable{ + private static final long serialVersionUID = 9989L; + public static final int magicNumber = 0x10; + + public int imageWidth; + public int imageHeight; + public byte[] data; + + public VideoFrameDataMessage(){ + imageWidth = -1; + imageHeight = -1; + data = null; + } +} \ No newline at end of file diff --git a/src/ve/ucv/ciens/ccg/networkdata/VideoStreamingControlMessage.java b/src/ve/ucv/ciens/ccg/networkdata/VideoStreamingControlMessage.java new file mode 100644 index 0000000..6f32081 --- /dev/null +++ b/src/ve/ucv/ciens/ccg/networkdata/VideoStreamingControlMessage.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2013 Miguel Angel Astor Romero + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ve.ucv.ciens.ccg.networkdata; + +import java.io.Serializable; + +public final class VideoStreamingControlMessage implements Serializable{ + private static final long serialVersionUID = 8898L; + public static final int magicNumber = 0x20; + + public byte message; + + public VideoStreamingControlMessage(){ + message = -1; + } +} \ No newline at end of file diff --git a/src/ve/ucv/ciens/ccg/nxtar/NxtARCore.java b/src/ve/ucv/ciens/ccg/nxtar/NxtARCore.java index 282b6d2..8af31ea 100644 --- a/src/ve/ucv/ciens/ccg/nxtar/NxtARCore.java +++ b/src/ve/ucv/ciens/ccg/nxtar/NxtARCore.java @@ -20,14 +20,17 @@ import ve.ucv.ciens.ccg.nxtar.interfaces.NetworkConnectionListener; import ve.ucv.ciens.ccg.nxtar.interfaces.Toaster; import ve.ucv.ciens.ccg.nxtar.network.RobotControlThread; import ve.ucv.ciens.ccg.nxtar.network.ServiceDiscoveryThread; +import ve.ucv.ciens.ccg.nxtar.network.VideoFrameMonitor; import ve.ucv.ciens.ccg.nxtar.network.VideoStreamingThread; import ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants; +import ve.ucv.ciens.ccg.nxtar.utils.Size; import com.badlogic.gdx.Application; import com.badlogic.gdx.ApplicationListener; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.GL10; import com.badlogic.gdx.graphics.OrthographicCamera; +import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.Texture.TextureFilter; import com.badlogic.gdx.graphics.g2d.Sprite; @@ -46,6 +49,7 @@ public class NxtARCore implements ApplicationListener, NetworkConnectionListener private MulticastEnabler mcastEnabler; private int connections; + private VideoFrameMonitor frameMonitor; private ServiceDiscoveryThread udpThread; private VideoStreamingThread videoThread; private RobotControlThread robotThread; @@ -83,14 +87,16 @@ public class NxtARCore implements ApplicationListener, NetworkConnectionListener sprite.setPosition(-sprite.getWidth()/2, -sprite.getHeight()/2); Gdx.app.debug(TAG, CLASS_NAME + ".create() :: Creating network threads"); + frameMonitor = VideoFrameMonitor.getInstance(); mcastEnabler.enableMulticast(); udpThread = ServiceDiscoveryThread.getInstance(); videoThread = VideoStreamingThread.getInstance().setToaster(toaster); - robotThread = RobotControlThread.getInstance().setToaster(toaster); + //robotThread = RobotControlThread.getInstance().setToaster(toaster); udpThread.start(); videoThread.start(); - robotThread.start(); + videoThread.startStreaming(); + //robotThread.start(); } @Override @@ -101,13 +107,32 @@ public class NxtARCore implements ApplicationListener, NetworkConnectionListener @Override public void render(){ + byte[] frame; + Size dimensions; + Gdx.gl.glClearColor(1, 1, 1, 1); Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT); - batch.setProjectionMatrix(camera.combined); - batch.begin(); - sprite.draw(batch); - batch.end(); + frame = frameMonitor.getCurrentFrame(); + if(frame != null){ + texture.dispose(); + + dimensions = frameMonitor.getFrameDimensions(); + texture = new Texture(new Pixmap(frame, 0, dimensions.getWidth() * dimensions.getHeight())); + texture.setFilter(TextureFilter.Linear, TextureFilter.Linear); + + TextureRegion region = new TextureRegion(texture, 0, 0, dimensions.getWidth(), dimensions.getHeight()); + + 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); + + batch.setProjectionMatrix(camera.combined); + batch.begin();{ + sprite.draw(batch); + }batch.end(); + } } @Override diff --git a/src/ve/ucv/ciens/ccg/nxtar/network/VideoFrameMonitor.java b/src/ve/ucv/ciens/ccg/nxtar/network/VideoFrameMonitor.java new file mode 100644 index 0000000..939cea0 --- /dev/null +++ b/src/ve/ucv/ciens/ccg/nxtar/network/VideoFrameMonitor.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2013 Miguel Angel Astor Romero + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ve.ucv.ciens.ccg.nxtar.network; + +import ve.ucv.ciens.ccg.nxtar.utils.Size; + +import com.badlogic.gdx.Gdx; + +public class VideoFrameMonitor{ + private final String TAG = "VIDEO_FRAME_MONITOR"; + private final String CLASS_NAME = VideoFrameMonitor.class.getSimpleName(); + + private byte[] frameA; + private byte[] frameB; + private Object frameMonitor; + private Size frameDimensions; + + private VideoFrameMonitor(){ + frameA = null; + frameB = null; + frameMonitor = new Object(); + frameDimensions = new Size(); + } + + private static class SingletonHolder{ + public static final VideoFrameMonitor INSTANCE = new VideoFrameMonitor(); + } + + public static VideoFrameMonitor getInstance(){ + return SingletonHolder.INSTANCE; + } + + public void setFrameDimensions(int width, int height){ + try{ + frameDimensions.setWidth(width); + frameDimensions.setHeight(height); + }catch(IllegalArgumentException ia){ + Gdx.app.debug(TAG, CLASS_NAME + ".setFrameDimensions() :: Bad argument to Size: " + ia.getMessage()); + frameDimensions.setWidth(0); + frameDimensions.setHeight(0); + } + } + + public Size getFrameDimensions(){ + return frameDimensions; + } + + public void setNewFrame(byte[] frame){ + byte[] temp; + + Gdx.app.debug(TAG, CLASS_NAME + ".setNewFrame() :: Loading new frame in frameA."); + frameA = frame; + synchronized(frameMonitor){ + Gdx.app.debug(TAG, CLASS_NAME + ".setNewFrame() :: Swapping frameA and frameB."); + temp = frameA; + frameA = frameB; + frameB = temp; + Gdx.app.debug(TAG, CLASS_NAME + ".setNewFrame() :: Swapping done."); + } + } + + public byte[] getCurrentFrame(){ + byte[] frame; + + synchronized(frameMonitor){ + //Gdx.app.debug(TAG, CLASS_NAME + ".getCurrentFrame() :: Fetching frameB."); + frame = frameB; + } + return frame; + } +} diff --git a/src/ve/ucv/ciens/ccg/nxtar/network/VideoStreamingThread.java b/src/ve/ucv/ciens/ccg/nxtar/network/VideoStreamingThread.java index 8ddef6a..b7b5889 100644 --- a/src/ve/ucv/ciens/ccg/nxtar/network/VideoStreamingThread.java +++ b/src/ve/ucv/ciens/ccg/nxtar/network/VideoStreamingThread.java @@ -16,11 +16,16 @@ package ve.ucv.ciens.ccg.nxtar.network; import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.net.ServerSocket; import java.net.Socket; +import ve.ucv.ciens.ccg.networkdata.VideoFrameDataMessage; +import ve.ucv.ciens.ccg.networkdata.VideoStreamingControlMessage; import ve.ucv.ciens.ccg.nxtar.interfaces.NetworkConnectionListener; import ve.ucv.ciens.ccg.nxtar.interfaces.Toaster; +import ve.ucv.ciens.ccg.nxtar.network.protocols.VideoStreamingProtocol; import ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants; import com.badlogic.gdx.Gdx; @@ -30,15 +35,35 @@ public class VideoStreamingThread extends Thread { private static final String TAG = "NXTAR_CORE_VIDEOTHREAD"; 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 NetworkConnectionListener netListener; private ServerSocket server; - private Socket client; private Toaster toaster; + private ProtocolState_t protocolState; + private boolean protocolStarted; + private boolean pauseProtocol; + private boolean endProtocol; + private boolean done; + private Object protocolPauseMonitor; + private Socket client; + private ObjectInputStream reader; + private ObjectOutputStream writer; + private VideoFrameMonitor frameMonitor; private VideoStreamingThread(){ super(THREAD_NAME); netListener = null; + toaster = null; + protocolStarted = false; + endProtocol = false; + pauseProtocol = false; + done = false; + protocolState = ProtocolState_t.WAIT_FOR_START; + protocolPauseMonitor = new Object(); + frameMonitor = VideoFrameMonitor.getInstance(); + try{ server = new ServerSocket(ProjectConstants.SERVER_TCP_PORT_1); }catch(IOException io){ @@ -63,16 +88,278 @@ public class VideoStreamingThread extends Thread { netListener = listener; } - @Override - public void run(){ - try{ - client = server.accept(); - if(netListener != null) - netListener.networkStreamConnected(THREAD_NAME); - toaster.showShortToast("Client connected to VideoStreamingThread"); - client.close(); - }catch(IOException io){ - Gdx.app.error(TAG, CLASS_NAME + ".run() :: Error accepting client: " + io.getMessage(), io); + private void toast(String message){ + if(toaster != null) + toaster.showShortToast(message); + } + + public void startStreaming(){ + if(!protocolStarted){ + Gdx.app.debug(TAG, CLASS_NAME + ".startStreaming() :: Requesting protocol start."); + synchronized(protocolPauseMonitor){ + protocolStarted = true; + protocolState = ProtocolState_t.SEND_CONTINUE; + protocolPauseMonitor.notifyAll(); + } } } + + public void pauseStreaming(){ + if(protocolStarted){ + Gdx.app.debug(TAG, CLASS_NAME + ".pauseStreaming() :: Requesting protocol pause."); + pauseProtocol = true; + }else + return; + } + + public void resumeStreaming(){ + if(protocolStarted){ + Gdx.app.debug(TAG, CLASS_NAME + ".resumeStreaming() :: Requesting protocol resume."); + synchronized(protocolPauseMonitor){ + pauseProtocol = false; + protocolPauseMonitor.notifyAll(); + } + }else + return; + } + + public void finishStreaming(){ + if(protocolStarted){ + Gdx.app.debug(TAG, CLASS_NAME + ".finishStreaming() :: Requesting protocol end."); + endProtocol = true; + }else + return; + } + + public void finish(){ + done = true; + } + + /*@Override + public void run(){ + Object tmpMessage; + VideoStreamingControlMessage controlMessage; + VideoFrameDataMessage dataMessage; + + // Listen on the server socket until a client successfully connects. + do{ + try{ + Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Listening for client."); + client = server.accept(); + if(netListener != null) + netListener.networkStreamConnected(THREAD_NAME); + writer = new ObjectOutputStream(client.getOutputStream()); + reader = new ObjectInputStream(client.getInputStream()); + toast("Client connected"); + }catch(IOException io){ + Gdx.app.error(TAG, CLASS_NAME + ".run() :: Error accepting client: " + io.getMessage(), io); + client = null; + } + }while(client != null && !client.isConnected()); + + while(!done){ + switch(protocolState){ + case WAIT_FOR_START: + Gdx.app.debug(TAG, CLASS_NAME + ".run() :: State is WAIT_FOR_START."); + // If the app has not started the protocol then wait. + synchronized(protocolPauseMonitor){ + while(!protocolStarted){ + try{ + Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Protocol has not started, waiting."); + protocolPauseMonitor.wait(); + }catch(InterruptedException ie){ } + } + } + break; + + case SEND_CONTINUE: + Gdx.app.debug(TAG, CLASS_NAME + ".run() :: State is SEND_CONTINUE."); + // Prepare the message. + controlMessage = new VideoStreamingControlMessage(); + if(!endProtocol){ + Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Preparing STREAM_CONTROL_END message."); + controlMessage.message = VideoStreamingProtocol.STREAM_CONTROL_END; + }else{ + Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Preparing FLOW_CONTROL_CONTINUE message."); + controlMessage.message = VideoStreamingProtocol.FLOW_CONTROL_CONTINUE; + } + + // Send it! + try{ + writer.writeObject(controlMessage); + }catch(IOException io){ + Gdx.app.error(TAG, CLASS_NAME + ".run() :: Error sending message: " + io.getMessage(), io); + }finally{ + protocolState = ProtocolState_t.RECEIVE_DATA; + } + break; + + case RECEIVE_DATA: + Gdx.app.debug(TAG, CLASS_NAME + ".run() :: State is RECEIVE_DATA."); + + try{ + tmpMessage = reader.readObject(); + }catch(IOException io){ + Gdx.app.error(TAG, CLASS_NAME + ".run() :: IOException while receiving message: " + io.getMessage(), io); + break; + }catch(ClassNotFoundException cn){ + Gdx.app.error(TAG, CLASS_NAME + ".run() :: ClassNotFoundException while receiving message: " + cn.getMessage(), cn); + break; + } + + if(tmpMessage instanceof VideoStreamingControlMessage){ + Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Received a control message."); + controlMessage = (VideoStreamingControlMessage) tmpMessage; + // TODO: handle this case correctly. + + }else if(tmpMessage instanceof VideoFrameDataMessage){ + Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Received a data message."); + dataMessage = (VideoFrameDataMessage) tmpMessage; + + Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Received frame dimensions are: " + + Integer.toString(dataMessage.imageWidth) + "x" + Integer.toString(dataMessage.imageHeight)); + frameMonitor.setFrameDimensions(dataMessage.imageWidth, dataMessage.imageHeight); + frameMonitor.setNewFrame(dataMessage.data); + + if(pauseProtocol) + protocolState = ProtocolState_t.SEND_ACK_WAIT; + else + protocolState = ProtocolState_t.SEND_ACK_NEXT; + + }else{ + Gdx.app.error(TAG, CLASS_NAME + ".run() :: Unrecognized message received!."); + // TODO: handle this case correctly. + System.exit(ProjectConstants.EXIT_FAILURE); + } + + break; + + case SEND_ACK_NEXT: + Gdx.app.debug(TAG, CLASS_NAME + ".run() :: State is SEND_ACK_NEXT."); + // Prepare the message. + controlMessage = new VideoStreamingControlMessage(); + if(!endProtocol){ + Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Preparing STREAM_CONTROL_END message."); + controlMessage.message = VideoStreamingProtocol.STREAM_CONTROL_END; + }else{ + Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Preparing ACK_SEND_NEXT message."); + controlMessage.message = VideoStreamingProtocol.ACK_SEND_NEXT; + } + + // Send it! + try{ + writer.writeObject(controlMessage); + }catch(IOException io){ + Gdx.app.error(TAG, CLASS_NAME + ".run() :: Error sending message: " + io.getMessage(), io); + }finally{ + if(!endProtocol) + protocolState = ProtocolState_t.RECEIVE_DATA; + else + protocolState = ProtocolState_t.END_STREAM; + } + break; + + case SEND_ACK_WAIT: + Gdx.app.debug(TAG, CLASS_NAME + ".run() :: State is SEND_ACK_WAIT."); + // Prepare the message. + controlMessage = new VideoStreamingControlMessage(); + controlMessage.message = VideoStreamingProtocol.ACK_WAIT; + + // Send it! + try{ + writer.writeObject(controlMessage); + }catch(IOException io){ + Gdx.app.error(TAG, CLASS_NAME + ".run() :: Error sending message: " + io.getMessage(), io); + }finally{ + protocolState = ProtocolState_t.PAUSED; + } + break; + + case PAUSED: + Gdx.app.debug(TAG, CLASS_NAME + ".run() :: State is PAUSED."); + // The app requested to stop the protocol temporarily. + synchronized(protocolPauseMonitor){ + while(pauseProtocol){ + try{ + Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Protocol pause requested, waiting."); + protocolPauseMonitor.wait(); + }catch(InterruptedException ie){ } + } + } + protocolState = ProtocolState_t.SEND_CONTINUE; + break; + + case END_STREAM: + Gdx.app.debug(TAG, CLASS_NAME + ".run() :: State is END_STREAM."); + // Simply disconnect from the client and end the thread. + try{ + client.close(); + }catch(IOException io){ + Gdx.app.error(TAG, CLASS_NAME + ".run() :: Error closing client: " + io.getMessage(), io); + } + done = true; + break; + } + } + Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Thread finished."); + }*/ + + private void receiveImage(){ + Object tmpMessage; + VideoFrameDataMessage dataMessage; + + Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Receiving data."); + + try{ + tmpMessage = (VideoFrameDataMessage)reader.readObject(); + }catch(IOException io){ + Gdx.app.error(TAG, CLASS_NAME + ".run() :: IOException while receiving message: " + io.getMessage()); + return; + }catch(ClassNotFoundException cn){ + Gdx.app.error(TAG, CLASS_NAME + ".run() :: ClassNotFoundException while receiving message: " + cn.getMessage()); + return; + } + + if(tmpMessage instanceof VideoFrameDataMessage){ + Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Received a data message."); + dataMessage = (VideoFrameDataMessage) tmpMessage; + frameMonitor.setFrameDimensions(dataMessage.imageWidth, dataMessage.imageHeight); + frameMonitor.setNewFrame(dataMessage.data); + + }else{ + Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Received something unknown."); + } + } + + @Override + public void run(){ + // Listen on the server socket until a client successfully connects. + do{ + try{ + Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Listening for client."); + client = server.accept(); + if(netListener != null) + netListener.networkStreamConnected(THREAD_NAME); + writer = new ObjectOutputStream(client.getOutputStream()); + reader = new ObjectInputStream(client.getInputStream()); + toast("Client connected"); + }catch(IOException io){ + Gdx.app.error(TAG, CLASS_NAME + ".run() :: Error accepting client: " + io.getMessage(), io); + client = null; + } + }while(client != null && !client.isConnected()); + + while(!done){ + receiveImage(); + } + + try{ + client.close(); + }catch(IOException io){ + Gdx.app.error(TAG, CLASS_NAME + ".run() :: Error closing client socket.", io); + } + + Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Thread finished."); + } + } diff --git a/src/ve/ucv/ciens/ccg/nxtar/network/protocols/VideoStreamingProtocol.java b/src/ve/ucv/ciens/ccg/nxtar/network/protocols/VideoStreamingProtocol.java new file mode 100644 index 0000000..183fdb0 --- /dev/null +++ b/src/ve/ucv/ciens/ccg/nxtar/network/protocols/VideoStreamingProtocol.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2013 Miguel Angel Astor Romero + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ve.ucv.ciens.ccg.nxtar.network.protocols; + +public final class VideoStreamingProtocol{ + public static final byte STREAM_CONTROL_END = 0x10; + public static final byte ACK_SEND_NEXT = 0x20; + public static final byte ACK_WAIT = 0x30; + public static final byte FLOW_CONTROL_WAIT = 0x40; + public static final byte FLOW_CONTROL_CONTINUE = 0x50; + public static final byte IMAGE_DATA = 0x60; + public static final byte UNRECOGNIZED = (byte)0xFF; + + public static boolean checkValidityOfMessage(byte message){ + boolean validity; + + switch(message){ + case STREAM_CONTROL_END: + case ACK_SEND_NEXT: + case ACK_WAIT: + case FLOW_CONTROL_WAIT: + case FLOW_CONTROL_CONTINUE: + case IMAGE_DATA: + case UNRECOGNIZED: + validity = true; + break; + default: + validity = false; + } + + return validity; + } +} diff --git a/src/ve/ucv/ciens/ccg/nxtar/utils/Size.java b/src/ve/ucv/ciens/ccg/nxtar/utils/Size.java new file mode 100644 index 0000000..6a47c69 --- /dev/null +++ b/src/ve/ucv/ciens/ccg/nxtar/utils/Size.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2013 Miguel Angel Astor Romero + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ve.ucv.ciens.ccg.nxtar.utils; + +public class Size { + private int width; + private int height; + + public Size(){ + width = 0; + height = 0; + } + + public Size(int width, int height){ + this.width = (width >= 0) ? width : -1 * width; + this.height = (height >= 0) ? height : -1 * height; + } + + public int getWidth(){ + return width; + } + + public int getHeight(){ + return height; + } + + public void setWidth(int width) throws IllegalArgumentException{ + if(width < 0) + throw new IllegalArgumentException("Width must not be less than cero."); + this.width = width; + } + + public void setHeight(int height) throws IllegalArgumentException{ + if(height < 0) + throw new IllegalArgumentException("Height must not be less than cero."); + this.height = height; + } +}