return hostTable;
}
- // TODO: Update publish with proper stream interface.
- public boolean publish(AittStream stream, String topic, byte[] message) {
- if (stream == null) {
- Log.e(TAG, "Stream is null.");
- return false;
- }
-
- return stream.publish(topic, message);
- }
-
/**
* Method to create transportHandler and publish message based on protocol
*
}
/**
- * Method that receives message from JNI layer for topics other than discovery topics
+ * Method to create AittStream object
*
* @param protocol The data received from JNI layer to be sent to application layer
* @param topic The data received from JNI layer to be sent to application layer
* @param streamRole The data received from JNI layer to be sent to application layer
+ * @return AittStream instance
*/
- public AittStream createStream(Protocol protocol, String topic, AittStream.StreamRole streamRole) {
+ public AittStream createStream(Protocol protocol, String topic, AittStream.StreamRole streamRole) throws InstantiationException {
ModuleHandler moduleHandler = createModuleHandler(protocol);
if (moduleHandler == null) {
Log.e(TAG, "Fail to create a module handler.");
switch (protocol) {
case WEBRTC:
WebRTCStream webRTCStream = (WebRTCStream) ((WebRTCHandler) moduleHandler).newStreamModule(protocol, topic, streamRole, appContext);
- if (webRTCStream != null)
- webRTCStream.setJNIInterface(mJniInterface);
+ webRTCStream.setJNIInterface(mJniInterface);
return webRTCStream;
case RTSP:
RTSPStream rtspStream = (RTSPStream) ((RTSPHandler) moduleHandler).newStreamModule(protocol, topic, streamRole, appContext);
- if (rtspStream != null)
- rtspStream.setJNIInterface(mJniInterface);
+ rtspStream.setJNIInterface(mJniInterface);
return rtspStream;
default:
Log.d(TAG, "Not supported yet.");
if (topic == null || topic.isEmpty())
throw new InvalidParameterException("Invalid topic");
- try {
- if (role == AittStream.StreamRole.SUBSCRIBER) {
- return createSubscriberStream(topic, role);
- } else {
- return createPublisherStream(topic, role);
- }
- } catch (Exception e) {
- Log.e(TAG, "Fail to create an AittStream instance.");
+ if (role == AittStream.StreamRole.SUBSCRIBER) {
+ return createSubscriberStream(topic, role);
+ } else {
+ return createPublisherStream(topic, role);
}
- return null;
}
}
* @param context context of the application
* @return returns stream object
*/
- AittStream newStreamModule(Aitt.Protocol protocol, String topic, AittStream.StreamRole role, Context context) {
+ AittStream newStreamModule(Aitt.Protocol protocol, String topic, AittStream.StreamRole role, Context context) throws InstantiationException {
// TODO: Change this function properly after refactoring WebRTC modules.
return null;
}
}
@Override
- public AittStream newStreamModule(Aitt.Protocol protocol, String topic, AittStream.StreamRole role, Context context) {
+ public AittStream newStreamModule(Aitt.Protocol protocol, String topic, AittStream.StreamRole role, Context context) throws InstantiationException {
if (protocol != Aitt.Protocol.WEBRTC)
throw new InvalidParameterException("Invalid protocol");
- try {
- if (role == AittStream.StreamRole.SUBSCRIBER) {
- return createSubscriberStream(topic, role, context);
- } else {
- return createPublisherStream(topic, role, context);
- }
- } catch (Exception e) {
- Log.e(TAG, "Fail to create an AittStream instance.");
+ if (role == AittStream.StreamRole.SUBSCRIBER) {
+ return createSubscriberStream(topic, role, context);
+ } else {
+ return createPublisherStream(topic, role, context);
}
-
- return null;
}
}
public static final int DEFAULT_IPC_PORT = 0;
public static final int DEFAULT_STREAM_WIDTH = 640;
public static final int DEFAULT_STREAM_HEIGHT = 480;
+ public static final int DEFAULT_FPS = 15;
}
void start();
/**
- * Method to publish to a topic
- * @param topic String topic to which data is published
+ * Method to push data on a topic
* @param message Data to be published
* @return returns status
*/
- boolean publish(String topic, byte[] message);
+ boolean push(byte[] message);
/**
* Method to disconnect from the broker
}
/**
- * Method to publish to a topic
+ * Method to push data on a topic
*
- * @param topic String topic to which data is published
* @param message Data to be published
* @return returns status
*/
@Override
- public boolean publish(String topic, byte[] message) {
+ public boolean push(byte[] message) {
// TODO: implement this function.
return true;
}
*/
package com.samsung.android.aitt.stream;
+import static com.samsung.android.aitt.internal.Definitions.DEFAULT_FPS;
import static com.samsung.android.aitt.internal.Definitions.DEFAULT_STREAM_HEIGHT;
import static com.samsung.android.aitt.internal.Definitions.DEFAULT_STREAM_WIDTH;
private final String watchTopic;
private final StreamRole streamRole;
private final String id;
- private final StreamInfo streamInfo;
+ private final WebRTC webrtc;
- private WebRTC webrtc;
private JniInterface jniInterface;
private StreamState streamState = StreamState.INIT;
private StreamStateChangeCallback stateChangeCallback = null;
- private static class StreamInfo {
- private int frameWidth = DEFAULT_STREAM_WIDTH;
- private int frameHeight = DEFAULT_STREAM_HEIGHT;
- }
-
- WebRTCStream(String topic, StreamRole streamRole, Context context) {
+ WebRTCStream(String topic, StreamRole streamRole, Context context) throws InstantiationException {
this.streamRole = streamRole;
id = createId();
if (streamRole == StreamRole.PUBLISHER) {
publishTopic = topic + SRC;
watchTopic = topic + SINK;
- try {
- webrtc = new WebRTCPublisher(context);
- } catch (InstantiationException e) {
- Log.e(TAG, "Failed to create WebRTC instance");
- }
+ webrtc = new WebRTCPublisher(context, DEFAULT_STREAM_WIDTH, DEFAULT_STREAM_HEIGHT, DEFAULT_FPS);
} else {
publishTopic = topic + SINK;
watchTopic = topic + SRC;
- try {
- webrtc = new WebRTCSubscriber(context);
- } catch (InstantiationException e) {
- Log.e(TAG, "Failed to create WebRTC instance");
- }
+ webrtc = new WebRTCSubscriber(context, DEFAULT_STREAM_WIDTH, DEFAULT_STREAM_HEIGHT, DEFAULT_FPS);
}
-
- streamInfo = new StreamInfo();
}
- public static WebRTCStream createSubscriberStream(String topic, StreamRole streamRole, Context context) {
+ public static WebRTCStream createSubscriberStream(String topic, StreamRole streamRole, Context context) throws InstantiationException {
if (streamRole != StreamRole.SUBSCRIBER)
throw new IllegalArgumentException("The role of this stream is not subscriber.");
return new WebRTCStream(topic, streamRole, context);
}
- public static WebRTCStream createPublisherStream(String topic, StreamRole streamRole, Context context) {
+ public static WebRTCStream createPublisherStream(String topic, StreamRole streamRole, Context context) throws InstantiationException {
if (streamRole != StreamRole.PUBLISHER)
throw new IllegalArgumentException("The role of this stream is not publisher.");
if (streamRole == StreamRole.SUBSCRIBER)
throw new IllegalArgumentException("The role of this stream is not publisher");
+ if (streamState == StreamState.READY)
+ throw new RuntimeException("Stream is already started, cannot change configuration now");
+
if (key == null)
throw new IllegalArgumentException("Invalid key");
switch (key) {
+ case "SOURCE_TYPE":
+ webrtc.setSourceType(value);
+ break;
case "HEIGHT":
int height = Integer.parseInt(value);
- if (height == 0)
- streamInfo.frameHeight = DEFAULT_STREAM_HEIGHT;
- else if (height < 0)
+ if (height <= 0)
throw new IllegalArgumentException("Invalid frame height");
else
- streamInfo.frameHeight = height;
+ webrtc.setFrameHeight(height);
break;
case "WIDTH":
int width = Integer.parseInt(value);
- if (width == 0)
- streamInfo.frameWidth = DEFAULT_STREAM_WIDTH;
- else if (width < 0)
+ if (width <= 0)
throw new IllegalArgumentException("Invalid frame width");
else
- streamInfo.frameWidth = width;
+ webrtc.setFrameWidth(width);
+ break;
+ case "FRAME_RATE":
+ int fps = Integer.parseInt(value);
+ if (fps <= 0)
+ throw new IllegalArgumentException("Invalid frame rate");
+ else
+ webrtc.setFrameRate(fps);
break;
default:
throw new IllegalArgumentException("Invalid key");
@Override
public void start() {
- if (invalidWebRTC()) {
+ if (streamState == StreamState.READY || streamState == StreamState.PLAYING) {
+ Log.e(TAG, "Stream already started");
return;
}
+
webrtc.registerIceCandidateAddedCallback(() -> publishDiscoveryMessage(generateDiscoveryMessage()));
+ webrtc.start();
if (streamRole == StreamRole.PUBLISHER) {
FlexBuffersBuilder fbb = new FlexBuffersBuilder(ByteBuffer.allocate(512));
fbb.putString(START);
throw new IllegalArgumentException("The given callback is null.");
if (streamRole == StreamRole.SUBSCRIBER) {
- if (invalidWebRTC())
- return;
webrtc.registerDataCallback(streamDataCallback::pushStreamData);
} else if (streamRole == StreamRole.PUBLISHER) {
Log.e(TAG, "Invalid function call");
@Override
public int getStreamHeight() {
- if (streamRole == StreamRole.SUBSCRIBER)
- return webrtc.getFrameHeight();
- return streamInfo.frameHeight;
+ return webrtc.getFrameHeight();
}
@Override
public int getStreamWidth() {
- if (streamRole == StreamRole.SUBSCRIBER)
- return webrtc.getFrameWidth();
- return streamInfo.frameWidth;
+ return webrtc.getFrameWidth();
}
@Override
- public boolean publish(String topic, byte[] message) {
- if (invalidWebRTC())
- return false;
+ public boolean push(byte[] message) {
+ if (streamRole == StreamRole.SUBSCRIBER) {
+ throw new RuntimeException("Push is not allowed with a subscriber stream");
+ }
- if (streamRole == StreamRole.PUBLISHER) {
- if (topic.endsWith(Definitions.RESPONSE_POSTFIX)) {
- Log.d(TAG, "A message is sent through a WebRTC publisher stream.");
- return webrtc.sendMessageData(message);
- } else {
- Log.d(TAG, "Video data are sent through a WebRTC publisher stream.");
- webrtc.sendVideoData(message, streamInfo.frameWidth, streamInfo.frameHeight);
- }
+ if (publishTopic.contains(Definitions.RESPONSE_POSTFIX)) {
+ Log.d(TAG, "A message is sent through a WebRTC publisher stream.");
+ return webrtc.pushMessageData(message);
} else {
- Log.e(TAG, "publish() is not allowed to a subscriber stream.");
+ Log.d(TAG, "Media-packet is sent through a WebRTC publisher stream.");
+ return webrtc.pushMediaPacket(message);
}
-
- return true;
}
public void setJNIInterface(JniInterface jniInterface) {
- if (invalidWebRTC())
- return;
-
this.jniInterface = jniInterface;
jniInterface.setDiscoveryCallback(watchTopic, (clientId, status, data) -> {
return UUID.randomUUID().toString();
}
- private boolean invalidWebRTC() {
- if (webrtc == null) {
- Log.e(TAG, "Invalid WebRTCStream");
- return true;
- }
- return false;
- }
-
private void handleStreamState(String clientId, ByteBuffer buffer) {
String peerState = FlexBuffers.getRoot(buffer).asString();
if (STOP.compareTo(peerState) == 0) {
jniInterface.updateDiscoveryMessage(publishTopic, data);
}
}
+
private void updateState(StreamState state) {
streamState = state;
if (stateChangeCallback != null)
import androidx.annotation.ColorInt;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.samsung.android.aitt.internal.Definitions;
import com.samsung.android.aitt.Aitt;
import com.samsung.android.aitt.stream.AittStream;
Aitt publisher = new Aitt(appContext, AITT_WEBRTC_CLIENT_ID, wifiIP, true);
publisher.connect(brokerIp, PORT);
AittStream publisherStream = publisher.createStream(Aitt.Protocol.WEBRTC, TEST_MESSAGE_TOPIC, PUBLISHER);
- publisherStream.start();
+ publisherStream.setConfig("SOURCE_TYPE", "MEDIA_PACKET").start();
Log.i(TAG, "A WebRTC client and a publisher stream are created.");
subscriberStream.start();
while (true) {
// TODO: Replace publish
- boolean isPublished = publisher.publish(publisherStream, TEST_MESSAGE_TOPIC, message.getBytes());
+ boolean isPublished = publisherStream.push(message.getBytes());
if (isPublished)
break;
}
Aitt publisher = new Aitt(appContext, AITT_WEBRTC_CLIENT_ID + LARGE_DATA_PREFIX, wifiIP, true);
publisher.connect(brokerIp, PORT);
AittStream publisherStream = publisher.createStream(Aitt.Protocol.WEBRTC, TEST_LARGE_MESSAGE_TOPIC, PUBLISHER);
- publisherStream.start();
+ publisherStream.setConfig("SOURCE_TYPE", "MEDIA_PACKET").start();
Log.i(TAG, "A WebRTC client and a publisher stream are created.");
subscriberStream.start();
while (true) {
// TODO: Replace publish
- boolean isPublished = publisher.publish(publisherStream, TEST_LARGE_MESSAGE_TOPIC, largeBytes);
+ boolean isPublished = publisherStream.push(largeBytes);
if (isPublished)
break;
Thread.sleep(SLEEP_INTERVAL);
}
@Test
- public void testWebRTCVideoStreamingNoConfig_N() {
+ public void testWebRTCMediaPacketStreamImproperConfig_N() {
try {
Aitt subscriber = new Aitt(appContext, AITT_WEBRTC_SERVER_ID + VIDEO_PREFIX, wifiIP, true);
subscriber.connect(brokerIp, PORT);
Aitt publisher = new Aitt(appContext, AITT_WEBRTC_CLIENT_ID + VIDEO_PREFIX, wifiIP, true);
publisher.connect(brokerIp, PORT);
AittStream publisherStream = publisher.createStream(Aitt.Protocol.WEBRTC, TEST_VIDEO_TOPIC, PUBLISHER);
+ publisherStream.setConfig("SOURCE_TYPE", "MEDIA_PACKET");
publisherStream.start();
Log.i(TAG, "A WebRTC client and a publisher stream are created.");
while (true) {
// TODO: Replace publish
- boolean isPublished = publisher.publish(publisherStream, TEST_VIDEO_TOPIC, frameImageBytes);
+ boolean isPublished = publisherStream.push(frameImageBytes);
if (isPublished)
break;
}
}
@Test
- public void testWebRTCVideoStreamingWithConfig_P() {
+ public void testWebRTCMediaPacketStreamWithConfig_P() {
try {
Aitt subscriber = new Aitt(appContext, AITT_WEBRTC_SERVER_ID + VIDEO_PREFIX, wifiIP, true);
subscriber.connect(brokerIp, PORT);
publisher.connect(brokerIp, PORT);
AittStream publisherStream = publisher.createStream(Aitt.Protocol.WEBRTC, TEST_VIDEO_TOPIC, PUBLISHER);
- publisherStream.setConfig("WIDTH", "320")
+ publisherStream.setConfig("SOURCE_TYPE", "MEDIA_PACKET")
+ .setConfig("WIDTH", "320")
.setConfig("HEIGHT", "240")
.start();
}
while (true) {
- boolean isPublished = publisher.publish(publisherStream, TEST_VIDEO_TOPIC, frameImageBytes);
+ boolean isPublished = publisherStream.push(frameImageBytes);
if (isPublished)
break;
}
}
@Test
- public void testWebRTCStreamConfigInvalidKey_N() {
+ public void testWebRTCCameraStream_P() {
try {
+ Aitt subscriber = new Aitt(appContext, AITT_WEBRTC_SERVER_ID + VIDEO_PREFIX, wifiIP, true);
+ subscriber.connect(brokerIp, PORT);
+ AittStream subscriberStream = subscriber.createStream(Aitt.Protocol.WEBRTC, TEST_VIDEO_TOPIC, SUBSCRIBER);
+ subscriberStream.setReceiveCallback(data -> {
+ Log.i(TAG, "A callback is received in testWebRTCVideoStreamingWithConfig");
+ if (Definitions.DEFAULT_STREAM_WIDTH != subscriberStream.getStreamWidth())
+ throw new RuntimeException("Wrong frame width");
+ if (Definitions.DEFAULT_STREAM_HEIGHT != subscriberStream.getStreamHeight())
+ throw new RuntimeException("Wrong frame height");
+
+ Log.i(TAG, "The correct test image is received.");
+
+ if (looper != null)
+ looper.quit();
+ });
+ Log.i(TAG, "A WebRTC server and a subscriber stream are created.");
+
Aitt publisher = new Aitt(appContext, AITT_WEBRTC_CLIENT_ID + VIDEO_PREFIX, wifiIP, true);
publisher.connect(brokerIp, PORT);
AittStream publisherStream = publisher.createStream(Aitt.Protocol.WEBRTC, TEST_VIDEO_TOPIC, PUBLISHER);
+ publisherStream.start();
- assertThrows(IllegalArgumentException.class, () -> publisherStream.setConfig("KEY", "value"));
+ Log.i(TAG, "A WebRTC client and a publisher stream are created.");
+
+ subscriberStream.start();
+ Log.i(TAG, "The subscriber stream starts.");
+
+ int intervalSum = 0;
+ while (intervalSum < 2000) {
+ Thread.sleep(SLEEP_INTERVAL);
+ intervalSum += SLEEP_INTERVAL;
+ }
+
+ Looper.loop();
+ Log.i(TAG, "A looper is finished.");
publisherStream.disconnect();
+
+ subscriberStream.stop();
} catch (Exception e) {
- fail("Failed testWebRTCStreamConfigInvalidKey, (" + e + ")");
+ fail("Failed testWebRTCVideoStreamingWithConfig, (" + e + ")");
}
}
@Test
- public void testWebRTCStreamConfigNullKey_N() {
+ public void testWebRTCStreamConfigInvalidKey_N() {
try {
Aitt publisher = new Aitt(appContext, AITT_WEBRTC_CLIENT_ID + VIDEO_PREFIX, wifiIP, true);
publisher.connect(brokerIp, PORT);
AittStream publisherStream = publisher.createStream(Aitt.Protocol.WEBRTC, TEST_VIDEO_TOPIC, PUBLISHER);
- assertThrows(IllegalArgumentException.class, () -> publisherStream.setConfig(null, "value"));
+ assertThrows(IllegalArgumentException.class, () -> publisherStream.setConfig("KEY", "value"));
publisherStream.disconnect();
} catch (Exception e) {
- fail("Failed testWebRTCStreamConfigNullKey, (" + e + ")");
+ fail("Failed testWebRTCStreamConfigInvalidKey, (" + e + ")");
}
}
@Test
- public void testWebRTCStreamConfigWithoutHeight_P() {
+ public void testWebRTCStreamConfigNullKey_N() {
try {
Aitt publisher = new Aitt(appContext, AITT_WEBRTC_CLIENT_ID + VIDEO_PREFIX, wifiIP, true);
publisher.connect(brokerIp, PORT);
AittStream publisherStream = publisher.createStream(Aitt.Protocol.WEBRTC, TEST_VIDEO_TOPIC, PUBLISHER);
- publisherStream.setConfig("WIDTH", "320")
- .setConfig("HEIGHT", "0")
- .start();
+ assertThrows(IllegalArgumentException.class, () -> publisherStream.setConfig(null, "value"));
publisherStream.disconnect();
} catch (Exception e) {
- fail("Failed testWebRTCStreamConfigWithoutHeight, (" + e + ")");
+ fail("Failed testWebRTCStreamConfigNullKey, (" + e + ")");
}
}
@Test
- public void testWebRTCStreamConfigWithoutWidth_P() {
+ public void testWebRTCStreamSetConfig_P() {
try {
Aitt publisher = new Aitt(appContext, AITT_WEBRTC_CLIENT_ID + VIDEO_PREFIX, wifiIP, true);
publisher.connect(brokerIp, PORT);
AittStream publisherStream = publisher.createStream(Aitt.Protocol.WEBRTC, TEST_VIDEO_TOPIC, PUBLISHER);
- publisherStream.setConfig("HEIGHT", "240")
- .setConfig("WIDTH", "0")
+ publisherStream.setConfig("SOURCE_TYPE", "CAMERA")
+ .setConfig("WIDTH", "320")
+ .setConfig("HEIGHT", "240")
+ .setConfig("FRAME_RATE", "30")
.start();
publisherStream.disconnect();
} catch (Exception e) {
- fail("Failed testWebRTCStreamConfigWithoutWidth, (" + e + ")");
+ fail("Failed testWebRTCStreamConfigWithoutHeight, (" + e + ")");
}
}
}
@Test
+ public void testWebRTCStreamInvalidSourceType_N() {
+ try {
+ Aitt publisher = new Aitt(appContext, AITT_WEBRTC_CLIENT_ID + VIDEO_PREFIX, wifiIP, true);
+ publisher.connect(brokerIp, PORT);
+ AittStream publisherStream = publisher.createStream(Aitt.Protocol.WEBRTC, TEST_VIDEO_TOPIC, PUBLISHER);
+
+ assertThrows(IllegalArgumentException.class, () -> publisherStream.setConfig("SOURCE_TYPE", "A"));
+
+ publisherStream.disconnect();
+ } catch (Exception e) {
+ fail("Failed testWebRTCStreamInvalidHeight, (" + e + ")");
+ }
+ }
+
+ @Test
+ public void testWebRTCStreamInvalidFrameRate_N() {
+ try {
+ Aitt publisher = new Aitt(appContext, AITT_WEBRTC_CLIENT_ID + VIDEO_PREFIX, wifiIP, true);
+ publisher.connect(brokerIp, PORT);
+ AittStream publisherStream = publisher.createStream(Aitt.Protocol.WEBRTC, TEST_VIDEO_TOPIC, PUBLISHER);
+
+ assertThrows(IllegalArgumentException.class, () -> publisherStream.setConfig("FRAME_RATE", "-1"));
+
+ publisherStream.disconnect();
+ } catch (Exception e) {
+ fail("Failed testWebRTCStreamInvalidHeight, (" + e + ")");
+ }
+ }
+
+ @Test
public void testWebRTCStreamConfigInvalidRole_N() {
try {
Aitt subscriber = new Aitt(appContext, AITT_WEBRTC_CLIENT_ID + VIDEO_PREFIX, wifiIP, true);
android:required="true" />
<uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
protected PeerConnection peerConnection;
protected PeerConnectionFactory connectionFactory;
+ protected EglBase eglBase;
protected DataChannel localDataChannel;
protected ReceiveDataCallback dataCallback;
protected IceCandidateAddedCallback iceCandidateAddedCallback;
protected String localDescription;
protected List<String> iceCandidates = new ArrayList<>();
protected String peerDiscoveryId;
+ protected int frameWidth;
+ protected int frameHeight;
+ protected int frameRate;
+ protected SourceType sourceType = SourceType.CAMERA;
protected final Context appContext;
+
private String peerId;
/**
void onIceCandidate();
}
+ protected enum SourceType {
+ CAMERA,
+ MEDIA_PACKET;
+
+ private static SourceType fromString(String str) {
+ switch (str) {
+ case "CAMERA":
+ return CAMERA;
+ case "MEDIA_PACKET":
+ return MEDIA_PACKET;
+ default:
+ throw new IllegalArgumentException("Invalid SOURCE_TYPE: " + str);
+ }
+ }
+ }
+
/**
* Method to set remote description of peer
*
protected abstract void initializePeerConnection();
+ protected abstract void configureStream();
+
/**
* WebRTC constructor to create webRTC instance
*
* @param appContext Application context creating webRTC instance
+ * @param width Frame width
+ * @param height Frame height
+ * @param fps Frames per second
*/
- public WebRTC(Context appContext) throws InstantiationException {
+ public WebRTC(Context appContext, int width, int height, int fps) throws InstantiationException {
if (appContext == null)
throw new IllegalArgumentException("App context is null.");
initializePeerConnection();
if (peerConnection == null)
throw new InstantiationException("Failed to create peer connection");
+
+ frameWidth = width;
+ frameHeight = height;
+ frameRate = fps;
}
/**
iceCandidateAddedCallback = cb;
}
+ public void start() {
+ Log.d(TAG, "Start WebRTC");
+ }
+
/**
* Method to disconnect the connection from peer
*/
stop();
}
+ public void setSourceType(String type) {
+ SourceType newType = SourceType.fromString(type);
+ if (sourceType == newType)
+ return;
+ sourceType = newType;
+ configureStream();
+ }
+
+ public void setFrameHeight(int height) {
+ frameHeight = height;
+ }
+
+ public void setFrameWidth(int width) {
+ frameWidth = width;
+ }
+
+ /**
+ * Method to get received Frame height
+ *
+ * @return Received frame height
+ */
+ public int getFrameHeight() {
+ return frameHeight;
+ }
+
+ /**
+ * Method to get received Frame width
+ *
+ * @return Received frame width
+ */
+ public int getFrameWidth() {
+ return frameWidth;
+ }
+
+ public void setFrameRate(int fps) {
+ frameRate = fps;
+ }
+
/**
- * Method used to send video data
+ * Method used to push media data
*
- * @param frame Video frame in byte format
- * @param width width of the video frame
- * @param height height of the video frame
+ * @param packet Media packet in byte format
+ * @return true if message is successfully sent, false otherwise
*/
- public void sendVideoData(byte[] frame, int width, int height) {
- Log.d(TAG, "Send video data");
+ public boolean pushMediaPacket(byte[] packet) {
+ Log.d(TAG, "Push video data");
+ return true;
}
/**
- * Method to send message data, if the message size is greater than MAX_MESSAGE_SIZE, message will be compressed before sending
+ * Method to push message data, if the message size is greater than MAX_MESSAGE_SIZE, message will be compressed before sending
*
* @param message message to be sent in byte format
+ * @return true if message is successfully sent, false otherwise
*/
- public boolean sendMessageData(byte[] message) {
- Log.d(TAG, "Send message data");
+ public boolean pushMessageData(byte[] message) {
+ Log.d(TAG, "Push message data");
return true;
}
* Method to initialize peer connection factory
*/
private void initializePeerConnectionFactory() {
- EglBase mRootEglBase;
- mRootEglBase = EglBase.create();
- VideoEncoderFactory encoderFactory = new DefaultVideoEncoderFactory(mRootEglBase.getEglBaseContext(), true , true);
- VideoDecoderFactory decoderFactory = new DefaultVideoDecoderFactory(mRootEglBase.getEglBaseContext());
+ eglBase = EglBase.create();
+ VideoEncoderFactory encoderFactory = new DefaultVideoEncoderFactory(eglBase.getEglBaseContext(), true , true);
+ VideoDecoderFactory decoderFactory = new DefaultVideoDecoderFactory(eglBase.getEglBaseContext());
PeerConnectionFactory.initialize(PeerConnectionFactory.InitializationOptions.builder(appContext).setEnableInternalTracer(true).createInitializationOptions());
PeerConnectionFactory.Builder builder = PeerConnectionFactory.builder().setVideoEncoderFactory(encoderFactory).setVideoDecoderFactory(decoderFactory);
Log.d(TAG, "onSetFailure: Reason = " + s);
}
}
-
- /**
- * Method to get received Frame height
- *
- * @return Received frame height
- */
- public int getFrameHeight() {
- return 0;
- }
-
- /**
- * Method to get received Frame width
- *
- * @return Received frame width
- */
- public int getFrameWidth() {
- return 0;
- }
}
import org.json.JSONException;
import org.json.JSONObject;
+import org.webrtc.Camera1Enumerator;
+import org.webrtc.Camera2Enumerator;
+import org.webrtc.CameraEnumerator;
import org.webrtc.CapturerObserver;
import org.webrtc.DataChannel;
import org.webrtc.IceCandidate;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.concurrent.TimeUnit;
public final class WebRTCPublisher extends WebRTC {
private static final String TAG = "WebRTCPublisher";
private static final String VIDEO_TRACK_ID = "AITT_WEBRTC_v0";
private static final String MEDIA_STREAM_ID = "AITT_WEBRTC";
-
- private VideoTrack videoTrackFromSource;
- private FrameVideoCapturer videoCapturer;
-
- public WebRTCPublisher(Context appContext) throws InstantiationException {
- super(appContext);
-
- VideoSource videoSource = connectionFactory.createVideoSource(false);
- createVideoTrack(videoSource);
- addVideoTrack();
- createVideoCapturer(videoSource);
+ private static final String SURFACE_TEXTURE_THREAD = "AITT_WEBRTC_SURFACE_TEXTURE";
+
+ private MediaStream mediaStream;
+ private VideoSource videoSource;
+ private VideoTrack videoTrack;
+ private MediaPacketCapturer mediaPacketCapturer;
+ private VideoCapturer cameraCapturer;
+ private CameraEnumerator cameraEnumerator;
+ private List<String> cameraDeviceNames;
+
+ public WebRTCPublisher(Context appContext, int width, int height, int fps) throws InstantiationException {
+ super(appContext, width, height, fps);
+ configureStream();
}
@Override
}
@Override
- public void sendVideoData(byte[] frame, int width, int height) {
- videoCapturer.send(frame, width, height);
+ public void start() {
+ if (sourceType == SourceType.CAMERA && cameraCapturer != null)
+ cameraCapturer.startCapture(frameWidth, frameHeight, frameRate);
+ }
+
+ @Override
+ public boolean pushMediaPacket(byte[] packet) {
+ if (sourceType != SourceType.MEDIA_PACKET)
+ throw new RuntimeException("Push is not supported for this source type");
+ return mediaPacketCapturer.send(packet, frameWidth, frameHeight);
}
@Override
- public boolean sendMessageData(byte[] message) {
+ public boolean pushMessageData(byte[] message) {
ByteBuffer chunkData;
if (message.length < MAX_MESSAGE_SIZE) {
ByteBuffer data = ByteBuffer.wrap(message);
localDataChannel = peerConnection.createDataChannel("sendDataChannel", new DataChannel.Init());
}
+ @Override
+ protected void configureStream() {
+ if (peerConnection == null)
+ throw new RuntimeException("Invalid stream state, peer connection not created");
+
+ cleanStream();
+ videoSource = connectionFactory.createVideoSource(false);
+ createVideoTrack();
+ createMediaStream();
+ createVideoCapturer();
+ }
+
+ private void cleanStream() {
+ if (mediaStream != null) {
+ peerConnection.removeStream(mediaStream);
+ if (videoTrack != null) {
+ mediaStream.removeTrack(videoTrack);
+ videoTrack.dispose();
+ }
+ }
+
+ if (videoSource != null)
+ videoSource.dispose();
+
+ if (cameraCapturer != null) {
+ try {
+ cameraCapturer.stopCapture();
+ cameraCapturer.dispose();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Failed to stop cameraCapturer:" + e.getMessage());
+ }
+ }
+ }
+
private void createAnswer() {
peerConnection.createAnswer(new SimpleSdpObserver() {
@Override
}, new MediaConstraints());
}
- private void createVideoCapturer(VideoSource videoSource) {
- videoCapturer = new FrameVideoCapturer();
- videoCapturer.initialize(null, null, videoSource.getCapturerObserver());
+ private void createVideoTrack() {
+ videoTrack = connectionFactory.createVideoTrack(VIDEO_TRACK_ID, videoSource);
+ videoTrack.setEnabled(true);
}
- /**
- * Method to create video track
- */
- private void createVideoTrack(VideoSource videoSource) {
- videoTrackFromSource = connectionFactory.createVideoTrack(VIDEO_TRACK_ID, videoSource);
- videoTrackFromSource.setEnabled(true);
+ private void createMediaStream() {
+ mediaStream = connectionFactory.createLocalMediaStream(MEDIA_STREAM_ID);
+ mediaStream.addTrack(videoTrack);
+ peerConnection.addStream(mediaStream);
}
- /**
- * Method to add video track
- */
- private void addVideoTrack() {
- MediaStream mediaStream = connectionFactory.createLocalMediaStream(MEDIA_STREAM_ID);
- mediaStream.addTrack(videoTrackFromSource);
- peerConnection.addStream(mediaStream);
+ private void createVideoCapturer() {
+ if (sourceType == SourceType.CAMERA) {
+ if (cameraDeviceNames == null)
+ setCameraDeviceNames();
+ for (String deviceName : cameraDeviceNames) {
+ cameraCapturer = cameraEnumerator.createCapturer(deviceName , null);
+ if (cameraCapturer != null)
+ break;
+ }
+ if (cameraCapturer != null) {
+ SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create(SURFACE_TEXTURE_THREAD, eglBase.getEglBaseContext());
+ cameraCapturer.initialize(surfaceTextureHelper, appContext, videoSource.getCapturerObserver());
+ }
+ } else {
+ mediaPacketCapturer = new MediaPacketCapturer();
+ mediaPacketCapturer.initialize(null, null, videoSource.getCapturerObserver());
+ }
+ }
+
+ private void setCameraDeviceNames() {
+ cameraDeviceNames = new ArrayList<>();
+ if (Camera2Enumerator.isSupported(appContext))
+ cameraEnumerator = new Camera2Enumerator(appContext);
+ else
+ cameraEnumerator = new Camera1Enumerator(true);
+
+ String[] deviceNames = cameraEnumerator.getDeviceNames();
+ for (String deviceName : deviceNames) {
+ if(cameraEnumerator.isFrontFacing(deviceName)){
+ cameraDeviceNames.add(deviceName);
+ }
+ }
}
/**
- * Class to implement Frame video capturer
+ * Class to implement Media Packet capturer
*/
- private static class FrameVideoCapturer implements VideoCapturer {
+ private static class MediaPacketCapturer implements VideoCapturer {
private CapturerObserver capturerObserver;
- void send(byte[] frame, int width, int height) {
+ boolean send(byte[] frame, int width, int height) {
long timestampNS = TimeUnit.MILLISECONDS.toNanos(SystemClock.elapsedRealtime());
NV21Buffer buffer = new NV21Buffer(frame, width, height, null);
VideoFrame videoFrame = new VideoFrame(buffer, 0, timestampNS);
this.capturerObserver.onFrameCaptured(videoFrame);
videoFrame.release();
+ return true;
}
@Override
import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
private final ByteArrayOutputStream baos;
private boolean recvLargeChunk = false;
- private int frameHeight = 0;
- private int frameWidth = 0;
- public WebRTCSubscriber(Context appContext) throws InstantiationException {
- super(appContext);
+ public WebRTCSubscriber(Context appContext, int width, int height, int fps) throws InstantiationException {
+ super(appContext, width, height, fps);
baos = new ByteArrayOutputStream();
}
}
@Override
- public int getFrameHeight() {
- return frameHeight;
- }
-
- @Override
- public int getFrameWidth() {
- return frameWidth;
- }
-
- @Override
protected void initializePeerConnection() {
PeerConnection.RTCConfiguration rtcConfig = new PeerConnection.RTCConfiguration(new ArrayList<>());
localDataChannel = peerConnection.createDataChannel("DataChannel", new DataChannel.Init());
}
+ @Override
+ protected void configureStream() {
+ Log.e(TAG, "Configuring stream not supported for subscriber");
+ }
+
private void createOffer() {
MediaConstraints sdpMediaConstraints = new MediaConstraints();
sdpMediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveVideo", "true"));
@Mock
private final Context context = mock(Context.class);
+ private static final int WIDTH = 320;
+ private static final int HEIGHT = 240;
+ private static final int FPS = 30;
+
private static MockedStatic<Log> mockedLog;
@BeforeClass
@Test(expected = IllegalArgumentException.class)
public void testWebRTCPublisherConstructor_N() throws IllegalArgumentException {
try {
- WebRTC webRTC = new WebRTCPublisher(null);
+ WebRTC webRTC = new WebRTCPublisher(null, WIDTH, HEIGHT, FPS);
assertNotNull("WebRTC instance not null", webRTC);
} catch (InstantiationException e) {
fail("Failed to create WebRTCPublisher" + e);
@Test(expected = IllegalArgumentException.class)
public void testWebRTCSubscriberConstructor_N() throws IllegalArgumentException {
try {
- WebRTC webRTC = new WebRTCSubscriber(null);
+ WebRTC webRTC = new WebRTCSubscriber(null, WIDTH, HEIGHT, FPS);
assertNotNull("WebRTC instance not null", webRTC);
} catch (InstantiationException e) {
fail("Failed to create WebRTCSubscriber" + e);