summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoey Grover <joeygrover@gmail.com>2017-10-12 16:31:55 -0400
committerGitHub <noreply@github.com>2017-10-12 16:31:55 -0400
commit3edaf00a2e0ed85ac983531ed91940d98a33ee29 (patch)
tree62696df69d83de79bac52e88a1631ca0cfa8777f
parentbf2f2b405ca10e777832b32eadfe14ed21d6a74d (diff)
parentaa8f0a3869bc0f3a03d41f3323ce9847956da90c (diff)
downloadsdl_android-3edaf00a2e0ed85ac983531ed91940d98a33ee29.tar.gz
Merge pull request #631 from smartdevicelink/feature/issue_469_update
Enhanced Video Streaming APIs (#469)
-rw-r--r--sdl_android/src/androidTest/java/com/smartdevicelink/test/proxy/SdlProxyBaseTests.java23
-rw-r--r--sdl_android/src/androidTest/java/com/smartdevicelink/test/proxy/SystemCapabilityManagerTests.java9
-rw-r--r--sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/AbstractPacketizerTests.java85
-rw-r--r--sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/MockInterfaceBroker.java61
-rw-r--r--sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/MockPacketizer.java33
-rw-r--r--sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/MockStreamListener.java13
-rw-r--r--sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/StreamRPCPacketizerTests.java5
-rw-r--r--sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/RTPH264PacketizerTest.java (renamed from sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/RTPH264PacketizerTest.java)21
-rw-r--r--sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/SdlRemoteDisplayTest.java139
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/SdlConnection/SdlSession.java22
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/encoder/VirtualDisplayEncoder.java400
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/protocol/WiProProtocol.java4
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBase.java568
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/proxy/interfaces/ISdl.java4
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/streaming/VideoStreamingParams.java105
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/streaming/audio/AudioStreamingCodec.java (renamed from sdl_android/src/main/java/com/smartdevicelink/streaming/AudioStreamingCodec.java)2
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/streaming/audio/AudioStreamingLPCMParams.java (renamed from sdl_android/src/main/java/com/smartdevicelink/streaming/AudioStreamingLPCMParams.java)2
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/streaming/audio/AudioStreamingParams.java (renamed from sdl_android/src/main/java/com/smartdevicelink/streaming/AudioStreamingParams.java)2
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/streaming/video/RTPH264Packetizer.java (renamed from sdl_android/src/main/java/com/smartdevicelink/streaming/RTPH264Packetizer.java)4
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/streaming/video/SdlRemoteDisplay.java208
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/streaming/video/VideoStreamingParameters.java218
21 files changed, 1515 insertions, 413 deletions
diff --git a/sdl_android/src/androidTest/java/com/smartdevicelink/test/proxy/SdlProxyBaseTests.java b/sdl_android/src/androidTest/java/com/smartdevicelink/test/proxy/SdlProxyBaseTests.java
index 678a4e95b..fdc52f9b7 100644
--- a/sdl_android/src/androidTest/java/com/smartdevicelink/test/proxy/SdlProxyBaseTests.java
+++ b/sdl_android/src/androidTest/java/com/smartdevicelink/test/proxy/SdlProxyBaseTests.java
@@ -1,6 +1,7 @@
package com.smartdevicelink.test.proxy;
import android.content.Context;
+import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.test.AndroidTestCase;
import android.util.Log;
@@ -79,9 +80,14 @@ import com.smartdevicelink.proxy.rpc.UnsubscribeVehicleDataResponse;
import com.smartdevicelink.proxy.rpc.UnsubscribeWayPointsResponse;
import com.smartdevicelink.proxy.rpc.UpdateTurnListResponse;
import com.smartdevicelink.proxy.rpc.enums.SdlDisconnectedReason;
+import com.smartdevicelink.streaming.video.SdlRemoteDisplay;
+import com.smartdevicelink.streaming.video.VideoStreamingParameters;
+import com.smartdevicelink.test.streaming.video.SdlRemoteDisplayTest;
import junit.framework.Assert;
+import java.lang.reflect.Method;
+
public class SdlProxyBaseTests extends AndroidTestCase{
public static final String TAG = "SdlProxyBaseTests";
@@ -139,6 +145,23 @@ public class SdlProxyBaseTests extends AndroidTestCase{
}
}
+ public void testRemoteDisplayStreaming(){
+ SdlProxyALM proxy = null;
+ SdlProxyBuilder.Builder builder = new SdlProxyBuilder.Builder(new ProxyListenerTest(), "appId", "appName", true, getContext());
+ try{
+ proxy = builder.build();
+ // public void startRemoteDisplayStream(Context context, final Class<? extends SdlRemoteDisplay> remoteDisplay, final VideoStreamingParameters parameters, final boolean encrypted){
+ Method m = SdlProxyALM.class.getDeclaredMethod("startRemoteDisplayStream", Context.class, SdlRemoteDisplay.class, VideoStreamingParameters.class, boolean.class);
+ assertNotNull(m);
+ m.setAccessible(true);
+ m.invoke(proxy,getContext(), SdlRemoteDisplayTest.MockRemoteDisplay.class, (VideoStreamingParameters)null, false);
+ assert true;
+
+ }catch (Exception e){
+ assert false;
+ }
+ }
+
public class ProxyListenerTest implements IProxyListenerALM {
@Override
diff --git a/sdl_android/src/androidTest/java/com/smartdevicelink/test/proxy/SystemCapabilityManagerTests.java b/sdl_android/src/androidTest/java/com/smartdevicelink/test/proxy/SystemCapabilityManagerTests.java
index 03feb2761..8d2ef8d20 100644
--- a/sdl_android/src/androidTest/java/com/smartdevicelink/test/proxy/SystemCapabilityManagerTests.java
+++ b/sdl_android/src/androidTest/java/com/smartdevicelink/test/proxy/SystemCapabilityManagerTests.java
@@ -12,7 +12,6 @@ import com.smartdevicelink.proxy.interfaces.OnSystemCapabilityListener;
import com.smartdevicelink.proxy.rpc.AudioPassThruCapabilities;
import com.smartdevicelink.proxy.rpc.ButtonCapabilities;
import com.smartdevicelink.proxy.rpc.DisplayCapabilities;
-import com.smartdevicelink.proxy.rpc.GetSystemCapability;
import com.smartdevicelink.proxy.rpc.GetSystemCapabilityResponse;
import com.smartdevicelink.proxy.rpc.HMICapabilities;
import com.smartdevicelink.proxy.rpc.PresetBankCapabilities;
@@ -24,17 +23,13 @@ import com.smartdevicelink.proxy.rpc.enums.HmiZoneCapabilities;
import com.smartdevicelink.proxy.rpc.enums.SpeechCapabilities;
import com.smartdevicelink.proxy.rpc.enums.SystemCapabilityType;
import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener;
-import com.smartdevicelink.streaming.VideoStreamingParams;
+import com.smartdevicelink.streaming.video.VideoStreamingParameters;
import com.smartdevicelink.test.Test;
import com.smartdevicelink.test.Validator;
import com.smartdevicelink.util.CorrelationIdGenerator;
import java.util.List;
-import static android.R.id.list;
-import static android.R.id.message;
-import static com.smartdevicelink.proxy.constants.Names.parameters;
-
public class SystemCapabilityManagerTests extends AndroidTestCase {
public static final String TAG = "SystemCapabilityManagerTests";
public static SystemCapabilityManager systemCapabilityManager;
@@ -153,7 +148,7 @@ public class SystemCapabilityManagerTests extends AndroidTestCase {
public void removeServiceListener(SessionType serviceType, ISdlServiceListener sdlServiceListener) {}
@Override
- public void startVideoService(VideoStreamingParams parameters, boolean encrypted) { }
+ public void startVideoService(VideoStreamingParameters parameters, boolean encrypted) { }
@Override
public void stopVideoService() {}
diff --git a/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/AbstractPacketizerTests.java b/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/AbstractPacketizerTests.java
index 80126c67c..c74e8ab84 100644
--- a/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/AbstractPacketizerTests.java
+++ b/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/AbstractPacketizerTests.java
@@ -90,88 +90,3 @@ public class AbstractPacketizerTests extends TestCase {
}
}
-/**
- * This is a mock class for testing the following :
- * {@link com.smartdevicelink.streaming.AbstractPacketizer}
- */
-class MockStreamListener implements IStreamListener {
- public MockStreamListener () { }
- @Override public void sendStreamPacket(ProtocolMessage pm) { }
-}
-
-/**
- * This is a mock class for testing the following :
- * {@link com.smartdevicelink.streaming.AbstractPacketizer}
- */
-class MockInterfaceBroker implements ISdlConnectionListener {
- public MockInterfaceBroker () { }
- @Override
- public void onTransportDisconnected(String info) {
-
- }
- @Override
- public void onTransportError(String info, Exception e) {
-
- }
- @Override
- public void onProtocolMessageReceived(ProtocolMessage msg) {
-
- }
- @Override
- public void onProtocolSessionStartedNACKed(SessionType sessionType,
- byte sessionID, byte version, String correlationID, List<String> rejectedParams) {
-
- }
- @Override
- public void onProtocolSessionStarted(SessionType sessionType,
- byte sessionID, byte version, String correlationID, int hashID,
- boolean isEncrypted) {
-
- }
- @Override
- public void onProtocolSessionEnded(SessionType sessionType, byte sessionID,
- String correlationID) {
-
- }
- @Override
- public void onProtocolSessionEndedNACKed(SessionType sessionType,
- byte sessionID, String correlationID) {
-
- }
- @Override
- public void onProtocolError(String info, Exception e) {
-
- }
- @Override
- public void onHeartbeatTimedOut(byte sessionID) {
-
- }
- @Override
- public void onProtocolServiceDataACK(SessionType sessionType, int dataSize,
- byte sessionID) {
-
- }
-}
-
-/**
- * This is a mock class for testing the following :
- * {@link com.smartdevicelink.streaming.AbstractPacketizer}
- */
-class MockPacketizer extends AbstractPacketizer {
- public MockPacketizer (IStreamListener l, InputStream i, SessionType s, byte sid, SdlSession sdlsession) throws IOException { super (l, i, s, sid, sdlsession); }
- public MockPacketizer (IStreamListener l, InputStream i, RPCRequest r, SessionType s, byte sid, byte w, SdlSession sdlsession) throws IOException { super (l, i, r, s, sid, w, sdlsession); }
-
- @Override public void start() throws IOException { }
- @Override public void stop() { }
-
- public IStreamListener getListener () { return _streamListener; }
- public InputStream getInputStream () { return is; }
- public SessionType getSessionType () { return _serviceType; }
- public SdlSession getSdlSession () { return _session; }
- public byte getSessionId () { return _rpcSessionID; }
- public RPCRequest getRPCRequest () { return _request; }
- public byte getWiproVersion () { return _wiproVersion; }
-
- @Override public void pause() { }
- @Override public void resume() { }
-}
diff --git a/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/MockInterfaceBroker.java b/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/MockInterfaceBroker.java
new file mode 100644
index 000000000..86dbd7b81
--- /dev/null
+++ b/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/MockInterfaceBroker.java
@@ -0,0 +1,61 @@
+package com.smartdevicelink.test.streaming;
+
+import com.smartdevicelink.SdlConnection.ISdlConnectionListener;
+import com.smartdevicelink.protocol.ProtocolMessage;
+import com.smartdevicelink.protocol.enums.SessionType;
+
+import java.util.List;
+
+/**
+ * This is a mock class for testing the following :
+ * {@link com.smartdevicelink.streaming.AbstractPacketizer}
+ */
+public class MockInterfaceBroker implements ISdlConnectionListener {
+ public MockInterfaceBroker () { }
+ @Override
+ public void onTransportDisconnected(String info) {
+
+ }
+ @Override
+ public void onTransportError(String info, Exception e) {
+
+ }
+ @Override
+ public void onProtocolMessageReceived(ProtocolMessage msg) {
+
+ }
+ @Override
+ public void onProtocolSessionStartedNACKed(SessionType sessionType,
+ byte sessionID, byte version, String correlationID, List<String> rejectedParams) {
+
+ }
+ @Override
+ public void onProtocolSessionStarted(SessionType sessionType,
+ byte sessionID, byte version, String correlationID, int hashID,
+ boolean isEncrypted) {
+
+ }
+ @Override
+ public void onProtocolSessionEnded(SessionType sessionType, byte sessionID,
+ String correlationID) {
+
+ }
+ @Override
+ public void onProtocolSessionEndedNACKed(SessionType sessionType,
+ byte sessionID, String correlationID) {
+
+ }
+ @Override
+ public void onProtocolError(String info, Exception e) {
+
+ }
+ @Override
+ public void onHeartbeatTimedOut(byte sessionID) {
+
+ }
+ @Override
+ public void onProtocolServiceDataACK(SessionType sessionType, int dataSize,
+ byte sessionID) {
+
+ }
+}
diff --git a/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/MockPacketizer.java b/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/MockPacketizer.java
new file mode 100644
index 000000000..d32a204a6
--- /dev/null
+++ b/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/MockPacketizer.java
@@ -0,0 +1,33 @@
+package com.smartdevicelink.test.streaming;
+
+import com.smartdevicelink.SdlConnection.SdlSession;
+import com.smartdevicelink.protocol.enums.SessionType;
+import com.smartdevicelink.proxy.RPCRequest;
+import com.smartdevicelink.streaming.AbstractPacketizer;
+import com.smartdevicelink.streaming.IStreamListener;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * This is a mock class for testing the following :
+ * {@link com.smartdevicelink.streaming.AbstractPacketizer}
+ */
+public class MockPacketizer extends AbstractPacketizer {
+ public MockPacketizer (IStreamListener l, InputStream i, SessionType s, byte sid, SdlSession sdlsession) throws IOException { super (l, i, s, sid, sdlsession); }
+ public MockPacketizer (IStreamListener l, InputStream i, RPCRequest r, SessionType s, byte sid, byte w, SdlSession sdlsession) throws IOException { super (l, i, r, s, sid, w, sdlsession); }
+
+ @Override public void start() throws IOException { }
+ @Override public void stop() { }
+
+ public IStreamListener getListener () { return _streamListener; }
+ public InputStream getInputStream () { return is; }
+ public SessionType getSessionType () { return _serviceType; }
+ public SdlSession getSdlSession () { return _session; }
+ public byte getSessionId () { return _rpcSessionID; }
+ public RPCRequest getRPCRequest () { return _request; }
+ public byte getWiproVersion () { return _wiproVersion; }
+
+ @Override public void pause() { }
+ @Override public void resume() { }
+}
diff --git a/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/MockStreamListener.java b/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/MockStreamListener.java
new file mode 100644
index 000000000..0081c65ea
--- /dev/null
+++ b/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/MockStreamListener.java
@@ -0,0 +1,13 @@
+package com.smartdevicelink.test.streaming;
+
+import com.smartdevicelink.protocol.ProtocolMessage;
+import com.smartdevicelink.streaming.IStreamListener;
+
+/**
+ * This is a mock class for testing the following :
+ * {@link com.smartdevicelink.streaming.AbstractPacketizer}
+ */
+public class MockStreamListener implements IStreamListener {
+ public MockStreamListener () { }
+ @Override public void sendStreamPacket(ProtocolMessage pm) { }
+}
diff --git a/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/StreamRPCPacketizerTests.java b/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/StreamRPCPacketizerTests.java
index cac3b1616..0f72eec95 100644
--- a/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/StreamRPCPacketizerTests.java
+++ b/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/StreamRPCPacketizerTests.java
@@ -10,6 +10,7 @@ import java.net.URLConnection;
import com.smartdevicelink.SdlConnection.SdlSession;
import com.smartdevicelink.protocol.enums.SessionType;
import com.smartdevicelink.proxy.RPCRequest;
+import com.smartdevicelink.proxy.SdlProxyBase;
import com.smartdevicelink.streaming.IStreamListener;
import com.smartdevicelink.streaming.StreamRPCPacketizer;
import com.smartdevicelink.test.Test;
@@ -20,13 +21,13 @@ import junit.framework.TestCase;
/**
* This is a unit test class for the SmartDeviceLink library project class :
- * {@link com.smartdevicelink.streaming.StreamRpcPacketizer}
+ * {@link com.smartdevicelink.streaming.StreamRPCPacketizer}
*/
public class StreamRPCPacketizerTests extends TestCase {
/**
* This is a unit test for the following methods :
- * {@link com.smartdevicelink.streaming.StreamRPCPacketizer#StreamRPCPacketizer(IStreamListener, InputStream, RPCRequest, SessionType, byte, byte)}
+ * {@link com.smartdevicelink.streaming.StreamRPCPacketizer#StreamRPCPacketizer(SdlProxyBase, IStreamListener, InputStream, RPCRequest, SessionType, byte, byte, long, SdlSession)}
*/
public void testConstructor () {
diff --git a/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/RTPH264PacketizerTest.java b/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/RTPH264PacketizerTest.java
index 70274444c..7090ba8e8 100644
--- a/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/RTPH264PacketizerTest.java
+++ b/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/RTPH264PacketizerTest.java
@@ -28,14 +28,15 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package com.smartdevicelink.test.streaming;
+package com.smartdevicelink.test.streaming.video;
import com.smartdevicelink.SdlConnection.SdlSession;
import com.smartdevicelink.protocol.ProtocolMessage;
import com.smartdevicelink.protocol.enums.SessionType;
import com.smartdevicelink.proxy.interfaces.IVideoStreamListener;
import com.smartdevicelink.streaming.IStreamListener;
-import com.smartdevicelink.streaming.RTPH264Packetizer;
+import com.smartdevicelink.streaming.video.RTPH264Packetizer;
+import com.smartdevicelink.test.streaming.MockInterfaceBroker;
import com.smartdevicelink.transport.BTTransportConfig;
import junit.framework.TestCase;
@@ -46,7 +47,7 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
- * This class includes a unit test for {@link com.smartdevicelink.streaming.RTPH264Packetizer}.
+ * This class includes a unit test for {@link RTPH264Packetizer}.
*
* @author Sho Amano
*/
@@ -309,7 +310,7 @@ public class RTPH264PacketizerTest extends TestCase {
}
/**
- * Test for {@link com.smartdevicelink.streaming.RTPH264Packetizer#setPayloadType(byte)}
+ * Test for {@link RTPH264Packetizer#setPayloadType(byte)}
*/
public void testSetPayloadType() {
byte pt = (byte)123;
@@ -340,7 +341,7 @@ public class RTPH264PacketizerTest extends TestCase {
}
/**
- * Test for {@link com.smartdevicelink.streaming.RTPH264Packetizer#setSSRC(int)}
+ * Test for {@link RTPH264Packetizer#setSSRC(int)}
*/
public void testSetSSRC() {
int ssrc = 0xFEDCBA98;
@@ -372,8 +373,8 @@ public class RTPH264PacketizerTest extends TestCase {
}
/**
- * Test for {@link com.smartdevicelink.streaming.RTPH264Packetizer#pause()} and
- * {@link com.smartdevicelink.streaming.RTPH264Packetizer#resume()}
+ * Test for {@link RTPH264Packetizer#pause()} and
+ * {@link RTPH264Packetizer#resume()}
*/
public void testPauseResume() {
int index = 0;
@@ -443,7 +444,7 @@ public class RTPH264PacketizerTest extends TestCase {
}
/**
- * Test for {@link com.smartdevicelink.streaming.RTPH264Packetizer#sendFrame(byte[], int, int, long)}
+ * Test for {@link RTPH264Packetizer#sendFrame(byte[], int, int, long)}
*/
public void testSendFrameInterfaceWithArray() {
StreamVerifier verifier = new StreamVerifier(SAMPLE_STREAM);
@@ -472,7 +473,7 @@ public class RTPH264PacketizerTest extends TestCase {
}
/**
- * Test for {@link com.smartdevicelink.streaming.RTPH264Packetizer#sendFrame(ByteBuffer, long)}
+ * Test for {@link RTPH264Packetizer#sendFrame(ByteBuffer, long)}
*/
public void testSendFrameInterfaceWithByteBuffer() {
StreamVerifier verifier = new StreamVerifier(SAMPLE_STREAM);
@@ -501,7 +502,7 @@ public class RTPH264PacketizerTest extends TestCase {
}
/**
- * Test for {@link com.smartdevicelink.streaming.RTPH264Packetizer#sendFrame(ByteBuffer, long)}
+ * Test for {@link RTPH264Packetizer#sendFrame(ByteBuffer, long)}
* with direct ByteBuffer
*/
public void testSendFrameInterfaceWithDirectByteBuffer() {
diff --git a/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/SdlRemoteDisplayTest.java b/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/SdlRemoteDisplayTest.java
new file mode 100644
index 000000000..96e6e29cb
--- /dev/null
+++ b/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/SdlRemoteDisplayTest.java
@@ -0,0 +1,139 @@
+package com.smartdevicelink.test.streaming.video;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Looper;
+import android.test.AndroidTestCase;
+import android.view.Display;
+import android.view.MotionEvent;
+import android.widget.RelativeLayout;
+
+import com.smartdevicelink.R;
+import com.smartdevicelink.encoder.VirtualDisplayEncoder;
+import com.smartdevicelink.proxy.interfaces.IVideoStreamListener;
+import com.smartdevicelink.streaming.video.SdlRemoteDisplay;
+import com.smartdevicelink.streaming.video.VideoStreamingParameters;
+
+import junit.framework.TestCase;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.FutureTask;
+
+
+public class SdlRemoteDisplayTest extends AndroidTestCase {
+
+ MockRemoteDisplayCallback rdCallback = new MockRemoteDisplayCallback();
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ if(Looper.myLooper() == null){
+ Looper.prepare();
+ }
+ }
+
+ public void testCreator(){
+ VirtualDisplayEncoder encoder = createVDE();
+ assertNotNull(encoder);
+
+
+ SdlRemoteDisplay.Creator creator = new SdlRemoteDisplay.Creator(this.getContext(), encoder.getVirtualDisplay(), null, MockRemoteDisplay.class, rdCallback);
+ assertNotNull(creator);
+ FutureTask<Boolean> fTask = new FutureTask<Boolean>(creator);
+ Thread showPresentation = new Thread(fTask);
+ showPresentation.start();
+ assert true;
+ }
+
+ public void testConstructor(){
+ VirtualDisplayEncoder encoder = createVDE();
+ assertNotNull(encoder);
+ MockRemoteDisplay remoteDisplay = new MockRemoteDisplay(getContext(), encoder.getVirtualDisplay());
+ assertNotNull(remoteDisplay);
+
+ encoder.shutDown();
+ }
+
+
+ @TargetApi(19)
+ public void testTouchEvents(){
+ VirtualDisplayEncoder encoder = createVDE();
+ assertNotNull(encoder);
+ MockRemoteDisplay remoteDisplay = new MockRemoteDisplay(getContext(), encoder.getVirtualDisplay());
+ assertNotNull(remoteDisplay);
+ remoteDisplay.show();
+
+ assertNotNull(remoteDisplay.getMainView());
+
+ try{
+ remoteDisplay.handleMotionEvent(MotionEvent.obtain(10, System.currentTimeMillis(), MotionEvent.ACTION_DOWN, 100, 100, 0));
+ }catch (Exception e){
+ assert false;
+ }
+
+ remoteDisplay.dismiss();
+ encoder.shutDown();
+ }
+
+
+ public VirtualDisplayEncoder createVDE(){
+ try{
+ VirtualDisplayEncoder displayEncoder = new VirtualDisplayEncoder();
+ displayEncoder.init(getContext(), new MockVideoStreamListener(), new VideoStreamingParameters());
+ displayEncoder.start();
+ return displayEncoder;
+ }catch (Exception e ){
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+
+ //Mock classes
+
+ public static class MockRemoteDisplay extends SdlRemoteDisplay{
+
+ public MockRemoteDisplay(Context context, Display display) {
+ super(context, display);
+
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView( new RelativeLayout(getContext()));
+
+ }
+
+
+ }
+
+ class MockVideoStreamListener implements IVideoStreamListener{
+
+ @Override
+ public void sendFrame(byte[] data, int offset, int length, long presentationTimeUs) throws ArrayIndexOutOfBoundsException {
+
+ }
+
+ @Override
+ public void sendFrame(ByteBuffer data, long presentationTimeUs) {
+
+ }
+ }
+
+ class MockRemoteDisplayCallback implements SdlRemoteDisplay.Callback{
+
+ @Override
+ public void onCreated(SdlRemoteDisplay remoteDisplay) {
+
+ }
+
+ @Override
+ public void onInvalidated(SdlRemoteDisplay remoteDisplay) {
+
+ }
+ }
+
+
+}
diff --git a/sdl_android/src/main/java/com/smartdevicelink/SdlConnection/SdlSession.java b/sdl_android/src/main/java/com/smartdevicelink/SdlConnection/SdlSession.java
index 269c9479a..20bcc0cc8 100644
--- a/sdl_android/src/main/java/com/smartdevicelink/SdlConnection/SdlSession.java
+++ b/sdl_android/src/main/java/com/smartdevicelink/SdlConnection/SdlSession.java
@@ -16,6 +16,7 @@ import android.util.Log;
import android.view.Surface;
import com.smartdevicelink.encoder.SdlEncoder;
+import com.smartdevicelink.encoder.VirtualDisplayEncoder;
import com.smartdevicelink.exception.SdlException;
import com.smartdevicelink.protocol.ProtocolMessage;
import com.smartdevicelink.protocol.enums.SessionType;
@@ -32,10 +33,10 @@ import com.smartdevicelink.security.ISecurityInitializedListener;
import com.smartdevicelink.security.SdlSecurityBase;
import com.smartdevicelink.streaming.AbstractPacketizer;
import com.smartdevicelink.streaming.IStreamListener;
-import com.smartdevicelink.streaming.RTPH264Packetizer;
+import com.smartdevicelink.streaming.video.RTPH264Packetizer;
import com.smartdevicelink.streaming.StreamPacketizer;
import com.smartdevicelink.streaming.StreamRPCPacketizer;
-import com.smartdevicelink.streaming.VideoStreamingParams;
+import com.smartdevicelink.streaming.video.VideoStreamingParameters;
import com.smartdevicelink.transport.BaseTransportConfig;
import com.smartdevicelink.transport.MultiplexTransport;
import com.smartdevicelink.transport.enums.TransportType;
@@ -58,11 +59,12 @@ public class SdlSession implements ISdlConnectionListener, IHeartbeatMonitorList
AbstractPacketizer mVideoPacketizer = null;
StreamPacketizer mAudioPacketizer = null;
SdlEncoder mSdlEncoder = null;
+ VirtualDisplayEncoder virtualDisplayEncoder = null;
private final static int BUFF_READ_SIZE = 1024;
private int sessionHashId = 0;
private HashMap<SessionType, CopyOnWriteArrayList<ISdlServiceListener>> serviceListeners;
- private VideoStreamingParams desiredVideoParams = null;
- private VideoStreamingParams acceptedVideoParams = null;
+ private VideoStreamingParameters desiredVideoParams = null;
+ private VideoStreamingParameters acceptedVideoParams = null;
public static SdlSession createSession(byte wiproVersion, ISdlConnectionListener listener, BaseTransportConfig btConfig) {
@@ -708,7 +710,7 @@ public class SdlSession implements ISdlConnectionListener, IHeartbeatMonitorList
return serviceListeners;
}
- public void setDesiredVideoParams(VideoStreamingParams params){
+ public void setDesiredVideoParams(VideoStreamingParameters params){
this.desiredVideoParams = params;
}
@@ -717,24 +719,24 @@ public class SdlSession implements ISdlConnectionListener, IHeartbeatMonitorList
* the default options will be returned and set for this instance.
* @return
*/
- public VideoStreamingParams getDesiredVideoParams(){
+ public VideoStreamingParameters getDesiredVideoParams(){
if(desiredVideoParams == null){
- desiredVideoParams = new VideoStreamingParams();
+ desiredVideoParams = new VideoStreamingParameters();
}
return desiredVideoParams;
}
- public void setAcceptedVideoParams(VideoStreamingParams params){
+ public void setAcceptedVideoParams(VideoStreamingParameters params){
this.acceptedVideoParams = params;
}
- public VideoStreamingParams getAcceptedVideoParams(){
+ public VideoStreamingParameters getAcceptedVideoParams(){
return acceptedVideoParams;
}
private VideoStreamingProtocol getAcceptedProtocol() {
// acquire default protocol (RAW)
- VideoStreamingProtocol protocol = new VideoStreamingParams().getFormat().getProtocol();
+ VideoStreamingProtocol protocol = new VideoStreamingParameters().getFormat().getProtocol();
if (acceptedVideoParams != null) {
VideoStreamingFormat format = acceptedVideoParams.getFormat();
diff --git a/sdl_android/src/main/java/com/smartdevicelink/encoder/VirtualDisplayEncoder.java b/sdl_android/src/main/java/com/smartdevicelink/encoder/VirtualDisplayEncoder.java
new file mode 100644
index 000000000..0fe303c71
--- /dev/null
+++ b/sdl_android/src/main/java/com/smartdevicelink/encoder/VirtualDisplayEncoder.java
@@ -0,0 +1,400 @@
+/*
+ * Copyright (c) 2017 Livio, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the Livio Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.smartdevicelink.encoder;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaFormat;
+import android.os.Build;
+import android.util.Log;
+import android.view.Display;
+import android.view.Surface;
+
+import com.smartdevicelink.proxy.interfaces.IVideoStreamListener;
+import com.smartdevicelink.proxy.rpc.ImageResolution;
+import com.smartdevicelink.proxy.rpc.VideoStreamingFormat;
+import com.smartdevicelink.proxy.rpc.enums.VideoStreamingCodec;
+import com.smartdevicelink.streaming.video.VideoStreamingParameters;
+
+import java.nio.ByteBuffer;
+
+@TargetApi(19)
+@SuppressWarnings("NullableProblems")
+public class VirtualDisplayEncoder {
+ private static final String TAG = "VirtualDisplayEncoder";
+ private VideoStreamingParameters streamingParams = new VideoStreamingParameters();
+ private DisplayManager mDisplayManager;
+ private volatile MediaCodec mVideoEncoder = null;
+ private volatile MediaCodec.BufferInfo mVideoBufferInfo = null;
+ private volatile Surface inputSurface = null;
+ private volatile VirtualDisplay virtualDisplay = null;
+ private IVideoStreamListener mOutputListener;
+ private Boolean initPassed = false;
+ private final Object STREAMING_LOCK = new Object();
+
+ // Codec-specific data (SPS and PPS)
+ private byte[] mH264CodecSpecificData = null;
+
+ //For older (<21) OS versions
+ private Thread encoderThread;
+
+
+ /**
+ * Initialization method for VirtualDisplayEncoder object. MUST be called before start() or shutdown()
+ * Will overwrite previously set videoWeight and videoHeight
+ * @param context to create the virtual display
+ * @param outputListener the listener that the video frames will be sent through
+ * @param streamingParams parameters to create the virtual display and encoder
+ * @throws Exception if the API level is <19 or supplied parameters were null
+ */
+ public void init(Context context, IVideoStreamListener outputListener, VideoStreamingParameters streamingParams) throws Exception {
+ if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
+ Log.e(TAG, "API level of 19 required for VirtualDisplayEncoder");
+ throw new Exception("API level of 19 required");
+ }
+
+ if (context == null || outputListener == null || streamingParams == null || streamingParams.getResolution() == null || streamingParams.getFormat() == null) {
+ Log.e(TAG, "init parameters cannot be null for VirtualDisplayEncoder");
+ throw new Exception("init parameters cannot be null");
+ }
+
+ this.mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
+
+ this.streamingParams.update(streamingParams);
+
+ mOutputListener = outputListener;
+
+ initPassed = true;
+ }
+
+ @SuppressWarnings("unused")
+ public VideoStreamingParameters getStreamingParams(){
+ return this.streamingParams;
+ }
+
+ @SuppressWarnings("unused")
+ public void setStreamingParams(int displayDensity, ImageResolution resolution, int frameRate, int bitrate, int interval, VideoStreamingFormat format) {
+ this.streamingParams = new VideoStreamingParameters(displayDensity, frameRate, bitrate, interval, resolution, format);
+ }
+
+ @SuppressWarnings("unused")
+ public void setStreamingParams(VideoStreamingParameters streamingParams) {
+ this.streamingParams = streamingParams;
+ }
+
+ /**
+ * NOTE: Must call init() without error before calling this method.
+ * Prepares the encoder and virtual display.
+ */
+ public void start() throws Exception {
+ if (!initPassed) {
+ Log.e(TAG, "VirtualDisplayEncoder was not properly initialized with the init() method.");
+ return;
+ }
+ if (streamingParams == null || streamingParams.getResolution() == null || streamingParams.getFormat() == null) {
+ return;
+ }
+
+ synchronized (STREAMING_LOCK) {
+
+ try {
+ inputSurface = prepareVideoEncoder();
+
+ // Create a virtual display that will output to our encoder.
+ virtualDisplay = mDisplayManager.createVirtualDisplay(TAG,
+ streamingParams.getResolution().getResolutionWidth(), streamingParams.getResolution().getResolutionHeight(),
+ streamingParams.getDisplayDensity(), inputSurface, DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION);
+
+ startEncoder();
+
+ } catch (Exception ex) {
+ Log.e(TAG, "Unable to create Virtual Display.");
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+
+ public void shutDown() {
+ if (!initPassed) {
+ Log.e(TAG, "VirtualDisplayEncoder was not properly initialized with the init() method.");
+ return;
+ }
+ try {
+ if (encoderThread != null) {
+ encoderThread.interrupt();
+ encoderThread = null;
+ }
+
+ if (mVideoEncoder != null) {
+ mVideoEncoder.stop();
+ mVideoEncoder.release();
+ mVideoEncoder = null;
+ }
+
+ if (virtualDisplay != null) {
+ virtualDisplay.release();
+ virtualDisplay = null;
+ }
+
+ if (inputSurface != null) {
+ inputSurface.release();
+ inputSurface = null;
+ }
+ } catch (Exception ex) {
+ Log.e(TAG, "shutDown() failed");
+ }
+ }
+
+ private Surface prepareVideoEncoder() {
+
+ if (streamingParams == null || streamingParams.getResolution() == null || streamingParams.getFormat() == null) {
+ return null;
+ }
+
+ if (mVideoBufferInfo == null) {
+ mVideoBufferInfo = new MediaCodec.BufferInfo();
+ }
+
+ String videoMimeType = getMimeForFormat(streamingParams.getFormat());
+
+ MediaFormat format = MediaFormat.createVideoFormat(videoMimeType, streamingParams.getResolution().getResolutionWidth(), streamingParams.getResolution().getResolutionHeight());
+
+ // Set some required properties. The media codec may fail if these aren't defined.
+ format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
+ format.setInteger(MediaFormat.KEY_BIT_RATE, streamingParams.getBitrate());
+ format.setInteger(MediaFormat.KEY_FRAME_RATE, streamingParams.getFrameRate());
+ format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, streamingParams.getInterval()); // seconds between I-frames
+
+
+ // Create a MediaCodec encoder and configure it. Get a Surface we can use for recording into.
+ try {
+ mVideoEncoder = MediaCodec.createEncoderByType(videoMimeType);
+ mVideoEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
+ Surface surface = mVideoEncoder.createInputSurface(); //prepared
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ mVideoEncoder.setCallback(new MediaCodec.Callback() {
+ @Override
+ public void onInputBufferAvailable(MediaCodec codec, int index) {
+ // nothing to do here
+ }
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ @Override
+ public void onOutputBufferAvailable(MediaCodec codec, int index, MediaCodec.BufferInfo info) {
+ try {
+ ByteBuffer encodedData = codec.getOutputBuffer(index);
+ if (encodedData != null) {
+ if (info.size != 0) {
+ byte[] dataToWrite;// = new byte[info.size];
+ int dataOffset = 0;
+
+ // append SPS and PPS in front of every IDR NAL unit
+ if ((info.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0 && mH264CodecSpecificData != null) {
+ dataToWrite = new byte[mH264CodecSpecificData.length + info.size];
+ System.arraycopy(mH264CodecSpecificData, 0, dataToWrite, 0, mH264CodecSpecificData.length);
+ dataOffset = mH264CodecSpecificData.length;
+ } else {
+ dataToWrite = new byte[info.size];
+ }
+
+ encodedData.position(info.offset);
+ encodedData.limit(info.offset + info.size);
+
+ encodedData.get(dataToWrite, dataOffset, info.size);
+ if (mOutputListener != null) {
+ mOutputListener.sendFrame(dataToWrite, 0, dataToWrite.length, info.presentationTimeUs);
+ }
+ }
+
+ codec.releaseOutputBuffer(index, false);
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ @Override
+ public void onError(MediaCodec codec, MediaCodec.CodecException e) {
+ e.printStackTrace();
+ }
+
+ @Override
+ public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
+ mH264CodecSpecificData = EncoderUtils.getCodecSpecificData(format);
+ }
+ });
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ //Implied from previous if check that this has to be older than Build.VERSION_CODES.LOLLIPOP
+ encoderThread = new Thread(new EncoderCompat());
+
+ } else {
+ Log.e(TAG, "Unable to start encoder. Android OS version " + Build.VERSION.SDK_INT + "is not supported");
+ }
+
+ return surface;
+
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ return null;
+ }
+
+ private void startEncoder() {
+ if (mVideoEncoder != null) {
+ mVideoEncoder.start();
+ }
+ if (encoderThread != null) {
+ encoderThread.start();
+ }
+ }
+
+ public Display getVirtualDisplay() {
+ return virtualDisplay.getDisplay();
+ }
+
+ private String getMimeForFormat(VideoStreamingFormat format) {
+ if (format != null) {
+ VideoStreamingCodec codec = format.getCodec();
+ if (codec != null) {
+ switch (codec) { //MediaFormat constants are only available in Android 21+
+ case VP8:
+ return "video/x-vnd.on2.vp8"; //MediaFormat.MIMETYPE_VIDEO_VP8
+ case VP9:
+ return "video/x-vnd.on2.vp9"; //MediaFormat.MIMETYPE_VIDEO_VP9
+ case H264: //Fall through
+ default:
+ return "video/avc"; // MediaFormat.MIMETYPE_VIDEO_AVC
+ }
+ }
+ }
+ return null;
+ }
+
+ private class EncoderCompat implements Runnable {
+
+ @Override
+ public void run() {
+ try {
+ drainEncoder(false);
+ } catch (Exception e) {
+ Log.w(TAG, "Error attempting to drain encoder");
+ } finally {
+ mVideoEncoder.release();
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ void drainEncoder(boolean endOfStream) {
+ if (mVideoEncoder == null || mOutputListener == null) {
+ return;
+ }
+
+ if (endOfStream) {
+ mVideoEncoder.signalEndOfInputStream();
+ }
+
+ ByteBuffer[] encoderOutputBuffers = mVideoEncoder.getOutputBuffers();
+ Thread currentThread = Thread.currentThread();
+ while (!currentThread.isInterrupted()) {
+ int encoderStatus = mVideoEncoder.dequeueOutputBuffer(mVideoBufferInfo, -1);
+ if(encoderStatus < 0){
+ if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
+ // no output available yet
+ if (!endOfStream) {
+ break; // out of while
+ }
+ } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+ // not expected for an encoder
+ encoderOutputBuffers = mVideoEncoder.getOutputBuffers();
+ } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+ if (mH264CodecSpecificData == null) {
+ MediaFormat format = mVideoEncoder.getOutputFormat();
+ mH264CodecSpecificData = EncoderUtils.getCodecSpecificData(format);
+ } else {
+ Log.w(TAG, "Output format change notified more than once, ignoring.");
+ }
+ }
+ } else {
+ if ((mVideoBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
+ // If we already retrieve codec specific data via OUTPUT_FORMAT_CHANGED event,
+ // we do not need this data.
+ if (mH264CodecSpecificData != null) {
+ mVideoBufferInfo.size = 0;
+ } else {
+ Log.i(TAG, "H264 codec specific data not retrieved yet.");
+ }
+ }
+
+ if (mVideoBufferInfo.size != 0) {
+ ByteBuffer encoderOutputBuffer = encoderOutputBuffers[encoderStatus];
+ byte[] dataToWrite;
+ int dataOffset = 0;
+
+ // append SPS and PPS in front of every IDR NAL unit
+ if ((mVideoBufferInfo.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0 && mH264CodecSpecificData != null) {
+ dataToWrite = new byte[mH264CodecSpecificData.length + mVideoBufferInfo.size];
+ System.arraycopy(mH264CodecSpecificData, 0, dataToWrite, 0, mH264CodecSpecificData.length);
+ dataOffset = mH264CodecSpecificData.length;
+ } else {
+ dataToWrite = new byte[mVideoBufferInfo.size];
+ }
+
+ try {
+ encoderOutputBuffer.position(mVideoBufferInfo.offset);
+ encoderOutputBuffer.limit(mVideoBufferInfo.offset + mVideoBufferInfo.size);
+
+ encoderOutputBuffer.get(dataToWrite, dataOffset, mVideoBufferInfo.size);
+
+ if (mOutputListener != null) {
+ mOutputListener.sendFrame(dataToWrite, 0, dataToWrite.length, mVideoBufferInfo.presentationTimeUs);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ mVideoEncoder.releaseOutputBuffer(encoderStatus, false);
+
+ if ((mVideoBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ break; // out of while
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/sdl_android/src/main/java/com/smartdevicelink/protocol/WiProProtocol.java b/sdl_android/src/main/java/com/smartdevicelink/protocol/WiProProtocol.java
index cfe05a61a..72482d47e 100644
--- a/sdl_android/src/main/java/com/smartdevicelink/protocol/WiProProtocol.java
+++ b/sdl_android/src/main/java/com/smartdevicelink/protocol/WiProProtocol.java
@@ -14,7 +14,7 @@ import com.smartdevicelink.proxy.rpc.VideoStreamingFormat;
import com.smartdevicelink.proxy.rpc.enums.VideoStreamingCodec;
import com.smartdevicelink.proxy.rpc.enums.VideoStreamingProtocol;
import com.smartdevicelink.security.SdlSecurityBase;
-import com.smartdevicelink.streaming.VideoStreamingParams;
+import com.smartdevicelink.streaming.video.VideoStreamingParameters;
import com.smartdevicelink.util.BitConverter;
import com.smartdevicelink.util.DebugTool;
import com.smartdevicelink.util.Version;
@@ -489,7 +489,7 @@ public class WiProProtocol extends AbstractProtocol {
acceptedResolution.setResolutionWidth((Integer) packet.getTag(ControlFrameTags.Video.StartServiceACK.WIDTH));
acceptedFormat.setCodec(VideoStreamingCodec.valueForString((String) packet.getTag(ControlFrameTags.Video.StartServiceACK.VIDEO_CODEC)));
acceptedFormat.setProtocol(VideoStreamingProtocol.valueForString((String) packet.getTag(ControlFrameTags.Video.StartServiceACK.VIDEO_PROTOCOL)));
- VideoStreamingParams agreedVideoParams = session.getDesiredVideoParams();
+ VideoStreamingParameters agreedVideoParams = session.getDesiredVideoParams();
agreedVideoParams.setResolution(acceptedResolution);
agreedVideoParams.setFormat(acceptedFormat);
session.setAcceptedVideoParams(agreedVideoParams);
diff --git a/sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBase.java b/sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBase.java
index dc284dbae..e1e80d5a0 100644
--- a/sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBase.java
+++ b/sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBase.java
@@ -12,7 +12,6 @@ import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
-import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;
@@ -26,15 +25,20 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
+import android.annotation.TargetApi;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Looper;
+import android.os.SystemClock;
import android.telephony.TelephonyManager;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.util.SparseArray;
+import android.view.Display;
+import android.view.MotionEvent;
import android.view.Surface;
import com.smartdevicelink.Dispatcher.IDispatchingStrategy;
@@ -42,11 +46,11 @@ import com.smartdevicelink.Dispatcher.ProxyMessageDispatcher;
import com.smartdevicelink.SdlConnection.ISdlConnectionListener;
import com.smartdevicelink.SdlConnection.SdlConnection;
import com.smartdevicelink.SdlConnection.SdlSession;
+import com.smartdevicelink.encoder.VirtualDisplayEncoder;
import com.smartdevicelink.exception.SdlException;
import com.smartdevicelink.exception.SdlExceptionCause;
import com.smartdevicelink.marshal.JsonRPCMarshaller;
import com.smartdevicelink.protocol.ProtocolMessage;
-import com.smartdevicelink.protocol.enums.ControlFrameTags;
import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.protocol.enums.MessageType;
import com.smartdevicelink.protocol.enums.SessionType;
@@ -86,17 +90,17 @@ import com.smartdevicelink.proxy.rpc.enums.SdlDisconnectedReason;
import com.smartdevicelink.proxy.rpc.enums.SdlInterfaceAvailability;
import com.smartdevicelink.proxy.rpc.enums.SystemCapabilityType;
import com.smartdevicelink.proxy.rpc.enums.TextAlignment;
+import com.smartdevicelink.proxy.rpc.enums.TouchType;
import com.smartdevicelink.proxy.rpc.enums.UpdateMode;
-import com.smartdevicelink.proxy.rpc.enums.VideoStreamingCodec;
-import com.smartdevicelink.proxy.rpc.enums.VideoStreamingProtocol;
import com.smartdevicelink.proxy.rpc.listeners.OnPutFileUpdateListener;
import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener;
import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener;
import com.smartdevicelink.security.SdlSecurityBase;
-import com.smartdevicelink.streaming.AudioStreamingCodec;
-import com.smartdevicelink.streaming.AudioStreamingParams;
+import com.smartdevicelink.streaming.audio.AudioStreamingCodec;
+import com.smartdevicelink.streaming.audio.AudioStreamingParams;
import com.smartdevicelink.streaming.StreamRPCPacketizer;
-import com.smartdevicelink.streaming.VideoStreamingParams;
+import com.smartdevicelink.streaming.video.SdlRemoteDisplay;
+import com.smartdevicelink.streaming.video.VideoStreamingParameters;
import com.smartdevicelink.trace.SdlTrace;
import com.smartdevicelink.trace.TraceDeviceInfo;
import com.smartdevicelink.trace.enums.InterfaceActivityDirection;
@@ -105,6 +109,7 @@ import com.smartdevicelink.transport.SiphonServer;
import com.smartdevicelink.transport.enums.TransportType;
import com.smartdevicelink.util.DebugTool;
+
@SuppressWarnings({"WeakerAccess", "Convert2Diamond"})
public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase> {
// Used for calls to Android Log class.
@@ -112,9 +117,6 @@ public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase>
private static final String SDL_LIB_TRACE_KEY = "42baba60-eb57-11df-98cf-0800200c9a66";
private static final int PROX_PROT_VER_ONE = 1;
private static final int RESPONSE_WAIT_TIME = 2000;
-
- private static final VideoStreamingFormat VIDEO_STREAMING_FORMAT_H264_RAW = new VideoStreamingFormat(VideoStreamingCodec.H264,VideoStreamingProtocol.RAW);
- private static final VideoStreamingFormat VIDEO_STREAMING_FORMAT_H264_RTP = new VideoStreamingFormat(VideoStreamingCodec.H264,VideoStreamingProtocol.RTP);
private SdlSession sdlSession = null;
private proxyListenerType _proxyListener = null;
@@ -229,6 +231,7 @@ public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase>
protected SparseArray<OnRPCResponseListener> rpcResponseListeners = null;
protected SparseArray<CopyOnWriteArrayList<OnRPCNotificationListener>> rpcNotificationListeners = null;
+ protected VideoStreamingManager manager; //Will move to SdlSession once the class becomes public
// Interface broker
private SdlInterfaceBroker _interfaceBroker = null;
@@ -259,21 +262,18 @@ public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase>
@Override
public void addServiceListener(SessionType serviceType, ISdlServiceListener sdlServiceListener) {
- if(sdlSession!=null){
- sdlSession.addServiceListener(serviceType,sdlServiceListener);
- }
+ SdlProxyBase.this.addServiceListener(serviceType,sdlServiceListener);
}
@Override
public void removeServiceListener(SessionType serviceType, ISdlServiceListener sdlServiceListener) {
- if(sdlSession!=null){
- sdlSession.removeServiceListener(serviceType,sdlServiceListener);
- }
+ SdlProxyBase.this.removeServiceListener(serviceType,sdlServiceListener);
}
@Override
- public void startVideoService(VideoStreamingParams parameters, boolean encrypted) {
+ public void startVideoService(VideoStreamingParameters parameters, boolean encrypted) {
if(isConnected()){
+ sdlSession.setDesiredVideoParams(parameters);
sdlSession.startService(SessionType.NAV,sdlSession.getSessionId(),encrypted);
}
}
@@ -3654,31 +3654,50 @@ public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase>
}
private class CallableMethod implements Callable<Void> {
+
private final long waitTime;
- public CallableMethod(int timeInMillis){
+ public CallableMethod(int timeInMillis){
this.waitTime=timeInMillis;
- }
- @Override
- public Void call() {
- try {
- Thread.sleep(waitTime);
- } catch (InterruptedException e) {
- e.printStackTrace();
}
- return null;
- }
+ @Override
+ public Void call() {
+ try {
+ Thread.sleep(waitTime);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
}
+
public FutureTask<Void> createFutureTask(CallableMethod callMethod){
return new FutureTask<Void>(callMethod);
}
+
public ScheduledExecutorService createScheduler(){
return Executors.newSingleThreadScheduledExecutor();
- }
+ }
+
+ @SuppressWarnings("unused")
+ public void startService(SessionType serviceType, boolean isEncrypted){
+ sdlSession.startService(serviceType, sdlSession.getSessionId(), isEncrypted);
+ }
+
+ @SuppressWarnings("unused")
+ public void endService(SessionType serviceType){
+ sdlSession.endService(serviceType, sdlSession.getSessionId());
+ }
+
+
/**
+ * @deprecated
*Opens the video service (serviceType 11) and subsequently streams raw H264 video from an InputStream provided by the app
*@return true if service is opened successfully and stream is started, return false otherwise
+ * @see #startRemoteDisplayStream(Context, Class, VideoStreamingParameters, boolean) startRemoteDisplayStream
+ * @see #startVideoStream(boolean, VideoStreamingParameters) startVideoStream
+ * @see #createOpenGLInputSurface(int, int, int, int, int, boolean) createOpenGLInputSurface
*/
@SuppressWarnings("unused")
@Deprecated
@@ -3693,7 +3712,7 @@ public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase>
// When startH264() API is used, we will not send video format / width / height information
// with StartService. (Reasons: InputStream does not provide timestamp information so RTP
// cannot be used. startH264() does not provide with/height information.)
- VideoStreamingParams emptyParam = new VideoStreamingParams();
+ VideoStreamingParameters emptyParam = new VideoStreamingParameters();
emptyParam.setResolution(null);
emptyParam.setFormat(null);
sdlSession.setDesiredVideoParams(emptyParam);
@@ -3721,8 +3740,12 @@ public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase>
}
/**
+ * @deprecated
*Opens the video service (serviceType 11) and subsequently provides an OutputStream to the app to use for a raw H264 video stream
- *@return OutputStream if service is opened successfully and stream is started, return null otherwise
+ *@return OutputStream if service is opened successfully and stream is started, return null otherwise
+ * @see #startRemoteDisplayStream(Context, Class, VideoStreamingParameters, boolean) startRemoteDisplayStream
+ * @see #startVideoStream(boolean, VideoStreamingParameters) startVideoStream
+ * @see #createOpenGLInputSurface(int, int, int, int, int, boolean) createOpenGLInputSurface
*/
@SuppressWarnings("unused")
@Deprecated
@@ -3737,7 +3760,7 @@ public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase>
// When startH264() API is used, we will not send video format / width / height information
// with StartService. (Reasons: OutputStream does not provide timestamp information so RTP
// cannot be used. startH264() does not provide with/height information.)
- VideoStreamingParams emptyParam = new VideoStreamingParams();
+ VideoStreamingParameters emptyParam = new VideoStreamingParameters();
emptyParam.setResolution(null);
emptyParam.setFormat(null);
sdlSession.setDesiredVideoParams(emptyParam);
@@ -3903,20 +3926,18 @@ public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase>
/**
* Opens a video service (service type 11) and subsequently provides an IVideoStreamListener
- * to the app to send video data.
+ * to the app to send video data. The supplied VideoStreamingParameters will be set as desired paramaters
+ * that will be used to negotiate
*
* @param isEncrypted Specify true if packets on this service have to be encrypted
- * @param codec Video codec which will be used for streaming. Currently, only
- * VideoStreamingCodec.H264 is accepted.
- * @param width Width of the video in pixels
- * @param height Height of the video in pixels
+ * @param parameters Video streaming parameters including: codec which will be used for streaming (currently, only
+ * VideoStreamingCodec.H264 is accepted), height and width of the video in pixels.
*
* @return IVideoStreamListener interface if service is opened successfully and streaming is
* started, null otherwise
*/
@SuppressWarnings("unused")
- public IVideoStreamListener startVideoStream(boolean isEncrypted, VideoStreamingCodec codec,
- int width, int height) {
+ public IVideoStreamListener startVideoStream(boolean isEncrypted, VideoStreamingParameters parameters) {
if (sdlSession == null) {
DebugTool.logWarning("SdlSession is not created yet.");
return null;
@@ -3926,11 +3947,14 @@ public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase>
return null;
}
- VideoStreamingCodec[] codecs = {codec};
- VideoStreamingParams acceptedParams = tryStartVideoStream(codecs, width, height, -1, -1,
- -1, isEncrypted);
+ sdlSession.setDesiredVideoParams(parameters);
+
+ VideoStreamingParameters acceptedParams = tryStartVideoStream(isEncrypted, parameters);
if (acceptedParams != null) {
return sdlSession.startVideoStream();
+ } else if(getWiProVersion() < 5){
+ sdlSession.setAcceptedVideoParams(new VideoStreamingParameters());
+ return sdlSession.startVideoStream();
} else {
return null;
}
@@ -3942,7 +3966,7 @@ public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase>
*/
@SuppressWarnings("unused")
public boolean endVideoStream() {
- if (sdlSession == null) return false;
+ if (sdlSession == null){ return false; }
navServiceEndResponseReceived = false;
navServiceEndResponse = false;
@@ -3990,13 +4014,20 @@ public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase>
public Surface createOpenGLInputSurface(int frameRate, int iFrameInterval, int width,
int height, int bitrate, boolean isEncrypted) {
- if (sdlSession == null) return null;
- SdlConnection sdlConn = sdlSession.getSdlConnection();
- if (sdlConn == null) return null;
+ if (sdlSession == null || sdlSession.getSdlConnection() == null){
+ return null;
+ }
+
+ VideoStreamingParameters desired = new VideoStreamingParameters();
+ desired.setFrameRate(frameRate);
+ desired.setInterval(iFrameInterval);
+ ImageResolution resolution = new ImageResolution();
+ resolution.setResolutionWidth(width);
+ resolution.setResolutionHeight(height);
+ desired.setResolution(resolution);
+ desired.setBitrate(bitrate);
- VideoStreamingCodec[] codecs = {VideoStreamingCodec.H264};
- VideoStreamingParams acceptedParams = tryStartVideoStream(codecs, width, height, bitrate,
- frameRate, iFrameInterval, isEncrypted);
+ VideoStreamingParameters acceptedParams = tryStartVideoStream(isEncrypted, desired);
if (acceptedParams != null) {
return sdlSession.createOpenGLInputSurface(frameRate, iFrameInterval, width,
height, bitrate, SessionType.NAV, sdlSession.getSessionId());
@@ -4005,172 +4036,136 @@ public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase>
}
}
+ /**
+ * Starts streaming a remote display to the module if there is a connected session. This method of streaming requires the device to be on API level 19 or higher
+ * @param context a context that can be used to create the remote display
+ * @param remoteDisplay class object of the remote display. This class will be used to create an instance of the remote display and will be projected to the module
+ * @param parameters streaming parameters to be used when streaming. If null is sent in, the default/optimized options will be used.
+ * If you are unsure about what parameters to be used it is best to just send null and let the system determine what
+ * works best for the currently connected module.
+ *
+ * @param encrypted a flag of if the stream should be encrypted. Only set if you have a supplied encryption library that the module can understand.
+ */
+ @TargetApi(19)
+ public void startRemoteDisplayStream(Context context, final Class<? extends SdlRemoteDisplay> remoteDisplay, final VideoStreamingParameters parameters, final boolean encrypted){
+ if(getWiProVersion() >= 5 && !_systemCapabilityManager.isCapabilitySupported(SystemCapabilityType.VIDEO_STREAMING)){
+ Log.e(TAG, "Video streaming not supported on this module");
+ return;
+ }
+ //Create streaming manager
+ if(manager == null){
+ manager = new VideoStreamingManager(context,this._internalInterface);
+ }
+
+ if(parameters == null){
+ if(getWiProVersion() >= 5) {
+ _systemCapabilityManager.getCapability(SystemCapabilityType.VIDEO_STREAMING, new OnSystemCapabilityListener() {
+ @Override
+ public void onCapabilityRetrieved(Object capability) {
+ VideoStreamingParameters params = new VideoStreamingParameters();
+ params.update((VideoStreamingCapability)capability); //Streaming parameters are ready time to stream
+ sdlSession.setDesiredVideoParams(params);
+ manager.startVideoStreaming(remoteDisplay, params, encrypted);
+ }
+
+ @Override
+ public void onError(String info) {
+ Log.e(TAG, "Error retrieving video streaming capability: " + info);
+
+ }
+ });
+ }else{
+ //We just use default video streaming params
+ VideoStreamingParameters params = new VideoStreamingParameters();
+ DisplayCapabilities dispCap = (DisplayCapabilities)_systemCapabilityManager.getCapability(SystemCapabilityType.DISPLAY);
+ if(dispCap !=null){
+ params.setResolution(dispCap.getScreenParams().getImageResolution());
+ }
+ sdlSession.setDesiredVideoParams(params);
+ manager.startVideoStreaming(remoteDisplay,params, encrypted);
+ }
+ }else{
+ sdlSession.setDesiredVideoParams(parameters);
+ manager.startVideoStreaming(remoteDisplay,parameters, encrypted);
+ }
+ }
+
+ /**
+ * Stops the remote display stream if one has been started
+ */
+ public void stopRemoteDisplayStream(){
+ if(manager!=null){
+ manager.dispose();
+ }
+ manager = null;
+ }
+
/**
- * Try to open a video service by trying all available codec/protocols one by one.
+ * Try to open a video service by using the video streaming parameters supplied.
*
* Only information from codecs, width and height are used during video format negotiation.
*
- * @param codecs List of video codecs which app or proxy would like to use
- * @param width Width of the video in pixels
- * @param height Height of the video in pixels
- * @param bitrate Specified bitrate of the video
- * @param frameRate Specified rate of frames
- * @param iFrameInterval Specified interval
* @param isEncrypted Specify true if packets on this service have to be encrypted
+ * @param parameters VideoStreamingParameters that are desired. Does not guarantee this is what will be accepted.
*
* @return If the service is opened successfully, an instance of VideoStreamingParams is
* returned which contains accepted video format. If the service is opened with legacy
* mode (i.e. without any negotiation) then an instance of VideoStreamingParams is
- * returned, but its video format and resolution are null.
- * If the service was not opened then null is returned.
+ * returned. If the service was not opened then null is returned.
*/
@SuppressWarnings("unused")
- private VideoStreamingParams tryStartVideoStream(VideoStreamingCodec[] codecs,
- int width, int height,
- int bitrate, int frameRate, int iFrameInterval,
- boolean isEncrypted) {
+ private VideoStreamingParameters tryStartVideoStream(boolean isEncrypted, VideoStreamingParameters parameters) {
if (sdlSession == null) {
DebugTool.logWarning("SdlSession is not created yet.");
return null;
}
- if (codecs == null || codecs.length == 0) {
- DebugTool.logWarning("Video codec list is not supplied.");
+ if(getWiProVersion() >= 5 && !_systemCapabilityManager.isCapabilitySupported(SystemCapabilityType.VIDEO_STREAMING)){
+ DebugTool.logWarning("Module doesn't support video streaming.");
+ return null;
+ }
+ if (parameters == null) {
+ DebugTool.logWarning("Video parameters were not supplied.");
return null;
}
- List<VideoStreamingFormat> availableFormats = new ArrayList<>();
- for (VideoStreamingCodec codec : codecs) {
- if (codec == VideoStreamingCodec.H264) {
- availableFormats.add(VIDEO_STREAMING_FORMAT_H264_RTP);
- availableFormats.add(VIDEO_STREAMING_FORMAT_H264_RAW);
- } else {
- DebugTool.logInfo("Video codec " + codec +" is not supported.");
- }
- }
-
- VideoStreamingCapability videoStreamingCapabilities = null;
- if (_systemCapabilityManager != null) {
- videoStreamingCapabilities = (VideoStreamingCapability) _systemCapabilityManager.getCapability(
- SystemCapabilityType.VIDEO_STREAMING);
- }
-
- List<VideoStreamingParams> desiredParamsList = createDesiredVideoParams(
- videoStreamingCapabilities, frameRate, iFrameInterval, width, height, bitrate,
- availableFormats.toArray(new VideoStreamingFormat[0]));
+ sdlSession.setDesiredVideoParams(parameters);
- // If none of video formats are accepted then try StartService without parameter at last.
- // This also applies to the case where the system is legacy and capability isn't available.
- VideoStreamingParams emptyParam = new VideoStreamingParams();
- emptyParam.setResolution(null);
- emptyParam.setFormat(null);
- desiredParamsList.add(emptyParam);
+ navServiceStartResponseReceived = false;
+ navServiceStartResponse = false;
+ navServiceStartRejectedParams = null;
- for (VideoStreamingParams params : desiredParamsList) {
- sdlSession.setDesiredVideoParams(params);
+ sdlSession.startService(SessionType.NAV, sdlSession.getSessionId(), isEncrypted);
- navServiceStartResponseReceived = false;
- navServiceStartResponse = false;
- navServiceStartRejectedParams = null;
+ FutureTask<Void> fTask = createFutureTask(new CallableMethod(RESPONSE_WAIT_TIME));
+ ScheduledExecutorService scheduler = createScheduler();
+ scheduler.execute(fTask);
- sdlSession.startService(SessionType.NAV, sdlSession.getSessionId(), isEncrypted);
+ //noinspection StatementWithEmptyBody
+ while (!navServiceStartResponseReceived && !fTask.isDone());
+ scheduler.shutdown();
- FutureTask<Void> fTask = createFutureTask(new CallableMethod(RESPONSE_WAIT_TIME));
- ScheduledExecutorService scheduler = createScheduler();
- scheduler.execute(fTask);
+ if (navServiceStartResponse) {
+ return sdlSession.getAcceptedVideoParams();
+ }
- //noinspection StatementWithEmptyBody
- while (!navServiceStartResponseReceived && !fTask.isDone());
- scheduler.shutdown();
+ if (navServiceStartRejectedParams != null) {
+ StringBuilder builder = new StringBuilder();
+ for (String paramName : navServiceStartRejectedParams) {
+ if (builder.length() > 0) {
+ builder.append(", ");
+ }
+ builder.append(paramName);
+ }
- if (navServiceStartResponse) {
- if (!params.equals(emptyParam)) {
- DebugTool.logInfo("StartService for nav succeeded with params: " + params);
- } else {
- DebugTool.logInfo("StartService for nav succeeded in legacy mode.");
- }
- return params;
- }
+ DebugTool.logWarning("StartService for nav failed. Rejected params: " + builder.toString());
- if (navServiceStartRejectedParams != null) {
- StringBuilder builder = new StringBuilder();
- for (String paramName : navServiceStartRejectedParams) {
- if (builder.length() > 0) {
- builder.append(", ");
- }
- builder.append(paramName);
- }
- DebugTool.logWarning("StartService for nav failed. Rejected params: " + builder.toString());
+ } else {
+ DebugTool.logWarning("StartService for nav failed (rejected params not supplied)");
+ }
- if (!navServiceStartRejectedParams.contains(ControlFrameTags.Video.StartService.VIDEO_PROTOCOL)
- && !navServiceStartRejectedParams.contains(ControlFrameTags.Video.StartService.VIDEO_CODEC)) {
- // The reason of NACK is not protocol nor codec. There is no point retrying with
- // another video format, so we simply fail here.
- break;
- }
- } else {
- DebugTool.logWarning("StartService for nav failed (rejected params not supplied)");
- }
- }
return null;
}
- /**
- * Creates a list of VideoStreamingParams with video formats that are supported by both HMI and proxy
- *
- * Note: this method does not take care of matching video resolution. App should look into
- * HMI capability's preferredResolution and adjust width and height accordingly.
- *
- * @param hmiCapability HMI's capability information
- * @param frameRate specified rate of frames
- * @param frameInterval specified interval
- * @param width specified width of the video
- * @param height specified height of the video
- * @param bitrate specified bitrate of the video
- * @param availableFormats list of video formats supported by proxy
- * @return list of VideoStreamingParams instance. This list can be empty.
- */
- private static List<VideoStreamingParams> createDesiredVideoParams(
- VideoStreamingCapability hmiCapability,
- int frameRate, int frameInterval, int width, int height, int bitrate,
- VideoStreamingFormat[] availableFormats) {
- ArrayList<VideoStreamingFormat> formats = new ArrayList<>();
- if (hmiCapability != null && hmiCapability.getSupportedFormats() != null
- && availableFormats != null) {
- // supportedFormat is listed in HMI's preferred order
- for (VideoStreamingFormat supportedFormat : hmiCapability.getSupportedFormats()) {
- for (VideoStreamingFormat availableFormat : availableFormats) {
- if (supportedFormat.getProtocol() == availableFormat.getProtocol()
- && supportedFormat.getCodec() == availableFormat.getCodec()) {
- formats.add(supportedFormat);
- break;
- }
- }
- }
- }
-
- ArrayList<VideoStreamingParams> list = new ArrayList<>();
- for (VideoStreamingFormat format : formats) {
- VideoStreamingParams params = new VideoStreamingParams();
- ImageResolution resolution = new ImageResolution();
- resolution.setResolutionWidth(width);
- resolution.setResolutionHeight(height);
- params.setResolution(resolution);
- if (frameRate >= 0) {
- params.setFrameRate(frameRate);
- }
- if (bitrate >= 0) {
- params.setBitrate(bitrate);
- }
- if (frameInterval >= 0) {
- params.setInterval(frameInterval);
- }
- params.setFormat(format);
- list.add(params);
- }
-
- return list;
- }
-
/**
*Starts the MediaCodec encoder utilized in conjunction with the Surface returned via the createOpenGLInputSurface method
*/
@@ -6157,7 +6152,25 @@ public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase>
return sdlSession != null && sdlSession.isServiceProtected(sType);
}
-
+
+
+ public void addServiceListener(SessionType serviceType, ISdlServiceListener sdlServiceListener){
+ if(serviceType != null && sdlSession != null && sdlServiceListener != null){
+ sdlSession.addServiceListener(serviceType, sdlServiceListener);
+ }
+ }
+
+ public void removeServiceListener(SessionType serviceType, ISdlServiceListener sdlServiceListener){
+ if(serviceType != null && sdlSession != null && sdlServiceListener != null){
+ sdlSession.removeServiceListener(serviceType, sdlServiceListener);
+ }
+ }
+
+ @SuppressWarnings("unused")
+ public VideoStreamingParameters getAcceptedVideoParams(){
+ return sdlSession.getAcceptedVideoParams();
+ }
+
public IProxyListenerBase getProxyListener()
{
return _proxyListener;
@@ -6211,5 +6224,188 @@ public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase>
{
return sPoliciesURL;
}
-
+
+
+
+ /**
+ * VideoStreamingManager houses all the elements needed to create a scoped, streaming manager for video projection. It is only a private, instance
+ * dependant class at the moment until it can become public. Once the class is public and API defined, it will be moved into the SdlSession class
+ */
+ @TargetApi(19)
+ private class VideoStreamingManager implements ISdlServiceListener{
+ Context context;
+ ISdl internalInterface;
+ volatile VirtualDisplayEncoder encoder;
+ private Class<? extends SdlRemoteDisplay> remoteDisplayClass = null;
+ SdlRemoteDisplay remoteDisplay;
+ IVideoStreamListener streamListener;
+ float[] touchScalar = {1.0f,1.0f}; //x, y
+
+ public VideoStreamingManager(Context context,ISdl iSdl){
+ this.context = context;
+ this.internalInterface = iSdl;
+ encoder = new VirtualDisplayEncoder();
+ internalInterface.addServiceListener(SessionType.NAV,this);
+ //Take care of the touch events
+ internalInterface.addOnRPCNotificationListener(FunctionID.ON_TOUCH_EVENT, new OnRPCNotificationListener() {
+ @Override
+ public void onNotified(RPCNotification notification) {
+ if(notification !=null && remoteDisplay != null){
+ MotionEvent event = convertTouchEvent((OnTouchEvent)notification);
+ if(event!=null){
+ remoteDisplay.handleMotionEvent(event);
+ }
+ }
+ }
+ });
+ }
+
+ public void startVideoStreaming(Class<? extends SdlRemoteDisplay> remoteDisplayClass, VideoStreamingParameters parameters, boolean encrypted){
+ streamListener = startVideoStream(encrypted,parameters);
+ if(streamListener == null){
+ Log.e(TAG, "Error starting video service");
+ return;
+ }
+ this.remoteDisplayClass = remoteDisplayClass;
+ try {
+ encoder.init(context,streamListener,parameters);
+ //We are all set so we can start streaming at at this point
+ encoder.start();
+ //Encoder should be up and running
+ createRemoteDisplay(encoder.getVirtualDisplay());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ Log.d(TAG, parameters.toString());
+ }
+
+ public void stopStreaming(){
+ if(remoteDisplay!=null){
+ remoteDisplay.stop();
+ }
+ if(encoder!=null){
+ encoder.shutDown();
+ }
+ }
+
+ public void dispose(){
+ stopStreaming();
+ internalInterface.removeServiceListener(SessionType.NAV,this);
+ }
+
+ private void createRemoteDisplay(final Display disp){
+ try{
+ if (disp == null){
+ return;
+ }
+
+ // Dismiss the current presentation if the display has changed.
+ if (remoteDisplay != null && remoteDisplay.getDisplay() != disp) {
+ remoteDisplay.dismissPresentation();
+ }
+
+ FutureTask<Boolean> fTask = new FutureTask<Boolean>( new SdlRemoteDisplay.Creator(context, disp, remoteDisplay, remoteDisplayClass, new SdlRemoteDisplay.Callback(){
+ @Override
+ public void onCreated(SdlRemoteDisplay remoteDisplay) {
+ //Remote display has been created.
+ //Now is a good time to do parsing for spatial data
+ VideoStreamingManager.this.remoteDisplay = remoteDisplay;
+
+ //Get touch scalars
+ ImageResolution resolution = null;
+ if(getWiProVersion()>=5){ //At this point we should already have the capability
+ VideoStreamingCapability capability = (VideoStreamingCapability)_systemCapabilityManager.getCapability(SystemCapabilityType.VIDEO_STREAMING);
+ resolution = capability.getPreferredResolution();
+ }else {
+ DisplayCapabilities dispCap = (DisplayCapabilities) _systemCapabilityManager.getCapability(SystemCapabilityType.DISPLAY);
+ if (dispCap != null) {
+ resolution = (dispCap.getScreenParams().getImageResolution());
+ }
+ }
+ if(resolution != null){
+ DisplayMetrics displayMetrics = new DisplayMetrics();
+ disp.getMetrics(displayMetrics);
+ touchScalar[0] = ((float)displayMetrics.widthPixels) / resolution.getResolutionWidth();
+ touchScalar[1] = ((float)displayMetrics.heightPixels) / resolution.getResolutionHeight();
+ }
+
+ }
+
+ @Override
+ public void onInvalidated(SdlRemoteDisplay remoteDisplay) {
+ //Our view has been invalidated
+ //A good time to refresh spatial data
+ }
+ } ));
+ Thread showPresentation = new Thread(fTask);
+
+ showPresentation.start();
+ } catch (Exception ex) {
+ Log.e(TAG, "Unable to create Virtual Display.");
+ }
+ }
+
+ @Override
+ public void onServiceStarted(SdlSession session, SessionType type, boolean isEncrypted) {
+
+
+ }
+
+ @Override
+ public void onServiceEnded(SdlSession session, SessionType type) {
+ if(SessionType.NAV.equals(type)){
+ dispose();
+ }
+
+ }
+
+ @Override
+ public void onServiceError(SdlSession session, SessionType type, String reason) {
+
+ }
+
+ private MotionEvent convertTouchEvent(OnTouchEvent touchEvent){
+ List<TouchEvent> eventList = touchEvent.getEvent();
+ if (eventList == null || eventList.size() == 0) return null;
+
+ TouchType touchType = touchEvent.getType();
+ if (touchType == null){ return null;}
+
+ float x;
+ float y;
+
+ TouchEvent event = eventList.get(eventList.size() - 1);
+ List<TouchCoord> coordList = event.getTouchCoordinates();
+ if (coordList == null || coordList.size() == 0){ return null;}
+
+ TouchCoord coord = coordList.get(coordList.size() - 1);
+ if (coord == null){ return null;}
+
+ x = (coord.getX() * touchScalar[0]);
+ y = (coord.getY() * touchScalar[1]);
+
+ if (x == 0 && y == 0){ return null;}
+
+ int eventAction = MotionEvent.ACTION_DOWN;
+ long downTime = 0;
+
+ if (touchType == TouchType.BEGIN) {
+ downTime = SystemClock.uptimeMillis();
+ eventAction = MotionEvent.ACTION_DOWN;
+ }
+
+ long eventTime = SystemClock.uptimeMillis();
+ if (downTime == 0){ downTime = eventTime - 100;}
+
+ if (touchType == TouchType.MOVE) {
+ eventAction = MotionEvent.ACTION_MOVE;
+ }
+
+ if (touchType == TouchType.END) {
+ eventAction = MotionEvent.ACTION_UP;
+ }
+
+ return MotionEvent.obtain(downTime, eventTime, eventAction, x, y, 0);
+ }
+ }
} // end-class
diff --git a/sdl_android/src/main/java/com/smartdevicelink/proxy/interfaces/ISdl.java b/sdl_android/src/main/java/com/smartdevicelink/proxy/interfaces/ISdl.java
index a28a24b88..9177a146b 100644
--- a/sdl_android/src/main/java/com/smartdevicelink/proxy/interfaces/ISdl.java
+++ b/sdl_android/src/main/java/com/smartdevicelink/proxy/interfaces/ISdl.java
@@ -4,7 +4,7 @@ import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.protocol.enums.SessionType;
import com.smartdevicelink.proxy.RPCRequest;
import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener;
-import com.smartdevicelink.streaming.VideoStreamingParams;
+import com.smartdevicelink.streaming.video.VideoStreamingParameters;
/*
* Copyright (c) 2017 Livio, Inc.
@@ -75,7 +75,7 @@ public interface ISdl {
* @param parameters desired video streaming params for this sevice to be started with
* @param encrypted flag to start this service with encryption or not
*/
- void startVideoService(VideoStreamingParams parameters, boolean encrypted);
+ void startVideoService(VideoStreamingParameters parameters, boolean encrypted);
/**
* Stops the video service if open
diff --git a/sdl_android/src/main/java/com/smartdevicelink/streaming/VideoStreamingParams.java b/sdl_android/src/main/java/com/smartdevicelink/streaming/VideoStreamingParams.java
deleted file mode 100644
index 8984b822c..000000000
--- a/sdl_android/src/main/java/com/smartdevicelink/streaming/VideoStreamingParams.java
+++ /dev/null
@@ -1,105 +0,0 @@
-package com.smartdevicelink.streaming;
-
-import android.content.Intent;
-import android.util.DisplayMetrics;
-
-import com.smartdevicelink.proxy.rpc.ImageResolution;
-import com.smartdevicelink.proxy.rpc.VideoStreamingFormat;
-import com.smartdevicelink.proxy.rpc.enums.VideoStreamingCodec;
-import com.smartdevicelink.proxy.rpc.enums.VideoStreamingProtocol;
-
-public class VideoStreamingParams {
- private final VideoStreamingProtocol DEFAULT_PROTOCOL = VideoStreamingProtocol.RAW;
- private final VideoStreamingCodec DEFAULT_CODEC = VideoStreamingCodec.H264;
- private final int DEFAULT_WIDTH = 800;
- private final int DEFAULT_HEIGHT = 480;
- private final int DEFAULT_DENSITY = DisplayMetrics.DENSITY_HIGH;
- private final int DEFAULT_FRAMERATE = 24;
- private final int DEFAULT_BITRATE = 512000;
- private final int DEFAULT_INTERVAL = 5;
-
-
- private int displayDensity;
- private int frameRate;
- private int bitrate;
- private int interval;
- private ImageResolution resolution;
- private VideoStreamingFormat format;
-
- public VideoStreamingParams(){
- displayDensity = DEFAULT_DENSITY;
- frameRate = DEFAULT_FRAMERATE;
- bitrate = DEFAULT_BITRATE;
- interval = DEFAULT_INTERVAL;
- resolution = new ImageResolution();
- resolution.setResolutionWidth(DEFAULT_WIDTH);
- resolution.setResolutionHeight(DEFAULT_HEIGHT);
- format = new VideoStreamingFormat();
- format.setProtocol(DEFAULT_PROTOCOL);
- format.setCodec(DEFAULT_CODEC);
- }
-
- public VideoStreamingParams(int displayDensity, int frameRate, int bitrate, int interval,
- ImageResolution resolution, VideoStreamingFormat format){
- this.displayDensity = displayDensity;
- this.frameRate = frameRate;
- this.bitrate = bitrate;
- this.interval = interval;
- this.resolution = resolution;
- this.format = format;
- }
-
- public void setDisplayDensity(int displayDensity) {
- this.displayDensity = displayDensity;
- }
-
- public int getDisplayDensity() {
- return displayDensity;
- }
-
- public void setFrameRate(int frameRate) {
- this.frameRate = frameRate;
- }
-
- public int getFrameRate() {
- return frameRate;
- }
-
- public void setBitrate(int bitrate) {
- this.bitrate = bitrate;
- }
-
- public int getBitrate() {
- return bitrate;
- }
-
- public void setInterval(int interval) {
- this.interval = interval;
- }
-
- public int getInterval() {
- return interval;
- }
-
- public void setFormat(VideoStreamingFormat format){
- this.format = format;
- }
-
- public VideoStreamingFormat getFormat(){
- return format;
- }
-
- public void setResolution(ImageResolution resolution){
- this.resolution = resolution;
- }
-
- public ImageResolution getResolution() {
- return resolution;
- }
-
- @Override
- public String toString() {
- return "format: {" + String.valueOf(format) +
- "}, resolution: {" + String.valueOf(resolution) + "}";
- }
-} \ No newline at end of file
diff --git a/sdl_android/src/main/java/com/smartdevicelink/streaming/AudioStreamingCodec.java b/sdl_android/src/main/java/com/smartdevicelink/streaming/audio/AudioStreamingCodec.java
index 8e8cb3857..bf69a792b 100644
--- a/sdl_android/src/main/java/com/smartdevicelink/streaming/AudioStreamingCodec.java
+++ b/sdl_android/src/main/java/com/smartdevicelink/streaming/audio/AudioStreamingCodec.java
@@ -28,7 +28,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package com.smartdevicelink.streaming;
+package com.smartdevicelink.streaming.audio;
/**
* Enum for each type of audio streaming codec.
diff --git a/sdl_android/src/main/java/com/smartdevicelink/streaming/AudioStreamingLPCMParams.java b/sdl_android/src/main/java/com/smartdevicelink/streaming/audio/AudioStreamingLPCMParams.java
index 551a1f032..bde6abc4c 100644
--- a/sdl_android/src/main/java/com/smartdevicelink/streaming/AudioStreamingLPCMParams.java
+++ b/sdl_android/src/main/java/com/smartdevicelink/streaming/audio/AudioStreamingLPCMParams.java
@@ -28,7 +28,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package com.smartdevicelink.streaming;
+package com.smartdevicelink.streaming.audio;
/**
* A struct to hold LPCM specific audio format information.
diff --git a/sdl_android/src/main/java/com/smartdevicelink/streaming/AudioStreamingParams.java b/sdl_android/src/main/java/com/smartdevicelink/streaming/audio/AudioStreamingParams.java
index cd95ed95a..fe0f7a981 100644
--- a/sdl_android/src/main/java/com/smartdevicelink/streaming/AudioStreamingParams.java
+++ b/sdl_android/src/main/java/com/smartdevicelink/streaming/audio/AudioStreamingParams.java
@@ -28,7 +28,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package com.smartdevicelink.streaming;
+package com.smartdevicelink.streaming.audio;
/**
* A struct to hold audio format information that are common to codecs.
diff --git a/sdl_android/src/main/java/com/smartdevicelink/streaming/RTPH264Packetizer.java b/sdl_android/src/main/java/com/smartdevicelink/streaming/video/RTPH264Packetizer.java
index 2a7646579..6946d1fbb 100644
--- a/sdl_android/src/main/java/com/smartdevicelink/streaming/RTPH264Packetizer.java
+++ b/sdl_android/src/main/java/com/smartdevicelink/streaming/video/RTPH264Packetizer.java
@@ -28,7 +28,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-package com.smartdevicelink.streaming;
+package com.smartdevicelink.streaming.video;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -44,6 +44,8 @@ import com.smartdevicelink.protocol.enums.SessionType;
import com.smartdevicelink.proxy.interfaces.IVideoStreamListener;
import com.smartdevicelink.proxy.rpc.enums.VideoStreamingCodec;
import com.smartdevicelink.proxy.rpc.enums.VideoStreamingProtocol;
+import com.smartdevicelink.streaming.AbstractPacketizer;
+import com.smartdevicelink.streaming.IStreamListener;
/*
* Note for testing.
diff --git a/sdl_android/src/main/java/com/smartdevicelink/streaming/video/SdlRemoteDisplay.java b/sdl_android/src/main/java/com/smartdevicelink/streaming/video/SdlRemoteDisplay.java
new file mode 100644
index 000000000..8af6b1c59
--- /dev/null
+++ b/sdl_android/src/main/java/com/smartdevicelink/streaming/video/SdlRemoteDisplay.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2017 Livio, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the Livio Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.smartdevicelink.streaming.video;
+
+import android.annotation.TargetApi;
+import android.app.Presentation;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.view.Display;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+
+import java.lang.reflect.Constructor;
+import java.util.concurrent.Callable;
+
+/**
+ * SdlRemoteDisplay is an abstract class that should be extended by developers to creat their remote displays.
+ * All logic for UI events can be stored in their extension.
+ *
+ * <br><br> <b>NOTE:</b> When the UI changes (buttons appear, layouts change, etc) the developer should call {@link #invalidate()} to alert any
+ * other interfaces that are listening for those types of events.
+ */
+@TargetApi(17)
+public abstract class SdlRemoteDisplay extends Presentation {
+ private static final String TAG = "SdlRemoteDisplay";
+ private static final int REFRESH_RATE_MS = 50;
+
+ protected Window w;
+ protected View mainView;
+ protected Handler handler = new Handler();
+ protected Handler uiHandler = new Handler(Looper.getMainLooper());
+ protected Callback callback;
+
+ public SdlRemoteDisplay(Context context, Display display) {
+ super(context, display);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+
+ super.onCreate(savedInstanceState);
+ setTitle(TAG);
+
+ w = getWindow();
+
+ startRefreshTask();
+
+ w.setType(WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
+ }
+
+ protected void startRefreshTask() {
+ handler.postDelayed(mStartRefreshTaskCallback, REFRESH_RATE_MS);
+ }
+
+ protected void stopRefreshTask() {
+ handler.removeCallbacks(mStartRefreshTaskCallback);
+ }
+
+ protected Runnable mStartRefreshTaskCallback = new Runnable() {
+ public void run() {
+ if(mainView == null){
+ mainView = w.getDecorView().findViewById(android.R.id.content);
+ }
+ if (mainView != null) {
+ mainView.invalidate();
+ }
+
+ handler.postDelayed(this, REFRESH_RATE_MS);
+ }
+ };
+
+ @SuppressWarnings("unused")
+ public View getMainView(){
+ if(mainView == null){
+ mainView = w.getDecorView().findViewById(android.R.id.content);
+ }
+ return this.mainView;
+ }
+
+ @SuppressWarnings("unused")
+ public void invalidate(){
+ // let listeners know the view has been invalidated
+ if(callback != null){
+ callback.onInvalidated(this);
+ }
+ }
+
+ public void handleMotionEvent(final MotionEvent motionEvent){
+ uiHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mainView.dispatchTouchEvent(motionEvent);
+ }
+ });
+ }
+
+ public void stop(){
+ stopRefreshTask();
+ dismissPresentation();
+ }
+
+ public void dismissPresentation() {
+ uiHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ dismiss();
+ }
+ });
+ }
+ public interface Callback{
+ void onCreated(SdlRemoteDisplay remoteDisplay);
+ void onInvalidated(SdlRemoteDisplay remoteDisplay);
+ }
+
+ public static class Creator implements Callable<Boolean> {
+ private Context context;
+ private Display mDisplay;
+ boolean presentationShowError = false;
+ SdlRemoteDisplay remoteDisplay;
+ Class<? extends SdlRemoteDisplay> remoteDisplayClass;
+ private Handler uiHandler = new Handler(Looper.getMainLooper());
+ private Callback callback;
+
+
+ public Creator(Context context, Display display, SdlRemoteDisplay remoteDisplay, Class<? extends SdlRemoteDisplay> remoteDisplayClass, Callback callback){
+ this.context = context;
+ this.mDisplay = display;
+ this.remoteDisplay = remoteDisplay;
+ this.remoteDisplayClass = remoteDisplayClass;
+ this.callback = callback;
+ }
+
+ @Override
+ public Boolean call() {
+
+ uiHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ // Want to create presentation on UI thread so it finds the right Looper
+ // when setting up the Dialog.
+ if ((remoteDisplay == null) && (mDisplay != null))
+ {
+ try {
+ Constructor constructor = remoteDisplayClass.getConstructor(Context.class, Display.class);
+ remoteDisplay = (SdlRemoteDisplay) constructor.newInstance(context, mDisplay);
+ } catch (Exception e) {
+ e.printStackTrace();
+ Log.e(TAG, "Unable to create Presentation Class");
+ presentationShowError = true;
+ return;
+ }
+
+ try {
+ remoteDisplay.show();
+ remoteDisplay.callback = callback;
+ if(callback!=null){
+ callback.onCreated(remoteDisplay);
+ }
+
+ } catch (WindowManager.InvalidDisplayException ex) {
+ Log.e(TAG, "Couldn't show presentation! Display was removed in the meantime.", ex);
+ remoteDisplay = null;
+ presentationShowError = true;
+ }
+ }
+ }
+ });
+
+ return presentationShowError;
+ }
+
+ }
+}
diff --git a/sdl_android/src/main/java/com/smartdevicelink/streaming/video/VideoStreamingParameters.java b/sdl_android/src/main/java/com/smartdevicelink/streaming/video/VideoStreamingParameters.java
new file mode 100644
index 000000000..f565a22dc
--- /dev/null
+++ b/sdl_android/src/main/java/com/smartdevicelink/streaming/video/VideoStreamingParameters.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2017 Livio, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the Livio Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.smartdevicelink.streaming.video;
+
+import android.util.DisplayMetrics;
+
+import com.smartdevicelink.proxy.rpc.ImageResolution;
+import com.smartdevicelink.proxy.rpc.VideoStreamingCapability;
+import com.smartdevicelink.proxy.rpc.VideoStreamingFormat;
+import com.smartdevicelink.proxy.rpc.enums.VideoStreamingCodec;
+import com.smartdevicelink.proxy.rpc.enums.VideoStreamingProtocol;
+
+import java.util.List;
+
+@SuppressWarnings("FieldCanBeLocal")
+public class VideoStreamingParameters {
+ private final VideoStreamingProtocol DEFAULT_PROTOCOL = VideoStreamingProtocol.RAW;
+ private final VideoStreamingCodec DEFAULT_CODEC = VideoStreamingCodec.H264;
+ private final int DEFAULT_WIDTH = 1024;
+ private final int DEFAULT_HEIGHT = 576;
+ private final int DEFAULT_DENSITY = DisplayMetrics.DENSITY_HIGH;
+ private final int DEFAULT_FRAMERATE = 30;
+ private final int DEFAULT_BITRATE = 512000;
+ private final int DEFAULT_INTERVAL = 5;
+
+
+ private int displayDensity;
+ private int frameRate;
+ private int bitrate;
+ private int interval;
+ private ImageResolution resolution;
+ private VideoStreamingFormat format;
+
+ public VideoStreamingParameters(){
+ displayDensity = DEFAULT_DENSITY;
+ frameRate = DEFAULT_FRAMERATE;
+ bitrate = DEFAULT_BITRATE;
+ interval = DEFAULT_INTERVAL;
+ resolution = new ImageResolution();
+ resolution.setResolutionWidth(DEFAULT_WIDTH);
+ resolution.setResolutionHeight(DEFAULT_HEIGHT);
+ format = new VideoStreamingFormat();
+ format.setProtocol(DEFAULT_PROTOCOL);
+ format.setCodec(DEFAULT_CODEC);
+ }
+
+ public VideoStreamingParameters(int displayDensity, int frameRate, int bitrate, int interval,
+ ImageResolution resolution, VideoStreamingFormat format){
+ this.displayDensity = displayDensity;
+ this.frameRate = frameRate;
+ this.bitrate = bitrate;
+ this.interval = interval;
+ this.resolution = resolution;
+ this.format = format;
+ }
+
+ /**
+ * Will only copy values that are not null or are greater than 0
+ * @param params VideoStreamingParameters that should be copied into this new instants
+ */
+ @SuppressWarnings("unused")
+ public VideoStreamingParameters(VideoStreamingParameters params){
+ update(params);
+ }
+
+ /**
+ * Will only copy values that are not null or are greater than 0
+ * @param params VideoStreamingParameters that should be copied into this new instants
+ */
+ public void update(VideoStreamingParameters params){
+ if(params!=null) {
+ if (params.displayDensity > 0) {
+ this.displayDensity = params.displayDensity;
+ }
+ if (params.frameRate > 0) {
+ this.frameRate = params.frameRate;
+ }
+ if (params.bitrate > 0) {
+ this.bitrate = params.bitrate;
+ }
+ if (params.interval > 0) {
+ this.interval = params.interval;
+ }
+ if (params.resolution != null) {
+ if (params.resolution.getResolutionHeight() != null && params.resolution.getResolutionHeight() > 0) {
+ this.resolution.setResolutionHeight(params.resolution.getResolutionHeight());
+ }
+ if (params.resolution.getResolutionWidth() != null && params.resolution.getResolutionWidth() > 0) {
+ this.resolution.setResolutionWidth(params.resolution.getResolutionWidth());
+ }
+ }
+ if (params.format != null) {
+ this.format = params.format;
+ }
+ }
+ }
+
+ /**
+ * Update the values contained in the capability that should have been returned through the SystemCapabilityManager.
+ * This update will use the most preferred streaming format from the module.
+ * @param capability the video streaming capability returned from the SystemCapabilityManager
+ * @see com.smartdevicelink.proxy.SystemCapabilityManager
+ * @see VideoStreamingCapability
+ */
+ public void update(VideoStreamingCapability capability){
+ if(capability.getMaxBitrate()!=null){ this.bitrate = capability.getMaxBitrate(); }
+ ImageResolution resolution = capability.getPreferredResolution();
+ if(resolution!=null){
+ if(resolution.getResolutionHeight()!=null && resolution.getResolutionHeight() > 0){ this.resolution.setResolutionHeight(resolution.getResolutionHeight()); }
+ if(resolution.getResolutionWidth()!=null && resolution.getResolutionWidth() > 0){ this.resolution.setResolutionWidth(resolution.getResolutionWidth()); }
+ }
+ List<VideoStreamingFormat> formats = capability.getSupportedFormats();
+ if(formats != null && formats.size()>0){
+ this.format = formats.get(0);
+ }
+
+ }
+
+ @SuppressWarnings("unused")
+ public void setDisplayDensity(int displayDensity) {
+ this.displayDensity = displayDensity;
+ }
+
+ public int getDisplayDensity() {
+ return displayDensity;
+ }
+
+ public void setFrameRate(int frameRate) {
+ this.frameRate = frameRate;
+ }
+
+ public int getFrameRate() {
+ return frameRate;
+ }
+
+ public void setBitrate(int bitrate) {
+ this.bitrate = bitrate;
+ }
+
+ public int getBitrate() {
+ return bitrate;
+ }
+
+ public void setInterval(int interval) {
+ this.interval = interval;
+ }
+
+ public int getInterval() {
+ return interval;
+ }
+
+ public void setFormat(VideoStreamingFormat format){
+ this.format = format;
+ }
+
+ public VideoStreamingFormat getFormat(){
+ return format;
+ }
+
+ public void setResolution(ImageResolution resolution){
+ this.resolution = resolution;
+ }
+
+ public ImageResolution getResolution() {
+ return resolution;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("VideoStreamingParams - format: {");
+ builder.append(format.toString());
+ builder.append("}, resolution: {");
+ builder.append(resolution.getResolutionHeight());
+ builder.append(" , ");
+ builder.append(resolution.getResolutionWidth());
+ builder.append("}, frame rate {");
+ builder.append(frameRate);
+ builder.append("}, displayDensity{ ");
+ builder.append(displayDensity);
+ builder.append("}, bitrate");
+ builder.append(bitrate);
+ builder.append("}, IFrame interval{ ");
+ builder.append(interval);
+ builder.append("}");
+ return builder.toString();
+ }
+} \ No newline at end of file