From f15bbaf39e02dedc85d5b75e20206d968fc4a603 Mon Sep 17 00:00:00 2001 From: Joey Grover Date: Wed, 4 Oct 2017 16:30:55 -0400 Subject: Refactor streaming classes into more defined packages --- .../test/proxy/SystemCapabilityManagerTests.java | 7 +- .../test/streaming/RTPH264PacketizerTest.java | 18 +- .../smartdevicelink/SdlConnection/SdlSession.java | 6 +- .../encoder/VirtualDisplayEncoder.java | 5 +- .../smartdevicelink/protocol/WiProProtocol.java | 2 +- .../com/smartdevicelink/proxy/SdlProxyBase.java | 6 +- .../com/smartdevicelink/proxy/interfaces/ISdl.java | 2 +- .../streaming/AudioStreamingCodec.java | 49 -- .../streaming/AudioStreamingLPCMParams.java | 64 --- .../streaming/AudioStreamingParams.java | 55 --- .../streaming/RTPH264Packetizer.java | 505 -------------------- .../streaming/VideoStreamingParameters.java | 105 ----- .../streaming/audio/AudioStreamingCodec.java | 49 ++ .../streaming/audio/AudioStreamingLPCMParams.java | 64 +++ .../streaming/audio/AudioStreamingParams.java | 55 +++ .../streaming/video/RTPH264Packetizer.java | 507 +++++++++++++++++++++ .../streaming/video/VideoStreamingParameters.java | 105 +++++ 17 files changed, 800 insertions(+), 804 deletions(-) delete mode 100644 sdl_android/src/main/java/com/smartdevicelink/streaming/AudioStreamingCodec.java delete mode 100644 sdl_android/src/main/java/com/smartdevicelink/streaming/AudioStreamingLPCMParams.java delete mode 100644 sdl_android/src/main/java/com/smartdevicelink/streaming/AudioStreamingParams.java delete mode 100644 sdl_android/src/main/java/com/smartdevicelink/streaming/RTPH264Packetizer.java delete mode 100644 sdl_android/src/main/java/com/smartdevicelink/streaming/VideoStreamingParameters.java create mode 100644 sdl_android/src/main/java/com/smartdevicelink/streaming/audio/AudioStreamingCodec.java create mode 100644 sdl_android/src/main/java/com/smartdevicelink/streaming/audio/AudioStreamingLPCMParams.java create mode 100644 sdl_android/src/main/java/com/smartdevicelink/streaming/audio/AudioStreamingParams.java create mode 100644 sdl_android/src/main/java/com/smartdevicelink/streaming/video/RTPH264Packetizer.java create mode 100644 sdl_android/src/main/java/com/smartdevicelink/streaming/video/VideoStreamingParameters.java (limited to 'sdl_android/src') 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 702b83cad..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.VideoStreamingParameters; +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; diff --git a/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/RTPH264PacketizerTest.java b/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/RTPH264PacketizerTest.java index 70274444c..6d169809a 100644 --- a/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/RTPH264PacketizerTest.java +++ b/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/RTPH264PacketizerTest.java @@ -35,7 +35,7 @@ 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.transport.BTTransportConfig; import junit.framework.TestCase; @@ -46,7 +46,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 +309,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 +340,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 +372,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 +443,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 +472,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 +501,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/main/java/com/smartdevicelink/SdlConnection/SdlSession.java b/sdl_android/src/main/java/com/smartdevicelink/SdlConnection/SdlSession.java index f4a9fb97f..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.VideoStreamingParameters; +import com.smartdevicelink.streaming.video.VideoStreamingParameters; import com.smartdevicelink.transport.BaseTransportConfig; import com.smartdevicelink.transport.MultiplexTransport; import com.smartdevicelink.transport.enums.TransportType; @@ -58,6 +59,7 @@ 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> serviceListeners; diff --git a/sdl_android/src/main/java/com/smartdevicelink/encoder/VirtualDisplayEncoder.java b/sdl_android/src/main/java/com/smartdevicelink/encoder/VirtualDisplayEncoder.java index 4df5aedf7..619e2d54d 100644 --- a/sdl_android/src/main/java/com/smartdevicelink/encoder/VirtualDisplayEncoder.java +++ b/sdl_android/src/main/java/com/smartdevicelink/encoder/VirtualDisplayEncoder.java @@ -13,7 +13,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.SystemClock; -import android.util.DisplayMetrics; import android.util.Log; import android.view.Display; import android.view.MotionEvent; @@ -29,7 +28,7 @@ import com.smartdevicelink.proxy.rpc.TouchCoord; import com.smartdevicelink.proxy.rpc.TouchEvent; import com.smartdevicelink.proxy.rpc.VideoStreamingFormat; import com.smartdevicelink.proxy.rpc.enums.TouchType; -import com.smartdevicelink.streaming.VideoStreamingParameters; +import com.smartdevicelink.streaming.video.VideoStreamingParameters; import java.io.IOException; import java.io.OutputStream; @@ -39,8 +38,6 @@ import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; -import static android.R.attr.format; - @TargetApi(21) public class VirtualDisplayEncoder { private static final String TAG = "VirtualDisplayEncoder"; 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 d79dbe27f..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.VideoStreamingParameters; +import com.smartdevicelink.streaming.video.VideoStreamingParameters; import com.smartdevicelink.util.BitConverter; import com.smartdevicelink.util.DebugTool; import com.smartdevicelink.util.Version; 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 8614791d0..3c59257f3 100644 --- a/sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBase.java +++ b/sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBase.java @@ -93,10 +93,10 @@ 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.VideoStreamingParameters; +import com.smartdevicelink.streaming.video.VideoStreamingParameters; import com.smartdevicelink.trace.SdlTrace; import com.smartdevicelink.trace.TraceDeviceInfo; import com.smartdevicelink.trace.enums.InterfaceActivityDirection; 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 2d67f93d7..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.VideoStreamingParameters; +import com.smartdevicelink.streaming.video.VideoStreamingParameters; /* * Copyright (c) 2017 Livio, Inc. diff --git a/sdl_android/src/main/java/com/smartdevicelink/streaming/AudioStreamingCodec.java b/sdl_android/src/main/java/com/smartdevicelink/streaming/AudioStreamingCodec.java deleted file mode 100644 index 8e8cb3857..000000000 --- a/sdl_android/src/main/java/com/smartdevicelink/streaming/AudioStreamingCodec.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2017, Xevo 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 copyright holder 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; - -/** - * Enum for each type of audio streaming codec. - */ -public enum AudioStreamingCodec { - /** - * Linear-PCM without any compression. - */ - LPCM; - - public static AudioStreamingCodec valueForString(String value) { - try { - return valueOf(value); - } catch (Exception e) { - return null; - } - } -} diff --git a/sdl_android/src/main/java/com/smartdevicelink/streaming/AudioStreamingLPCMParams.java b/sdl_android/src/main/java/com/smartdevicelink/streaming/AudioStreamingLPCMParams.java deleted file mode 100644 index 551a1f032..000000000 --- a/sdl_android/src/main/java/com/smartdevicelink/streaming/AudioStreamingLPCMParams.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2017, Xevo 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 copyright holder 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; - -/** - * A struct to hold LPCM specific audio format information. - */ -public class AudioStreamingLPCMParams extends AudioStreamingParams { - /** - * Sample format of linear PCM data. - */ - public enum SampleFormat { - /** - * LPCM data is represented by 8-bit unsigned integers. Centerpoint is 128. - */ - LPCM_8BIT_UNSIGNED, - - /** - * LPCM data is represented by 16-bit signed integers, in little endian. - */ - LPCM_16BIT_SIGNED_LITTLE_ENDIAN, - } - - /** - * Sample format in which app will provide LPCM data to - * IAudioStreamListener.sendAudio() - *

- * This is reserved for future and not used right now. - */ - public SampleFormat sampleFormat; - - public AudioStreamingLPCMParams(SampleFormat sampleFormat, int samplingRate, int channels) { - super(samplingRate, channels); - this.sampleFormat = sampleFormat; - } -} diff --git a/sdl_android/src/main/java/com/smartdevicelink/streaming/AudioStreamingParams.java b/sdl_android/src/main/java/com/smartdevicelink/streaming/AudioStreamingParams.java deleted file mode 100644 index cd95ed95a..000000000 --- a/sdl_android/src/main/java/com/smartdevicelink/streaming/AudioStreamingParams.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2017, Xevo 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 copyright holder 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; - -/** - * A struct to hold audio format information that are common to codecs. - */ -public class AudioStreamingParams { - /** - * Sampling rate in Hz, e.g. 44100 - *

- * This is reserved for future and not used right now. - */ - public int samplingRate; - - /** - * Number of channels in the audio stream - *

- * This is reserved for future and not used right now. - */ - public int channels; - - public AudioStreamingParams(int samplingRate, int channels) { - this.samplingRate = samplingRate; - this.channels = channels; - } -} diff --git a/sdl_android/src/main/java/com/smartdevicelink/streaming/RTPH264Packetizer.java b/sdl_android/src/main/java/com/smartdevicelink/streaming/RTPH264Packetizer.java deleted file mode 100644 index 2a7646579..000000000 --- a/sdl_android/src/main/java/com/smartdevicelink/streaming/RTPH264Packetizer.java +++ /dev/null @@ -1,505 +0,0 @@ -/* - * Copyright (c) 2017, Xevo 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 copyright holder 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; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.Random; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; - -import com.smartdevicelink.SdlConnection.SdlConnection; -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.proxy.rpc.enums.VideoStreamingCodec; -import com.smartdevicelink.proxy.rpc.enums.VideoStreamingProtocol; - -/* - * Note for testing. - * The RTP stream generated by this packetizer can be tested with GStreamer (1.4 or later). - * Assuming that "VideoStreamPort" is configured as 5050 in smartDeviceLink.ini, here is the - * GStreamer pipeline that receives the stream, decode it and render it: - * - * $ gst-launch-1.0 souphttpsrc location=http://127.0.0.1:5050 ! "application/x-rtp-stream" ! rtpstreamdepay ! "application/x-rtp,media=(string)video,clock-rate=90000,encoding-name=(string)H264" ! rtph264depay ! "video/x-h264, stream-format=(string)avc, alignment=(string)au" ! avdec_h264 ! autovideosink sync=false - */ - -/** - * This class receives H.264 byte stream (in Annex-B format), parses it, construct RTP packets - * from it based on RFC 6184, then frame the packets based on RFC 4571. - * The primary purpose of using RTP is to carry timestamp information along with the data. - * - * @author Sho Amano - */ -public class RTPH264Packetizer extends AbstractPacketizer implements IVideoStreamListener, Runnable { - - // Approximate size of data that mOutputQueue can hold in bytes. - // By adding a buffer, we accept underlying transport being stuck for a short time. By setting - // a limit of the buffer size, we avoid buffer overflows when underlying transport is too slow. - private static final int MAX_QUEUE_SIZE = 256 * 1024; - - private static final int FRAME_LENGTH_LEN = 2; - private static final int MAX_RTP_PACKET_SIZE = 65535; // because length field is two bytes (RFC 4571) - private static final int RTP_HEADER_LEN = 12; - private static final byte DEFAULT_RTP_PAYLOAD_TYPE = 96; - private static final int FU_INDICATOR_LEN = 1; - private static final int FU_HEADER_LEN = 1; - private static final byte TYPE_FU_A = 28; - - // To align with StreamPacketizer class - private final static int TLS_MAX_RECORD_SIZE = 16384; - private final static int TLS_RECORD_HEADER_SIZE = 5; - private final static int TLS_RECORD_MES_AUTH_CDE_SIZE = 32; - private final static int TLS_MAX_RECORD_PADDING_SIZE = 256; - - private final static int MAX_DATA_SIZE_FOR_ENCRYPTED_SERVICE = - TLS_MAX_RECORD_SIZE - TLS_RECORD_HEADER_SIZE - TLS_RECORD_MES_AUTH_CDE_SIZE- TLS_MAX_RECORD_PADDING_SIZE; - - private boolean mServiceProtected; - private Thread mThread; - private BlockingQueue mOutputQueue; - private volatile boolean mPaused; - private boolean mWaitForIDR; - private NALUnitReader mNALUnitReader; - private byte mPayloadType = 0; - private int mSSRC = 0; - private char mSequenceNum = 0; - private int mInitialPTS = 0; - - /** - * Constructor - * - * @param streamListener The listener which this packetizer outputs SDL frames to - * @param serviceType The value of "Service Type" field in SDL frames - * @param sessionID The value of "Session ID" field in SDL frames - * @param session The SdlSession instance that this packetizer belongs to - */ - public RTPH264Packetizer(IStreamListener streamListener, - SessionType serviceType, byte sessionID, SdlSession session) throws IOException { - - super(streamListener, null, serviceType, sessionID, session); - - mServiceProtected = session.isServiceProtected(_serviceType); - - bufferSize = (int)this._session.getMtu(SessionType.NAV); - if (bufferSize == 0) { - // fail safe - bufferSize = MAX_DATA_SIZE_FOR_ENCRYPTED_SERVICE; - } - if (mServiceProtected && bufferSize > MAX_DATA_SIZE_FOR_ENCRYPTED_SERVICE) { - bufferSize = MAX_DATA_SIZE_FOR_ENCRYPTED_SERVICE; - } - - mOutputQueue = new LinkedBlockingQueue(MAX_QUEUE_SIZE / bufferSize); - mNALUnitReader = new NALUnitReader(); - mPayloadType = DEFAULT_RTP_PAYLOAD_TYPE; - - Random r = new Random(); - mSSRC = r.nextInt(); - - // initial value of the sequence number and timestamp should be random ([5.1] in RFC3550) - mSequenceNum = (char)r.nextInt(65536); - mInitialPTS = r.nextInt(); - } - - /** - * Sets the Payload Type (PT) of RTP header field. - * - * Use this method if PT needs to be specified. The value should be between 0 and 127. - * Otherwise, a default value (96) is used. - * - * @param type A value indicating the Payload Type - */ - public void setPayloadType(byte type) { - if (type >= 0 && type <= 127) { - mPayloadType = type; - } else { - mPayloadType = DEFAULT_RTP_PAYLOAD_TYPE; - } - } - - /** - * Sets the SSRC of RTP header field. - * - * Use this method if SSRC needs to be specified. Otherwise, a random value is generated and - * used. - * - * @param ssrc An integer value representing SSRC - */ - public void setSSRC(int ssrc) { - mSSRC = ssrc; - } - - /** - * Starts this packetizer. - * - * It is recommended that the video encoder is started after the packetizer is started. - */ - @Override - public void start() throws IOException { - if (mThread != null) { - return; - } - - mThread = new Thread(this); - mThread.start(); - } - - /** - * Stops this packetizer. - * - * It is recommended that the video encoder is stopped prior to the packetizer. - */ - @Override - public void stop() { - if (mThread == null) { - return; - } - - mThread.interrupt(); - mThread = null; - - mPaused = false; - mWaitForIDR = false; - mOutputQueue.clear(); - } - - /** - * Pauses this packetizer. - * - * This pauses the packetizer but does not pause the video encoder. - */ - @Override - public void pause() { - mPaused = true; - } - - /** - * Resumes this packetizer. - */ - @Override - public void resume() { - mWaitForIDR = true; - mPaused = false; - } - - /** - * The thread routine. - */ - public void run() { - SdlConnection connection = _session.getSdlConnection(); - - while (mThread != null && !mThread.isInterrupted()) { - ByteBuffer frame; - try { - frame = mOutputQueue.take(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - break; - } - - while (frame.hasRemaining()) { - int len = frame.remaining() > bufferSize ? bufferSize : frame.remaining(); - - ProtocolMessage pm = new ProtocolMessage(); - pm.setSessionID(_rpcSessionID); - pm.setSessionType(_serviceType); - pm.setFunctionID(0); - pm.setCorrID(0); - pm.setData(frame.array(), frame.arrayOffset() + frame.position(), len); - pm.setPayloadProtected(mServiceProtected); - - _streamListener.sendStreamPacket(pm); - - frame.position(frame.position() + len); - } - } - - // XXX: This is added to sync with StreamPacketizer. Actually it shouldn't be here since - // it's confusing that a packetizer takes care of End Service request. - if (connection != null) { - connection.endService(_serviceType, _rpcSessionID); - } - } - - /** - * Called by the app and encoder. - * - * @see com.smartdevicelink.proxy.interfaces.IVideoStreamListener#sendFrame(byte[], int, int, long) - */ - @Override - public void sendFrame(byte[] data, int offset, int length, long presentationTimeUs) - throws ArrayIndexOutOfBoundsException { - mNALUnitReader.init(data, offset, length); - onEncoderOutput(mNALUnitReader, presentationTimeUs); - } - - /** - * Called by the app and encoder. - * - * @see com.smartdevicelink.proxy.interfaces.IVideoStreamListener#sendFrame(ByteBuffer, long) - */ - @Override - public void sendFrame(ByteBuffer data, long presentationTimeUs) { - mNALUnitReader.init(data); - onEncoderOutput(mNALUnitReader, presentationTimeUs); - } - - private void onEncoderOutput(NALUnitReader nalUnitReader, long ptsInUs) { - if (mPaused) { - return; - } - - ByteBuffer nalUnit; - - while ((nalUnit = nalUnitReader.getNalUnit()) != null) { - if (mWaitForIDR) { - if (isIDR(nalUnit)) { - mWaitForIDR = false; - } else { - continue; - } - } - outputRTPFrames(nalUnit, ptsInUs, nalUnitReader.hasConsumedAll()); - } - } - - private boolean outputRTPFrames(ByteBuffer nalUnit, long ptsInUs, boolean isLast) { - if (RTP_HEADER_LEN + nalUnit.remaining() > MAX_RTP_PACKET_SIZE) { - // Split into multiple Fragmentation Units ([5.8] in RFC 6184) - byte firstByte = nalUnit.get(); - boolean firstFragment = true; - boolean lastFragment = false; - - while (nalUnit.remaining() > 0) { - int payloadLength = MAX_RTP_PACKET_SIZE - (RTP_HEADER_LEN + FU_INDICATOR_LEN + FU_HEADER_LEN); - if (nalUnit.remaining() <= payloadLength) { - payloadLength = nalUnit.remaining(); - lastFragment = true; - } - - ByteBuffer frame = allocateRTPFrame(FU_INDICATOR_LEN + FU_HEADER_LEN + payloadLength, - false, isLast, ptsInUs); - // FU indicator - frame.put((byte)((firstByte & 0xE0) | TYPE_FU_A)); - // FU header - frame.put((byte)((firstFragment ? 0x80 : lastFragment ? 0x40 : 0) | (firstByte & 0x1F))); - // FU payload - frame.put(nalUnit.array(), nalUnit.position(), payloadLength); - nalUnit.position(nalUnit.position() + payloadLength); - frame.flip(); - - try { - mOutputQueue.put(frame); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - return false; - } - - firstFragment = false; - } - } else { - // Use Single NAL Unit Packet ([5.6] in RFC 6184) - ByteBuffer frame = allocateRTPFrame(nalUnit.remaining(), false, isLast, ptsInUs); - frame.put(nalUnit); - frame.flip(); - - try { - mOutputQueue.put(frame); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - return false; - } - } - - return true; - } - - private ByteBuffer allocateRTPFrame(int rtpPayloadLen, - boolean hasPadding, boolean isLast, long ptsInUs) { - if (rtpPayloadLen <= 0) { - throw new IllegalArgumentException("Invalid rtpPayloadLen value: " + rtpPayloadLen); - } - if (ptsInUs < 0) { - throw new IllegalArgumentException("Invalid ptsInUs value: " + ptsInUs); - } - - int packetLength = RTP_HEADER_LEN + rtpPayloadLen; - if (packetLength > MAX_RTP_PACKET_SIZE) { - throw new IllegalArgumentException("Invalid rtpPayloadLen value: " + rtpPayloadLen); - } - int ptsIn90kHz = (int)(ptsInUs * 9 / 100) + mInitialPTS; - - ByteBuffer frame = ByteBuffer.allocate(FRAME_LENGTH_LEN + packetLength); - frame.order(ByteOrder.BIG_ENDIAN); - frame.putShort((short)packetLength); - - // Version = 2, Padding = hasPadding, Extension = 0, CSRC count = 0 - frame.put((byte)(0x80 | (hasPadding ? 0x20 : 0))) - // Marker = isLast, Payload type = mPayloadType - .put((byte)((isLast ? 0x80 : 0) | (mPayloadType & 0x7F))) - .putChar(mSequenceNum) - .putInt(ptsIn90kHz) - .putInt(mSSRC); - - if (frame.position() != FRAME_LENGTH_LEN + RTP_HEADER_LEN) { - throw new RuntimeException("Data size in ByteBuffer mismatch"); - } - - mSequenceNum++; - return frame; - } - - private static boolean isIDR(ByteBuffer nalUnit) { - if (nalUnit == null || !nalUnit.hasRemaining()) { - throw new IllegalArgumentException("Invalid nalUnit arg"); - } - - byte nalUnitType = (byte)(nalUnit.get(nalUnit.position()) & 0x1F); - return nalUnitType == 5; - } - - - private static int SKIP_TABLE[] = new int[256]; - static { - // Sunday's quick search algorithm is used to find the start code. - // Prepare the table (SKIP_TABLE[0] = 2, SKIP_TABLE[1] = 1 and other elements will be 4). - byte[] NAL_UNIT_START_CODE = {0, 0, 1}; - int searchStringLen = NAL_UNIT_START_CODE.length; - for (int i = 0; i < SKIP_TABLE.length; i++) { - SKIP_TABLE[i] = searchStringLen + 1; - } - for (int i = 0; i < searchStringLen; i++) { - SKIP_TABLE[NAL_UNIT_START_CODE[i] & 0xFF] = searchStringLen - i; - } - } - - private class NALUnitReader { - private byte[] mData; - private int mOffset; - private int mLimit; - - NALUnitReader() { - } - - void init(byte[] data) { - mData = data; - mOffset = 0; - mLimit = data.length; - } - - void init(byte[] data, int offset, int length) throws ArrayIndexOutOfBoundsException { - if (offset < 0 || offset > data.length || length <= 0 || offset + length > data.length) { - throw new ArrayIndexOutOfBoundsException(); - } - mData = data; - mOffset = offset; - mLimit = offset + length; - } - - void init(ByteBuffer data) { - if (data == null || data.remaining() == 0) { - mData = null; - mOffset = 0; - mLimit = 0; - return; - } - - if (data.hasArray()) { - mData = data.array(); - mOffset = data.position() + data.arrayOffset(); - mLimit = mOffset + data.remaining(); - - // mark the buffer as consumed - data.position(data.position() + data.remaining()); - } else { - byte[] buffer = new byte[data.remaining()]; - data.get(buffer); - - mData = buffer; - mOffset = 0; - mLimit = buffer.length; - } - } - - ByteBuffer getNalUnit() { - if (hasConsumedAll()) { - return null; - } - - int pos = mOffset; - int start = -1; - - while (mLimit - pos >= 3) { - if (mData[pos] == 0 && mData[pos+1] == 0 && mData[pos+2] == 1) { - if (start != -1) { - // We've found a start code, a NAL unit and then another start code. - mOffset = pos; - // remove 0x00s in front of the start code - while (pos > start && mData[pos-1] == 0) { - pos--; - } - if (pos > start) { - return ByteBuffer.wrap(mData, start, pos - start); - } else { - // No NAL unit between two start codes?! Forget it and search for - // another start code. - pos = mOffset; - } - } - // This is the first start code. - pos += 3; - start = pos; - } else { - try { - pos += SKIP_TABLE[mData[pos+3] & 0xFF]; - } catch (ArrayIndexOutOfBoundsException e) { - break; - } - } - } - - mOffset = mLimit; - if (start != -1 && mLimit > start) { - // We've found a start code and then reached to the end of array. - return ByteBuffer.wrap(mData, start, mLimit - start); - } - // A start code was not found - return null; - } - - boolean hasConsumedAll() { - return (mData == null) || (mLimit - mOffset < 4); - } - } -} diff --git a/sdl_android/src/main/java/com/smartdevicelink/streaming/VideoStreamingParameters.java b/sdl_android/src/main/java/com/smartdevicelink/streaming/VideoStreamingParameters.java deleted file mode 100644 index cac4ebbb8..000000000 --- a/sdl_android/src/main/java/com/smartdevicelink/streaming/VideoStreamingParameters.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 VideoStreamingParameters { - 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 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; - } - - 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/audio/AudioStreamingCodec.java b/sdl_android/src/main/java/com/smartdevicelink/streaming/audio/AudioStreamingCodec.java new file mode 100644 index 000000000..bf69a792b --- /dev/null +++ b/sdl_android/src/main/java/com/smartdevicelink/streaming/audio/AudioStreamingCodec.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2017, Xevo 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 copyright holder 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.audio; + +/** + * Enum for each type of audio streaming codec. + */ +public enum AudioStreamingCodec { + /** + * Linear-PCM without any compression. + */ + LPCM; + + public static AudioStreamingCodec valueForString(String value) { + try { + return valueOf(value); + } catch (Exception e) { + return null; + } + } +} diff --git a/sdl_android/src/main/java/com/smartdevicelink/streaming/audio/AudioStreamingLPCMParams.java b/sdl_android/src/main/java/com/smartdevicelink/streaming/audio/AudioStreamingLPCMParams.java new file mode 100644 index 000000000..bde6abc4c --- /dev/null +++ b/sdl_android/src/main/java/com/smartdevicelink/streaming/audio/AudioStreamingLPCMParams.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2017, Xevo 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 copyright holder 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.audio; + +/** + * A struct to hold LPCM specific audio format information. + */ +public class AudioStreamingLPCMParams extends AudioStreamingParams { + /** + * Sample format of linear PCM data. + */ + public enum SampleFormat { + /** + * LPCM data is represented by 8-bit unsigned integers. Centerpoint is 128. + */ + LPCM_8BIT_UNSIGNED, + + /** + * LPCM data is represented by 16-bit signed integers, in little endian. + */ + LPCM_16BIT_SIGNED_LITTLE_ENDIAN, + } + + /** + * Sample format in which app will provide LPCM data to + * IAudioStreamListener.sendAudio() + *

+ * This is reserved for future and not used right now. + */ + public SampleFormat sampleFormat; + + public AudioStreamingLPCMParams(SampleFormat sampleFormat, int samplingRate, int channels) { + super(samplingRate, channels); + this.sampleFormat = sampleFormat; + } +} diff --git a/sdl_android/src/main/java/com/smartdevicelink/streaming/audio/AudioStreamingParams.java b/sdl_android/src/main/java/com/smartdevicelink/streaming/audio/AudioStreamingParams.java new file mode 100644 index 000000000..fe0f7a981 --- /dev/null +++ b/sdl_android/src/main/java/com/smartdevicelink/streaming/audio/AudioStreamingParams.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017, Xevo 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 copyright holder 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.audio; + +/** + * A struct to hold audio format information that are common to codecs. + */ +public class AudioStreamingParams { + /** + * Sampling rate in Hz, e.g. 44100 + *

+ * This is reserved for future and not used right now. + */ + public int samplingRate; + + /** + * Number of channels in the audio stream + *

+ * This is reserved for future and not used right now. + */ + public int channels; + + public AudioStreamingParams(int samplingRate, int channels) { + this.samplingRate = samplingRate; + this.channels = channels; + } +} diff --git a/sdl_android/src/main/java/com/smartdevicelink/streaming/video/RTPH264Packetizer.java b/sdl_android/src/main/java/com/smartdevicelink/streaming/video/RTPH264Packetizer.java new file mode 100644 index 000000000..6946d1fbb --- /dev/null +++ b/sdl_android/src/main/java/com/smartdevicelink/streaming/video/RTPH264Packetizer.java @@ -0,0 +1,507 @@ +/* + * Copyright (c) 2017, Xevo 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 copyright holder 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 java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Random; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +import com.smartdevicelink.SdlConnection.SdlConnection; +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.proxy.rpc.enums.VideoStreamingCodec; +import com.smartdevicelink.proxy.rpc.enums.VideoStreamingProtocol; +import com.smartdevicelink.streaming.AbstractPacketizer; +import com.smartdevicelink.streaming.IStreamListener; + +/* + * Note for testing. + * The RTP stream generated by this packetizer can be tested with GStreamer (1.4 or later). + * Assuming that "VideoStreamPort" is configured as 5050 in smartDeviceLink.ini, here is the + * GStreamer pipeline that receives the stream, decode it and render it: + * + * $ gst-launch-1.0 souphttpsrc location=http://127.0.0.1:5050 ! "application/x-rtp-stream" ! rtpstreamdepay ! "application/x-rtp,media=(string)video,clock-rate=90000,encoding-name=(string)H264" ! rtph264depay ! "video/x-h264, stream-format=(string)avc, alignment=(string)au" ! avdec_h264 ! autovideosink sync=false + */ + +/** + * This class receives H.264 byte stream (in Annex-B format), parses it, construct RTP packets + * from it based on RFC 6184, then frame the packets based on RFC 4571. + * The primary purpose of using RTP is to carry timestamp information along with the data. + * + * @author Sho Amano + */ +public class RTPH264Packetizer extends AbstractPacketizer implements IVideoStreamListener, Runnable { + + // Approximate size of data that mOutputQueue can hold in bytes. + // By adding a buffer, we accept underlying transport being stuck for a short time. By setting + // a limit of the buffer size, we avoid buffer overflows when underlying transport is too slow. + private static final int MAX_QUEUE_SIZE = 256 * 1024; + + private static final int FRAME_LENGTH_LEN = 2; + private static final int MAX_RTP_PACKET_SIZE = 65535; // because length field is two bytes (RFC 4571) + private static final int RTP_HEADER_LEN = 12; + private static final byte DEFAULT_RTP_PAYLOAD_TYPE = 96; + private static final int FU_INDICATOR_LEN = 1; + private static final int FU_HEADER_LEN = 1; + private static final byte TYPE_FU_A = 28; + + // To align with StreamPacketizer class + private final static int TLS_MAX_RECORD_SIZE = 16384; + private final static int TLS_RECORD_HEADER_SIZE = 5; + private final static int TLS_RECORD_MES_AUTH_CDE_SIZE = 32; + private final static int TLS_MAX_RECORD_PADDING_SIZE = 256; + + private final static int MAX_DATA_SIZE_FOR_ENCRYPTED_SERVICE = + TLS_MAX_RECORD_SIZE - TLS_RECORD_HEADER_SIZE - TLS_RECORD_MES_AUTH_CDE_SIZE- TLS_MAX_RECORD_PADDING_SIZE; + + private boolean mServiceProtected; + private Thread mThread; + private BlockingQueue mOutputQueue; + private volatile boolean mPaused; + private boolean mWaitForIDR; + private NALUnitReader mNALUnitReader; + private byte mPayloadType = 0; + private int mSSRC = 0; + private char mSequenceNum = 0; + private int mInitialPTS = 0; + + /** + * Constructor + * + * @param streamListener The listener which this packetizer outputs SDL frames to + * @param serviceType The value of "Service Type" field in SDL frames + * @param sessionID The value of "Session ID" field in SDL frames + * @param session The SdlSession instance that this packetizer belongs to + */ + public RTPH264Packetizer(IStreamListener streamListener, + SessionType serviceType, byte sessionID, SdlSession session) throws IOException { + + super(streamListener, null, serviceType, sessionID, session); + + mServiceProtected = session.isServiceProtected(_serviceType); + + bufferSize = (int)this._session.getMtu(SessionType.NAV); + if (bufferSize == 0) { + // fail safe + bufferSize = MAX_DATA_SIZE_FOR_ENCRYPTED_SERVICE; + } + if (mServiceProtected && bufferSize > MAX_DATA_SIZE_FOR_ENCRYPTED_SERVICE) { + bufferSize = MAX_DATA_SIZE_FOR_ENCRYPTED_SERVICE; + } + + mOutputQueue = new LinkedBlockingQueue(MAX_QUEUE_SIZE / bufferSize); + mNALUnitReader = new NALUnitReader(); + mPayloadType = DEFAULT_RTP_PAYLOAD_TYPE; + + Random r = new Random(); + mSSRC = r.nextInt(); + + // initial value of the sequence number and timestamp should be random ([5.1] in RFC3550) + mSequenceNum = (char)r.nextInt(65536); + mInitialPTS = r.nextInt(); + } + + /** + * Sets the Payload Type (PT) of RTP header field. + * + * Use this method if PT needs to be specified. The value should be between 0 and 127. + * Otherwise, a default value (96) is used. + * + * @param type A value indicating the Payload Type + */ + public void setPayloadType(byte type) { + if (type >= 0 && type <= 127) { + mPayloadType = type; + } else { + mPayloadType = DEFAULT_RTP_PAYLOAD_TYPE; + } + } + + /** + * Sets the SSRC of RTP header field. + * + * Use this method if SSRC needs to be specified. Otherwise, a random value is generated and + * used. + * + * @param ssrc An integer value representing SSRC + */ + public void setSSRC(int ssrc) { + mSSRC = ssrc; + } + + /** + * Starts this packetizer. + * + * It is recommended that the video encoder is started after the packetizer is started. + */ + @Override + public void start() throws IOException { + if (mThread != null) { + return; + } + + mThread = new Thread(this); + mThread.start(); + } + + /** + * Stops this packetizer. + * + * It is recommended that the video encoder is stopped prior to the packetizer. + */ + @Override + public void stop() { + if (mThread == null) { + return; + } + + mThread.interrupt(); + mThread = null; + + mPaused = false; + mWaitForIDR = false; + mOutputQueue.clear(); + } + + /** + * Pauses this packetizer. + * + * This pauses the packetizer but does not pause the video encoder. + */ + @Override + public void pause() { + mPaused = true; + } + + /** + * Resumes this packetizer. + */ + @Override + public void resume() { + mWaitForIDR = true; + mPaused = false; + } + + /** + * The thread routine. + */ + public void run() { + SdlConnection connection = _session.getSdlConnection(); + + while (mThread != null && !mThread.isInterrupted()) { + ByteBuffer frame; + try { + frame = mOutputQueue.take(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } + + while (frame.hasRemaining()) { + int len = frame.remaining() > bufferSize ? bufferSize : frame.remaining(); + + ProtocolMessage pm = new ProtocolMessage(); + pm.setSessionID(_rpcSessionID); + pm.setSessionType(_serviceType); + pm.setFunctionID(0); + pm.setCorrID(0); + pm.setData(frame.array(), frame.arrayOffset() + frame.position(), len); + pm.setPayloadProtected(mServiceProtected); + + _streamListener.sendStreamPacket(pm); + + frame.position(frame.position() + len); + } + } + + // XXX: This is added to sync with StreamPacketizer. Actually it shouldn't be here since + // it's confusing that a packetizer takes care of End Service request. + if (connection != null) { + connection.endService(_serviceType, _rpcSessionID); + } + } + + /** + * Called by the app and encoder. + * + * @see com.smartdevicelink.proxy.interfaces.IVideoStreamListener#sendFrame(byte[], int, int, long) + */ + @Override + public void sendFrame(byte[] data, int offset, int length, long presentationTimeUs) + throws ArrayIndexOutOfBoundsException { + mNALUnitReader.init(data, offset, length); + onEncoderOutput(mNALUnitReader, presentationTimeUs); + } + + /** + * Called by the app and encoder. + * + * @see com.smartdevicelink.proxy.interfaces.IVideoStreamListener#sendFrame(ByteBuffer, long) + */ + @Override + public void sendFrame(ByteBuffer data, long presentationTimeUs) { + mNALUnitReader.init(data); + onEncoderOutput(mNALUnitReader, presentationTimeUs); + } + + private void onEncoderOutput(NALUnitReader nalUnitReader, long ptsInUs) { + if (mPaused) { + return; + } + + ByteBuffer nalUnit; + + while ((nalUnit = nalUnitReader.getNalUnit()) != null) { + if (mWaitForIDR) { + if (isIDR(nalUnit)) { + mWaitForIDR = false; + } else { + continue; + } + } + outputRTPFrames(nalUnit, ptsInUs, nalUnitReader.hasConsumedAll()); + } + } + + private boolean outputRTPFrames(ByteBuffer nalUnit, long ptsInUs, boolean isLast) { + if (RTP_HEADER_LEN + nalUnit.remaining() > MAX_RTP_PACKET_SIZE) { + // Split into multiple Fragmentation Units ([5.8] in RFC 6184) + byte firstByte = nalUnit.get(); + boolean firstFragment = true; + boolean lastFragment = false; + + while (nalUnit.remaining() > 0) { + int payloadLength = MAX_RTP_PACKET_SIZE - (RTP_HEADER_LEN + FU_INDICATOR_LEN + FU_HEADER_LEN); + if (nalUnit.remaining() <= payloadLength) { + payloadLength = nalUnit.remaining(); + lastFragment = true; + } + + ByteBuffer frame = allocateRTPFrame(FU_INDICATOR_LEN + FU_HEADER_LEN + payloadLength, + false, isLast, ptsInUs); + // FU indicator + frame.put((byte)((firstByte & 0xE0) | TYPE_FU_A)); + // FU header + frame.put((byte)((firstFragment ? 0x80 : lastFragment ? 0x40 : 0) | (firstByte & 0x1F))); + // FU payload + frame.put(nalUnit.array(), nalUnit.position(), payloadLength); + nalUnit.position(nalUnit.position() + payloadLength); + frame.flip(); + + try { + mOutputQueue.put(frame); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return false; + } + + firstFragment = false; + } + } else { + // Use Single NAL Unit Packet ([5.6] in RFC 6184) + ByteBuffer frame = allocateRTPFrame(nalUnit.remaining(), false, isLast, ptsInUs); + frame.put(nalUnit); + frame.flip(); + + try { + mOutputQueue.put(frame); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return false; + } + } + + return true; + } + + private ByteBuffer allocateRTPFrame(int rtpPayloadLen, + boolean hasPadding, boolean isLast, long ptsInUs) { + if (rtpPayloadLen <= 0) { + throw new IllegalArgumentException("Invalid rtpPayloadLen value: " + rtpPayloadLen); + } + if (ptsInUs < 0) { + throw new IllegalArgumentException("Invalid ptsInUs value: " + ptsInUs); + } + + int packetLength = RTP_HEADER_LEN + rtpPayloadLen; + if (packetLength > MAX_RTP_PACKET_SIZE) { + throw new IllegalArgumentException("Invalid rtpPayloadLen value: " + rtpPayloadLen); + } + int ptsIn90kHz = (int)(ptsInUs * 9 / 100) + mInitialPTS; + + ByteBuffer frame = ByteBuffer.allocate(FRAME_LENGTH_LEN + packetLength); + frame.order(ByteOrder.BIG_ENDIAN); + frame.putShort((short)packetLength); + + // Version = 2, Padding = hasPadding, Extension = 0, CSRC count = 0 + frame.put((byte)(0x80 | (hasPadding ? 0x20 : 0))) + // Marker = isLast, Payload type = mPayloadType + .put((byte)((isLast ? 0x80 : 0) | (mPayloadType & 0x7F))) + .putChar(mSequenceNum) + .putInt(ptsIn90kHz) + .putInt(mSSRC); + + if (frame.position() != FRAME_LENGTH_LEN + RTP_HEADER_LEN) { + throw new RuntimeException("Data size in ByteBuffer mismatch"); + } + + mSequenceNum++; + return frame; + } + + private static boolean isIDR(ByteBuffer nalUnit) { + if (nalUnit == null || !nalUnit.hasRemaining()) { + throw new IllegalArgumentException("Invalid nalUnit arg"); + } + + byte nalUnitType = (byte)(nalUnit.get(nalUnit.position()) & 0x1F); + return nalUnitType == 5; + } + + + private static int SKIP_TABLE[] = new int[256]; + static { + // Sunday's quick search algorithm is used to find the start code. + // Prepare the table (SKIP_TABLE[0] = 2, SKIP_TABLE[1] = 1 and other elements will be 4). + byte[] NAL_UNIT_START_CODE = {0, 0, 1}; + int searchStringLen = NAL_UNIT_START_CODE.length; + for (int i = 0; i < SKIP_TABLE.length; i++) { + SKIP_TABLE[i] = searchStringLen + 1; + } + for (int i = 0; i < searchStringLen; i++) { + SKIP_TABLE[NAL_UNIT_START_CODE[i] & 0xFF] = searchStringLen - i; + } + } + + private class NALUnitReader { + private byte[] mData; + private int mOffset; + private int mLimit; + + NALUnitReader() { + } + + void init(byte[] data) { + mData = data; + mOffset = 0; + mLimit = data.length; + } + + void init(byte[] data, int offset, int length) throws ArrayIndexOutOfBoundsException { + if (offset < 0 || offset > data.length || length <= 0 || offset + length > data.length) { + throw new ArrayIndexOutOfBoundsException(); + } + mData = data; + mOffset = offset; + mLimit = offset + length; + } + + void init(ByteBuffer data) { + if (data == null || data.remaining() == 0) { + mData = null; + mOffset = 0; + mLimit = 0; + return; + } + + if (data.hasArray()) { + mData = data.array(); + mOffset = data.position() + data.arrayOffset(); + mLimit = mOffset + data.remaining(); + + // mark the buffer as consumed + data.position(data.position() + data.remaining()); + } else { + byte[] buffer = new byte[data.remaining()]; + data.get(buffer); + + mData = buffer; + mOffset = 0; + mLimit = buffer.length; + } + } + + ByteBuffer getNalUnit() { + if (hasConsumedAll()) { + return null; + } + + int pos = mOffset; + int start = -1; + + while (mLimit - pos >= 3) { + if (mData[pos] == 0 && mData[pos+1] == 0 && mData[pos+2] == 1) { + if (start != -1) { + // We've found a start code, a NAL unit and then another start code. + mOffset = pos; + // remove 0x00s in front of the start code + while (pos > start && mData[pos-1] == 0) { + pos--; + } + if (pos > start) { + return ByteBuffer.wrap(mData, start, pos - start); + } else { + // No NAL unit between two start codes?! Forget it and search for + // another start code. + pos = mOffset; + } + } + // This is the first start code. + pos += 3; + start = pos; + } else { + try { + pos += SKIP_TABLE[mData[pos+3] & 0xFF]; + } catch (ArrayIndexOutOfBoundsException e) { + break; + } + } + } + + mOffset = mLimit; + if (start != -1 && mLimit > start) { + // We've found a start code and then reached to the end of array. + return ByteBuffer.wrap(mData, start, mLimit - start); + } + // A start code was not found + return null; + } + + boolean hasConsumedAll() { + return (mData == null) || (mLimit - mOffset < 4); + } + } +} 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..d2e6754ca --- /dev/null +++ b/sdl_android/src/main/java/com/smartdevicelink/streaming/video/VideoStreamingParameters.java @@ -0,0 +1,105 @@ +package com.smartdevicelink.streaming.video; + +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 VideoStreamingParameters { + 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 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; + } + + 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 -- cgit v1.2.1