diff options
22 files changed, 1534 insertions, 425 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/SdlEncoder.java b/sdl_android/src/main/java/com/smartdevicelink/encoder/SdlEncoder.java index 196f5d98c..bd1e0c364 100644 --- a/sdl_android/src/main/java/com/smartdevicelink/encoder/SdlEncoder.java +++ b/sdl_android/src/main/java/com/smartdevicelink/encoder/SdlEncoder.java @@ -176,22 +176,29 @@ public class SdlEncoder { Log.i(TAG, "H264 codec specific data not retrieved yet."); } } - // append SPS and PPS in front of every IDR NAL unit - if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0 - && mBufferInfo.size != 0 - && mH264CodecSpecificData != null) { - try { - mOutputStream.write(mH264CodecSpecificData, 0, - mH264CodecSpecificData.length); - } catch (Exception e) {} - } if (mBufferInfo.size != 0) { - byte[] dataToWrite = new byte[mBufferInfo.size]; - encoderOutputBuffers[encoderStatus].get(dataToWrite, - mBufferInfo.offset, mBufferInfo.size); + ByteBuffer encoderOutputBuffer = encoderOutputBuffers[encoderStatus]; + byte[] dataToWrite = null; + int dataOffset = 0; + + // append SPS and PPS in front of every IDR NAL unit + if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0 + && mH264CodecSpecificData != null) { + dataToWrite = new byte[mH264CodecSpecificData.length + mBufferInfo.size]; + System.arraycopy(mH264CodecSpecificData, 0, + dataToWrite, 0, mH264CodecSpecificData.length); + dataOffset = mH264CodecSpecificData.length; + } else { + dataToWrite = new byte[mBufferInfo.size]; + } try { + encoderOutputBuffer.position(mBufferInfo.offset); + encoderOutputBuffer.limit(mBufferInfo.offset + mBufferInfo.size); + + encoderOutputBuffer.get(dataToWrite, dataOffset, mBufferInfo.size); + if (mOutputStream != null) { mOutputStream.write(dataToWrite, 0, mBufferInfo.size); } else if (mOutputListener != null) { 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 |