getAudioPlaybackList() {
if (audioPlaybackList == null) {
- audioPlaybackList = getMedia().getAudioPlayback();
+ audioPlaybackList = new ArrayList<>(getMedia().getAudioPlayback());
}
String log = "[getAudioPlaybackList] AudioPlaybackList is: " + audioPlaybackList;
Log.d(TAG, log);
@@ -1135,8 +1178,8 @@ public boolean setAudioPlaybackIndex(int newValue) {
// If currently subscribing, do not set new audioSourceIndex.
if (isSubscribing()) {
logD(TAG, logTag + "NOT setting to " + newValue + " as currently subscribing.");
- logD(TAG, logTag + "AudioPlayback:" +
- getAudioSourceStr(audioPlayback, true));
+ logD(TAG, logTag + "AudioPlayback:" + "");
+ getAudioPlaybackStr(audioPlayback, true);
return false;
}
@@ -1164,7 +1207,7 @@ public VideoRenderer getRendererPub() {
String logTag = "[Video][Render][er][Pub] ";
// If it's not available, create it with application context.
if (rendererPub == null) {
- rendererPub = new VideoRenderer(context);
+ rendererPub = new VideoRenderer(context, null);
rendererPub.setScalingType(scalingPub);
logD(TAG, logTag + "Created renderer with application context.");
@@ -1188,7 +1231,7 @@ public VideoRenderer getRendererSub() {
String logTag = "[Video][Render][er][Sub] ";
// If it's not available, create it with application context.
if (rendererSub == null) {
- rendererSub = new VideoRenderer(context);
+ rendererSub = new VideoRenderer(context, null);
rendererSub.setScalingType(scalingSub);
logD(TAG, logTag + "Created renderer with application context.");
} else {
@@ -1367,9 +1410,6 @@ public boolean isNdiOutputEnabled(boolean forAudio) {
if (track == null) {
logD(TAG, logTag + "Flag: " + ndiEnabled + ", Track: Does not exist.");
return ndiEnabled;
- } else {
- boolean enabled = track.isNdiOutputEnabled();
- logD(TAG, logTag + "Flag: " + ndiEnabled + ", Track: " + enabled + ".");
}
return ndiEnabled;
}
@@ -1377,7 +1417,7 @@ public boolean isNdiOutputEnabled(boolean forAudio) {
/**
*
* Warning: As of SDK 1.1.1 and until documented otherwise in the SDK,
- * {@link AudioTrack#enableNdiOutput enabling} and {@link AudioTrack#disableNdiOutput() disabling}
+ * {@link VideoTrack#enableNdiOutput enabling} and {@link VideoTrack#disableNdiOutput() disabling}
* NDI output for {@link AudioTrack} is still not functional, i.e. will not have any impact on
* NDI output when called. To enable audio NDI output for subscribed stream, please use
* "ndi output" from the {@link #audioPlaybackList list of Audio Playback Devices}.
@@ -1392,7 +1432,7 @@ public boolean isNdiOutputEnabled(boolean forAudio) {
* @param sourceName If NDI output enabled, this will be the NDI source name.
*/
public void enableNdiOutput(boolean enable, boolean forAudio, String sourceName) {
- String logTag = "[Sub][NDI]";
+ /*String logTag = "[Sub][NDI]";
String log = "";
Track track = audioTrackSub;
@@ -1448,6 +1488,7 @@ public void enableNdiOutput(boolean enable, boolean forAudio, String sourceName)
}
}
logD(TAG, logTag + log);
+ */
}
//**********************************************************************************************
@@ -1473,15 +1514,15 @@ public void setBitrate(int bitrate, MCTypes.Bitrate type) {
return;
}
- BitrateSettings settings = optionPub.bitrateSettings;
+ CompatBitrateSettings settings = optionPub.getBitrateSettings();
switch (type) {
case MIN:
logTag += "[Min] ";
- settings.minBitrateKbps = Optional.of(bitrate);
+ settings.setMinBitrateKbps(bitrate);
break;
case MAX:
logTag += "[Max] ";
- settings.maxBitrateKbps = Optional.of(bitrate);
+ settings.setMaxBitrateKbps(bitrate);
break;
}
logD(TAG, logTag + bitrate + "kbps.");
@@ -1542,28 +1583,27 @@ public int getVideoCodecIndex() {
public boolean setCodecIndex(int newValue, boolean forAudio) {
String logTag = "[Codec][Index][Set] ";
// Set new value into SharePreferences.
- int oldValue = videoCodecIndex;
- String key = videoCodecIndexKey;
+ AtomicInteger oldValue = new AtomicInteger(videoCodecIndex);
+ AtomicReference key = new AtomicReference<>(videoCodecIndexKey);
if (forAudio) {
logTag = "[Audio]" + logTag;
} else {
logTag = "[Video]" + logTag;
}
- if (isPublishing()) {
- logD(TAG, logTag + "Failed! Unable to set new codec while publishing!");
+ String finalLogTag = logTag;
+ if (pubState == PublisherState.PUBLISHING) {
+ logD(TAG, finalLogTag + "Failed! Unable to set new codec while publishing!");
return false;
}
-
if (forAudio) {
- oldValue = audioCodecIndex;
- key = audioCodecIndexKey;
+ oldValue.set(audioCodecIndex);
+ key.set(audioCodecIndexKey);
audioCodecIndex = newValue;
} else {
videoCodecIndex = newValue;
}
-
- Utils.setSaved(key, newValue, context);
- logD(TAG, logTag + "Now: " + newValue +
+ Utils.setSaved(key.get(), newValue, context);
+ logD(TAG, finalLogTag + "Now: " + newValue +
" Was: " + oldValue);
// Set new codec
@@ -1619,23 +1659,30 @@ public void connectPub() {
return;
}
- if (publisher.isConnected()) {
- logD(TAG, logTag + "Not doing as we're already connected!");
- return;
- }
-
- setPubState(PublisherState.CONNECTING);
+ publisher.isConnected()
+ .then(connected -> {
+ if (connected) {
+ logD(TAG, logTag + "Not doing as we're already connected!");
+ } else {
+ setPubState(PublisherState.CONNECTING);
+ logD(TAG, logTag + "Trying...");
+ // Connect Publisher.
+ connectPubMc().then(success -> {
+ if (success) {
+ logD(TAG, logTag + "OK.");
+ } else {
+ setPubState(PublisherState.DISCONNECTED);
+ logD(TAG, logTag + "Failed! Connection requirements not fulfilled. Check inputs (e.g. credentials) and any Millicast error message.");
+ }
+ }).error(e -> {
+ logD(TAG, logTag + e.getLocalizedMessage());
+ });
+ }
+ }).error(e -> {
+ logD(TAG, logTag + e.getLocalizedMessage());
+ });
- // Connect Publisher.
- logD(TAG, logTag + "Trying...");
- boolean success = connectPubMc();
- if (success) {
- logD(TAG, logTag + "OK.");
- } else {
- setPubState(PublisherState.DISCONNECTED);
- logD(TAG, logTag + "Failed! Connection requirements not fulfilled. Check inputs (e.g. credentials) and any Millicast error message.");
- }
}
/**
@@ -1652,25 +1699,22 @@ public void connectSub() {
return;
}
- if (subscriber.isConnected()) {
- logD(TAG, logTag + "Not doing as we're already connected!");
- return;
- }
+ subscriber.isConnected().then(connected -> {
+ if (connected) {
+ logD(TAG, logTag + "Not doing as we're already connected!");
+ return;
+ }
- setSubState(SubscriberState.CONNECTING);
+ setSubState(SubscriberState.CONNECTING);
- // Connect Subscriber.
- logD(TAG, logTag + "Trying...");
- boolean success = connectSubMc();
+ // Connect Subscriber.
+ logD(TAG, logTag + "Trying...");
+ connectSubMc();
- if (success) {
- logD(TAG, logTag + "OK.");
- logD(TAG, logTag + "Initializing audio playback device...");
- audioPlaybackStart();
- } else {
- setSubState(SubscriberState.DISCONNECTED);
- logD(TAG, logTag + "Failed! Connection requirements not fulfilled. Check inputs (e.g. credentials) and any Millicast error message.");
- }
+
+ }).error(e -> {
+ logD(TAG, logTag + e.getLocalizedMessage());
+ });
}
@@ -1690,45 +1734,32 @@ public void startPub() {
return;
}
- if (!publisher.isConnected()) {
- if (pubState == PublisherState.CONNECTED) {
- logD(TAG, logTag + "Client.isConnected FALSE!!! " +
- "Continuing as pubState is " + pubState + ".");
- } else {
- logD(TAG, logTag + "Failed! Publisher not connected!" +
- " pubState is " + pubState + ".");
- return;
- }
- }
-
- if (isPublishing()) {
- logD(TAG, logTag + "Not publishing as we are already publishing!");
- return;
- }
-
- if (!isAudioVideoCaptured()) {
- logD(TAG, logTag + "Failed! Both audio & video are not captured.");
- return;
- }
-
- // Publish to Millicast
- logD(TAG, logTag + "Trying...");
-
- setRecordingEventHandlers();
- boolean success = startPubMc();
-
- if (success) {
- logD(TAG, logTag + "Starting publish...");
- } else {
- logD(TAG, logTag + "Failed! Start publish requirements not fulfilled. Check current states and any Millicast error message.");
- return;
- }
-
- // Get Publisher stats every 10 seconds.
- int sec = 10;
- enableStatsPub(sec * 1000);
- logD(TAG, logTag + "Stats started. Collecting every " + sec + ".");
- logD(TAG, logTag + "OK.");
+ publisher.isConnected()
+ .then(connected -> {
+ if (!connected) {
+ logD(TAG, logTag + "Not publishing as we are not connected!");
+ return;
+ }
+ publisher.isPublishing()
+ .then(publishing -> {
+ if (publishing) {
+ logD(TAG, logTag + "Not publishing as we are already publishing!");
+ return;
+ }
+ if (!isAudioVideoCaptured()) {
+ logD(TAG, logTag + "Failed! Both audio & video are not captured.");
+ return;
+ }
+ logD(TAG, logTag + "Trying...");
+ startPubMc();
+ })
+ .error(e -> {
+ logD(TAG, logTag + e.getLocalizedMessage());
+ });
+
+ }).error(e -> {
+ logD(TAG, logTag + e.getLocalizedMessage());
+ });
}
/**
@@ -1737,51 +1768,20 @@ public void startPub() {
*/
public void stopPub() {
String logTag = "[Pub][Stop] ";
- if (!isPublishing()) {
- logD(TAG, logTag + "Not doing as we're not publishing!");
- return;
- }
-
- // Stop publishing
- logD(TAG, logTag + "Trying to stop publish...");
- boolean success = stopPubMc();
-
- if (success) {
- logD(TAG, logTag + "Publish stopped and we have disconnected.");
- setPubState(PublisherState.DISCONNECTED);
- } else {
- logD(TAG, logTag + "Failed! Stop publishing requirements not fulfilled. Check current states and any Millicast error message.");
- return;
- }
-
- enableStatsPub(0);
- logD(TAG, logTag + "Stats stopped");
-
- // Remove Publisher.
- publisher = null;
- logD(TAG, logTag + "Publisher removed.");
- logD(TAG, logTag + "OK.");
- }
-
- private void setRecordingEventHandlers(){
- String logTag = "[Pub][Rec] ";
-
- publisher.recording.onRecordingStarted(() -> {
- logD(TAG, logTag + "Recording started.");
- });
- publisher.recording.onRecordingStopped(() -> {
- logD(TAG, logTag + "Recording stopped.");
- });
- publisher.recording.onFailedToStartRecording(() -> {
- logD(TAG, logTag + "Failed to start recording.");
- });
- publisher.recording.onFailedToStopRecording(() -> {
- logD(TAG, logTag + "Failed to stop recording.");
+ publisher.isPublishing().then(publishing -> {
+ if (!publishing) {
+ logD(TAG, logTag + "Not doing as we're not publishing!");
+ return;
+ }
+ // Stop publishing
+ logD(TAG, logTag + "Trying to stop publish...");
+ stopPubMc();
+ }).error(e -> {
+ logD(TAG, logTag + e.getLocalizedMessage());
});
-
-
}
+
public PublishFragment getFragmentPub() {
return fragmentPub;
}
@@ -1822,38 +1822,31 @@ public void startSub() {
return;
}
- if (!subscriber.isConnected()) {
- if (subState == SubscriberState.CONNECTED) {
- logD(TAG, logTag + "Client.isConnected FALSE!!! " +
- "Continuing as subState is " + subState + ".");
- } else {
- logD(TAG, logTag + "Failed! Subscriber not connected!" +
- " subState is " + subState + ".");
+ subscriber.isConnected().then(connected -> {
+ if (!connected) {
+ logD(TAG, logTag + "Failed! Subscriber not connected!" + " subState is " + subState + ".");
+ return;
+ }
+ if (isSubscribing()) {
+ logD(TAG, logTag + "Not subscribing as we are already subscribing!");
return;
}
- }
-
- if (isSubscribing()) {
- logD(TAG, logTag + "Not subscribing as we are already subscribing!");
- return;
- }
-
- // Subscribe to Millicast
- logD(TAG, logTag + "Trying...");
- boolean success = startSubMc();
-
- if (success) {
- logD(TAG, logTag + "Starting subscribe...");
- } else {
- logD(TAG, logTag + "Failed! Start subscribe requirements not fulfilled. Check current states and any Millicast error message.");
- return;
- }
- // Get Subscriber stats every 10 seconds.
- int sec = 10;
- enableStatsSub(sec * 1000);
- logD(TAG, logTag + "Stats started. Collecting every " + sec + ".");
- logD(TAG, logTag + "OK.");
+ // Subscribe to Millicast
+ logD(TAG, logTag + "Trying...");
+ startSubMc().then(success -> {
+ logD(TAG, logTag + "OK. Starting subscribe to Millicast.");
+ // Enable Subscriber stats
+ logD(TAG, logTag + "Enabling stats...");
+ enableStatsSub(true);
+ logD(TAG, logTag + "OK.");
+ }).error(e -> {
+ logD(TAG, logTag + "Failed! Check current states and any Millicast error message.");
+ logD(TAG, logTag + "Failed!" + e.getLocalizedMessage());
+ });
+ }).error(e -> {
+ logD(TAG, logTag + "Failed!" + e.getLocalizedMessage());
+ });
}
/**
@@ -1869,17 +1862,9 @@ public void stopSub() {
// Stop subscribing
logD(TAG, logTag + "Trying to stop subscribe...");
- boolean success = stopSubMc();
+ stopSubMc();
- if (success) {
- logD(TAG, logTag + "Subscribe stopped and we have disconnected.");
- setSubState(SubscriberState.DISCONNECTED);
- } else {
- logD(TAG, logTag + "Failed! Stop subscribing requirements not fulfilled. Check current states and any Millicast error message.");
- return;
- }
-
- enableStatsSub(0);
+ enableStatsSub(false);
logD(TAG, logTag + "Stats stopped.");
// Remove Subscriber.
@@ -1986,6 +1971,7 @@ public SourceInfo getSourceProjected(boolean isAudio) {
*/
public boolean projectSource(String sourceId, boolean isAudio) {
String logTag = "[Source][Id][Project]:" + sourceId + " ";
+ Promise rval;
if (isAudio) {
logTag += "A ";
} else {
@@ -2022,7 +2008,7 @@ public boolean projectSource(String sourceId, boolean isAudio) {
}
// Generate the ProjectionData required from the MediaInfo.
- ArrayList projectionData =
+ ArrayList projectionData =
sourceInfo.getProjectionData(mid, isAudio);
if (projectionData == null) {
log = "Failed! ProjectData is not available for sourceInfo! " +
@@ -2034,29 +2020,29 @@ public boolean projectSource(String sourceId, boolean isAudio) {
// Send the request to Millicast and return the result of the request.
log = "Trying to project source onto mid:" + mid + " ...";
logD(TAG, logTag + log);
- boolean result = subscriber.project(sourceId, projectionData);
-
- // If successful, track the new sourceId.
- if (result) {
- log = "OK. Projected new ";
+ String finalLogTag = logTag;
+ subscriber.project(sourceId, projectionData).then(success -> {
+ String log2 = "[Sub][Project]";
+ // If successful, track the new sourceId.
+ log2 += "OK. Projected new ";
if (isAudio) {
- log += "Audio source.\n";
+ log2 += "Audio source.\n";
sourceIdAudioSub = sourceId;
- logD(TAG, logTag + log);
+ logD(TAG, finalLogTag + log2);
} else {
- log += "Video source.\n";
+ log2 += "Video source.\n";
sourceIdVideoSub = sourceId;
// Set view to display the Layers of the projected video source.
- log += "Setting new Layers of the project source in the view...";
- logD(TAG, logTag + log);
+ log2 += "Setting new Layers of the project source in the view...";
+ logD(TAG, finalLogTag + log2);
// Reset the Layers UI in the view.
loadViewSubLayer();
}
- } else {
- log = "Failed!";
- logD(TAG, logTag + log);
- }
- return result;
+ }).error(e -> {
+ logD(TAG, finalLogTag + "Failed! ");
+ logD(TAG, finalLogTag + e.getLocalizedMessage());
+ });
+ return true;
}
/**
@@ -2291,49 +2277,56 @@ public Optional getLayerData() {
* @param layerId
* @return True if the Layer of the layerId was (or already) selected, false otherwise.
*/
- public boolean selectLayer(String layerId) {
- String logTag = "[Layer][Select]:" + layerId + " ";
- String log;
+ public Promise selectLayer(String layerId) {
+ return new Promise<>((resolve, reject) -> {
+ String logTag = "[Layer][Select]:" + layerId + " ";
+ String log;
- if (layerId == null) {
- logD(TAG, logTag + "Failed! LayerId invalid.");
- return false;
- }
+ if (layerId == null) {
+ logD(TAG, logTag + "Failed! LayerId invalid.");
+ reject.call(new Throwable("LayerId invalid."));
- SourceInfo source = getSourceProjected(false);
- if (source == null) {
- logD(TAG, logTag + "Failed! No video source set.");
- return false;
- }
+ }
- // Check if already selected.
- String currentLayerId = getLayerActiveId();
- if (layerId.equals(currentLayerId)) {
- log = "OK. Already selected, not selecting again.";
- logD(TAG, logTag + log);
- return true;
- }
+ SourceInfo source = getSourceProjected(false);
+ if (source == null) {
+ logD(TAG, logTag + "Failed! No video source set.");
+ reject.call(new Throwable("No video source set."));
+ }
- // Get the layerData that we should select.
- Optional layerData = source.getLayerData(layerId);
- if (layerData == null) {
- logD(TAG, logTag + "Failed! Unable to get Layer.");
- return false;
- }
+ // Check if already selected.
+ String currentLayerId = getLayerActiveId();
+ if (layerId.equals(currentLayerId)) {
+ log = "OK. Already selected, not selecting again.";
+ logD(TAG, logTag + log);
+ reject.call(new Throwable("Already selected, not selecting again."));
+ }
- // Perform layer selection and return the result.
- if (subscriber.select(layerData)) {
- logD(TAG, logTag + "OK.");
- if (source.setLayerActiveId(layerId)) {
- logD(TAG, logTag + "Set new layer active layerId.");
- } else {
- logD(TAG, logTag + "Error! Failed to set new layer active layerId!");
+ // Get the layerData that we should select.
+ LayerData layerData = source.getLayerData(layerId).get();
+ if (layerData == null) {
+ logD(TAG, logTag + "Failed! Unable to get Layer.");
+ reject.call(new Throwable("Unable to get Layer."));
}
- return true;
- } else {
- logD(TAG, logTag + "Failed! Could not select layer!");
- return false;
- }
+
+ // Perform layer selection and return the result.
+ subscriber.select(layerData)
+ .then(success -> {
+ logD(TAG, logTag + "OK.");
+ if (source.setLayerActiveId(layerId)) {
+ logD(TAG, logTag + "Set new layer active layerId.");
+ resolve.call(true);
+ } else {
+ logD(TAG, logTag + "Error! Failed to set new layer active layerId!");
+ reject.call(new Throwable("Failed to set new layer active layerId!"));
+ }
+ }).error(e -> {
+ logD(TAG, logTag + "Failed! Could not select layer!");
+ logD(TAG, logTag + e.getLocalizedMessage());
+ reject.call(e);
+ });
+ });
+
}
/**
@@ -2400,30 +2393,31 @@ public int getSelectedIndex(ArrayList list) {
* @param isAudio
* @return
*/
- public boolean addMediaTrack(boolean isAudio) {
- String kind = "audio";
- if (!isAudio) {
- kind = "video";
- }
- return this.subscriber.addRemoteTrack(kind);
- }
- /**
- * Get the Mid of a Subscribed track by the given trackId.
- * The trackId can be obtained by calling {@link Track#getName()}.
- *
- * @param trackId
- * @return
- */
- public String getTrackMidSub(String trackId) {
- return this.subscriber.getMid(trackId).get();
- }
+ //TODO clean up
+// public boolean addMediaTrack(boolean isAudio) {
+// TrackType kind = TrackType.Audio;
+// if (!isAudio) {
+// kind = TrackType.Video;
+// }
+// return this.subscriber.addRemoteTrack(kind);
+// }
+//
+// /**
+// * Get the Mid of a Subscribed track by the given trackId.
+// * The trackId can be obtained by calling {@link Track#getName()}.
+// *
+// * @param trackId
+// * @return
+// */
+// public String getTrackMidSub(String trackId) {
+// return this.subscriber.getMid(trackId);
+// }
//**********************************************************************************************
// Utilities
//**********************************************************************************************
-
public Context getContext() {
return context;
}
@@ -2534,19 +2528,14 @@ public String getCodecName(boolean forAudio) {
* Enable or disable Publisher's WebRTC stats.
* Stats are only collected after Publisher is connected to Millicast.
*
- * @param enable The interval in ms between stats reports.
- * Set to 0 to disable stats.
+ * @param enable
*/
- public void enableStatsPub(int enable) {
+ public void enableStatsPub(boolean enable) {
if (publisher != null) {
String logTag = "[Pub][Stats][Enable] ";
- if (enable > 0) {
- publisher.getStats(enable);
- logD(TAG, logTag + "YES. Interval: " + enable + "ms.");
- } else {
- publisher.getStats(0);
- logD(TAG, logTag + "NO.");
- }
+ publisher.getStats(enable);
+ logD(TAG, logTag + enable);
+
}
}
@@ -2554,19 +2543,14 @@ public void enableStatsPub(int enable) {
* Enable or disable Subscriber's WebRTC stats.
* Stats are only collected after Subscriber is connected to Millicast.
*
- * @param enable The interval in ms between stats reports.
- * Set to 0 to disable stats.
+ * @param enable
*/
- public void enableStatsSub(int enable) {
+ public void enableStatsSub(boolean enable) {
if (subscriber != null) {
String logTag = "[Sub][Stats][Enable] ";
- if (enable > 0) {
- subscriber.getStats(enable);
- logD(TAG, logTag + "YES. Interval: " + enable + "ms.");
- } else {
- subscriber.getStats(0);
- logD(TAG, logTag + "NO.");
- }
+ subscriber.getStats(enable);
+ logD(TAG, logTag + enable);
+
}
}
@@ -2625,7 +2609,7 @@ private SwitchHdl getSwitchHdl() {
public boolean setCameraParams(String shootMode) {
boolean result = true;
try {
- VideoSource videoSource = MillicastManager.getSingleInstance().getVideoSource(false);
+ CameraVideoSource videoSource = MillicastManager.getSingleInstance().getVideoSource(false);
Camera.Parameters parameters = videoSource.getParameters();
Log.d(TAG, "setCameraParams: setting " + shootMode + "... ");
@@ -2737,7 +2721,7 @@ public void setMainActivity(Activity mainActivity) {
private Media getMedia() {
if (media == null) {
- media = Media.getInstance(context);
+ media = Media.INSTANCE;
}
return media;
}
@@ -2761,7 +2745,7 @@ public void run() {
logD(TAG, logTag + "Getting new audioSources.");
// Get new audioSources.
- audioSourceList = getMedia().getAudioSources();
+ audioSourceList = (ArrayList) getMedia().getAudioSources();
// Print out list of audioSources.
logD(TAG, logTag + "Checking for audioSources...");
@@ -2891,7 +2875,7 @@ public void run() {
logD(TAG, logTag + "Getting new videoSources.");
// Get new videoSources.
- videoSourceList = getMedia().getVideoSources();
+ videoSourceList = new ArrayList<>(getMedia().getVideoSources());
// Print out list of videoSources.
logD(TAG, logTag + "Checking for videoSources...");
@@ -2941,7 +2925,7 @@ public void run() {
*
* @param getSwitched If true, return videoSourceSwitched instead.
*/
- private VideoSource getVideoSource(boolean getSwitched) {
+ private CameraVideoSource getVideoSource(boolean getSwitched) {
String logTag = "[Source][Video][Get] ";
if (getSwitched) {
@@ -2973,7 +2957,7 @@ private void setVideoSource() {
String logTag = "[Source][Video][Set] ";
// Create new videoSource based on index.
- VideoSource videoSourceNew;
+ CameraVideoSource videoSourceNew;
if (videoSourceList == null) {
logD(TAG, logTag + "Failed as no valid videoSource was available!");
@@ -3000,7 +2984,7 @@ private void setVideoSource() {
return;
}
- videoSourceNew = videoSourceList.get(videoSourceIndex);
+ videoSourceNew = (CameraVideoSource) videoSourceList.get(videoSourceIndex);
String log;
if (videoSourceNew != null) {
@@ -3056,7 +3040,7 @@ private void setCapabilityList() {
String logTag = "[Capability][List][Set] ";
String log = logTag;
- VideoSource vs = null;
+ CameraVideoSource vs = null;
if (videoSourceSwitched != null) {
vs = videoSourceSwitched;
log += "From videoSourceSwitched.";
@@ -3068,7 +3052,7 @@ private void setCapabilityList() {
return;
}
- capabilityList = vs.getCapabilities();
+ capabilityList = (ArrayList) vs.getCapabilities();
logD(TAG, log);
int size = 0;
@@ -3222,16 +3206,14 @@ private Integer videoSourceIndexNextNonNdi(boolean ascending, int curIndex, Arra
int now = curIndex;
int next = videoSourceIndexNext(ascending, now, size);
// Keep searching for the next non-NDI until
- while (videoSourceList.get(next).getType() == NDI) {
- if (next == now) {
- logD(TAG, logTag + "Failed! 1 complete cycle done.");
- return null;
+ while (videoSourceList.get(next).getType() == DEVICE) {
+ if (next != now) {
+ return next;
}
- now = next;
- next = videoSourceIndexNext(ascending, now, size);
+ next = videoSourceIndexNext(ascending, next, size);
}
logD(TAG, logTag + next + " Failed! 1 complete cycle done.");
- return next;
+ return null;
}
/**
@@ -3410,7 +3392,7 @@ private void startCaptureVideo() {
" with Cap: " + getCapabilityStr(capability) + ".");
videoSource.setEventsHandler(getVideoSourceEvtHdl());
- VideoTrack videoTrack = (VideoTrack) videoSource.startCapture();
+ VideoTrack videoTrack = videoSource.startCapture();
if (videoSource.getType() == NDI) {
setCapState(CaptureState.IS_CAPTURED);
@@ -3576,7 +3558,7 @@ private void setAudioPlayback() {
String log;
if (audioPlaybackNew != null) {
- log = getAudioSourceStr(audioPlaybackNew, true);
+ log = getAudioPlaybackStr(audioPlaybackNew, true);
} else {
log = "None";
}
@@ -3720,7 +3702,7 @@ private void mirrorFrontCamera() {
}
CameraEnumerator cameraEnumerator;
String name = getVideoSource(true).getName();
- if (Media.isCamera2Supported(context)) {
+ if (Media.INSTANCE.isCamera2Supported()) {
cameraEnumerator = new Camera2Enumerator(context);
} else {
cameraEnumerator = new Camera1Enumerator();
@@ -3741,13 +3723,13 @@ private void mirrorFrontCamera() {
/**
* Set the current audio/videoCodec at the audio/videoCodecIndex of the current
* audio/videoCodecList,
- * and in the {@link Publisher.Option} as preferred codecs if available and NOT currently publishing.
+ * and in the {@link com.millicast.publishers.Option} as preferred codecs if available and NOT currently publishing.
*/
private void setCodecs() {
String logTag = "[Codec][Set] ";
final String none = "None";
- String ac = none;
- String vc = none;
+ String ac;
+ String vc;
getCodecList(true);
getCodecList(false);
@@ -3755,6 +3737,7 @@ private void setCodecs() {
logD(TAG, logTag + "Selecting a new one based on selected index.");
if (audioCodecList == null || audioCodecList.size() < 1) {
+ ac = none;
logD(TAG, logTag + "Failed to set audio codec as none was available!");
} else {
int size = audioCodecList.size();
@@ -3774,6 +3757,7 @@ private void setCodecs() {
}
if (videoCodecList == null || videoCodecList.size() < 1) {
+ vc = none;
logD(TAG, logTag + "Failed to set video codec as none was available!");
} else {
int size = videoCodecList.size();
@@ -3796,33 +3780,40 @@ private void setCodecs() {
logD(TAG, logTag + "Selected at index:" + audioCodecIndex + "/" + videoCodecIndex +
" is: " + ac + "/" + vc + ".");
- String log = logTag + "OK. ";
+ StringBuilder sb = new StringBuilder();
+ sb.append(logTag + "OK. ");
if (publisher != null) {
- if (!publisher.isPublishing()) {
- if (!none.equals(ac)) {
- audioCodec = ac;
- optionPub.audioCodec = Optional.of(ac);
- log += "Set preferred Audio:" + audioCodec + " on Publisher. ";
- } else {
- log += "Audio NOT set on Publisher.";
- }
- if (!none.equals(vc)) {
- videoCodec = vc;
- optionPub.videoCodec = Optional.of(vc);
- log += "Set preferred Video:" + videoCodec + " on Publisher.";
+ publisher.isPublishing().then(publishing -> {
+ if (!publishing) {
+ if (!none.equals(ac)) {
+ audioCodec = ac;
+ optionPub.setAudioCodec(ac);
+ sb.append("Set preferred Audio:" + audioCodec + " on Publisher. ");
+ } else {
+ sb.append("Audio NOT set on Publisher.");
+ }
+ if (!none.equals(vc)) {
+ videoCodec = vc;
+ optionPub.setVideoCodec(vc);
+ sb.append("Set preferred Video:" + videoCodec + " on Publisher.");
+ } else {
+ sb.append("Video NOT set on Publisher.");
+ }
+
} else {
- log += "Video NOT set on Publisher.";
+ sb.append("NOT set, as publishing is ongoing: ");
}
-
- } else {
- log += "NOT set, as publishing is ongoing: ";
- }
+ logD(TAG, sb.toString());
+ }).error(e -> {
+ logD(TAG, logTag + e.getLocalizedMessage());
+ });
} else {
- log += "NOT set, as publisher does not exists: ";
+ sb.append("NOT set, as publisher does not exists: ");
audioCodec = ac;
videoCodec = vc;
+ logD(TAG, sb.toString());
}
- logD(TAG, log);
+
}
/**
@@ -3865,79 +3856,78 @@ private Integer codecIndexNext(boolean ascending, boolean forAudio) {
* Millicast methods to connect to Millicast for publishing.
* Publishing credentials required.
* If connecting requirements are met, will return true and trigger SDK to start connecting to Millicast. Otherwise, will return false.
- * Actual connection success will be reported by {@link Publisher.Listener#onConnected()}.
- */
- private boolean connectPubMc() {
- String logTag = "[Pub][Con][Mc] ";
-
- // Set Credentials
- Publisher.Credential creds = publisher.getCredentials();
- creds.apiUrl = getUrlPub(CURRENT);
- creds.streamName = getStreamNamePub(CURRENT);
- creds.token = getTokenPub(CURRENT);
- publisher.setCredentials(creds);
- logD(TAG, logTag + "Set Credentials.");
-
- // Connect Publisher to Millicast.
- boolean success = false;
- String error = logTag + "Failed!";
- try {
- success = publisher.connect();
- } catch (Exception e) {
- error += " Error: " + e.getLocalizedMessage();
- logD(TAG, error);
- }
+ * Actual connection success will be reported by {@link PubListener#onConnected()}.
+ */
+ private Promise connectPubMc() {
+ return new Promise<>((resolve, reject) -> {
+ String logTag = "[Pub][Con][Mc] ";
+
+ // Set Credentials
+ CompatPublisherCredentials creds = publisher.getCredentials();
+ creds.setApiUrl(getUrlPub(CURRENT));
+ creds.setStreamName(getStreamNamePub(CURRENT));
+ creds.setToken(getTokenPub(CURRENT));
+ publisher.setCredentials(creds);
+ logD(TAG, logTag + "Set Credentials.");
+
+ // Connect Publisher to Millicast.
+ getPublisher().connect().then(success -> {
+ logD(TAG, logTag + "OK. Connecting to Millicast.");
+ resolve.call(true);
+ }).error(e -> {
+ logD(TAG, logTag + "Failed. Not Connecting to Millicast.");
+ logD(TAG, "Failed! Error: " + e.getLocalizedMessage());
+ reject.call(e);
+ });
+ });
- if (success) {
- logD(TAG, logTag + "OK. Connecting to Millicast.");
- } else {
- logD(TAG, error);
- }
- return success;
}
/**
* Millicast methods to connect to Millicast for subscribing.
* Subscribing credentials required.
* If connecting requirements are met, will return true and trigger SDK to start connecting to Millicast. Otherwise, will return false.
- * Actual connection success will be reported by {@link Subscriber.Listener#onConnected()}.
+ * Actual connection success will be reported by {@link com.millicast.subscribers.SubscriberListener#onConnected()}.
*/
- private boolean connectSubMc() {
+ private void connectSubMc() {
String logTag = "[Sub][Con][Mc] ";
// Set Credentials
- Subscriber.Credential creds = subscriber.getCredentials();
- creds.accountId = getAccountId(CURRENT);
- creds.streamName = getStreamNameSub(CURRENT);
- creds.apiUrl = getUrlSub(CURRENT);
+ CompatSubscriberCreds creds = subscriber.getCredentials();
+ creds.setAccountId(getAccountId(CURRENT));
+ creds.setStreamName(getStreamNameSub(CURRENT));
+ creds.setApiUrl(getUrlSub(CURRENT));
// If a Subscribe Token is required, add it.
// If it is not required:
// - There is no need to add it.
// - Adding a valid Subscribe Token is harmless.
String tokenSub = getTokenSub(CURRENT);
if (tokenSub != null && !tokenSub.isEmpty()) {
- creds.token = Optional.of(tokenSub);
+ creds.setToken(tokenSub);
logD(TAG, logTag + "Added Subscribe Token.");
}
subscriber.setCredentials(creds);
logD(TAG, logTag + "Set Credentials.");
// Connect Subscriber to Millicast.
- boolean success = false;
- String error = logTag + "Failed!";
+ StringBuilder error = new StringBuilder();
+ error.append(logTag + "Failed!");
try {
- success = subscriber.connect();
+ getSubscriber().connect().then(success -> {
+ logD(TAG, logTag + "OK. Connecting to Millicast.");
+ logD(TAG, logTag + "Initializing audio playback device...");
+ audioPlaybackStart();
+ }).error(e -> {
+ setSubState(SubscriberState.DISCONNECTED);
+ logD(TAG, logTag + "Failed! Connection requirements not fulfilled. Check inputs (e.g. credentials) and any Millicast error message.");
+ error.append(" Error: " + e.getLocalizedMessage());
+ logD(TAG, logTag + error.toString());
+
+ });
} catch (Exception e) {
- error += " Error: " + e.getLocalizedMessage();
- logD(TAG, error);
- }
+ logD(TAG, error.toString() + e.getLocalizedMessage());
- if (success) {
- logD(TAG, logTag + "OK. Connecting to Millicast.");
- } else {
- logD(TAG, error);
}
- return success;
}
//**********************************************************************************************
@@ -3966,14 +3956,14 @@ private PubListener getListenerPub() {
*
* @return
*/
- private Publisher getPublisher() {
+ private PublisherCompat getPublisher() {
if (publisher != null) {
logD(TAG, "[getPublisher] Returning existing Publisher.");
return publisher;
}
logD(TAG, "[getPublisher] Trying to create one...");
- publisher = Publisher.createPublisher(getListenerPub());
+ publisher = new PublisherCompat(getListenerPub());
logD(TAG, "[getPublisher] Created and returning a new Publisher.");
return publisher;
@@ -3982,31 +3972,37 @@ private Publisher getPublisher() {
/**
* Millicast methods to start publishing.
* Audio and video tracks that are already captured will be added to Publisher.
- * {@link Publisher.Option} (including preferred codecs) will be set into Publisher.
+ * {@link com.millicast.publishers.Option} (including preferred codecs) will be set into Publisher.
* If publishing requirements are met, will return true and trigger SDK to start publish. Otherwise, will return false.
- * Actual publishing success will be reported by {@link Publisher.Listener#onPublishing()}.
+ * Actual publishing success will be reported by {@link PubListener}.
*/
- private boolean startPubMc() {
+ private void startPubMc() {
String logTag = "[Pub][Start][Mc] ";
if (audioTrackPub != null) {
- publisher.addTrack(audioTrackPub);
- logD(TAG, logTag + "Audio track added.");
+ publisher.addTrack(audioTrackPub).then(success -> {
+ logD(TAG, logTag + "Audio track added.");
+ }).error(e -> {
+ logD(TAG, logTag + "Failed! Audio NOT track added.");
+ });
+
} else {
logD(TAG, logTag + "Audio track NOT added as it does not exist.");
}
if (videoTrackPub != null) {
- publisher.addTrack(videoTrackPub);
- logD(TAG, logTag + "Video track added.");
+ publisher.addTrack(videoTrackPub).then(success -> {
+ logD(TAG, logTag + "Video track added.");
+ }).error(e -> {
+ logD(TAG, logTag + "Failed! Video NOT track added.");
+ });
} else {
logD(TAG, logTag + "Video track NOT added as it does not exist.");
}
// Set Publisher Options
- Optional sourceIdPub = getOptSourceIdPub(CURRENT);
- optionPub.sourceId = sourceIdPub;
- optionPub.recordStream = Optional.of(this.recordingEnabledPub);
+ optionPub.setSourceId(getOptSourceIdPub(CURRENT).toString());
+ optionPub.setRecordStream(this.recordingEnabledPub);
logD(TAG, logTag + "SourceId (" + sourceIdPub + ") set in Option.");
setCodecs();
@@ -4020,43 +4016,56 @@ private boolean startPubMc() {
logD(TAG, logTag + "Options set in Publisher.");
// Publish to Millicast
- boolean success = publisher.publish();
- if (success) {
- logD(TAG, logTag + "OK. Starting publish to Millicast.");
- } else {
- logD(TAG, logTag + "Failed! Check current states and any Millicast error message.");
- }
- return success;
+
+ publisher.publish()
+ .then(success -> {
+ logD(TAG, logTag + "OK. Starting publish to Millicast.");
+ // Enable Publisher Stats
+ enableStatsPub(true);
+ logD(TAG, logTag + "OK.");
+ })
+ .error(e -> {
+ logD(TAG, logTag + e.getLocalizedMessage());
+ });
}
/**
* Millicast methods to stop publishing.
*/
- private boolean stopPubMc() {
+ private void stopPubMc() {
String logTag = "[Pub][Stop][Mc] ";
// Stop publishing to Millicast.
- boolean success = publisher.unpublish();
- if (success) {
- logD(TAG, logTag + "OK. Stopped publishing to Millicast.");
- } else {
- logD(TAG, logTag + "Failed!");
- }
- return success;
+ publisher.unpublish()
+ .then(success -> {
+ logD(TAG, logTag + "OK. Stopped publishing to Millicast.");
+ enableStatsPub(false);
+ logD(TAG, logTag + "Stats stopped");
+ setPubState(PublisherState.DISCONNECTED);
+ fragmentPub.setUI();
+ // Remove Publisher.
+ publisher = null;
+ logD(TAG, logTag + "Publisher removed.");
+ logD(TAG, logTag + "OK.");
+ })
+ .error(e -> {
+ logD(TAG, logTag + "Failed! Stop publishing requirements not fulfilled. Check current states and any Millicast error message.");
+ logD(TAG, logTag + e.getLocalizedMessage());
+ });
}
/**
* Check if we are currently publishing.
*/
- private boolean isPublishing() {
- String logTag = "[Pub][?] ";
- if (publisher == null || !publisher.isPublishing()) {
- logD(TAG, logTag + "No!");
- return false;
- }
- logD(TAG, logTag + "Yes.");
- return true;
- }
+// private boolean isPublishing() {
+// String logTag = "[Pub][?] ";
+// if (publisher == null || getPubState() != PublisherState.PUBLISHING) {
+// logD(TAG, logTag + "No!");
+// return false;
+// }
+// logD(TAG, logTag + "Yes.");
+// return true;
+// }
//**********************************************************************************************
// Subscribe
@@ -4084,25 +4093,25 @@ private SubListener getListenerSub() {
*
* @return
*/
- private Subscriber getSubscriber() {
+ private SubscriberCompat getSubscriber() {
if (subscriber != null) {
logD(TAG, "[getSubscriber] Returning existing Subscriber.");
return subscriber;
}
logD(TAG, "[getSubscriber] Trying to create one...");
- subscriber = Subscriber.createSubscriber(getListenerSub());
+ subscriber = new SubscriberCompat(getListenerSub());
logD(TAG, "[getSubscriber] Created and returning a new Subscriber.");
return subscriber;
}
/**
* Millicast methods to start subscribing.
- * {@link Subscriber.Option} will be set into Subscriber.
+ * {@link com.millicast.subscribers.Option} will be set into Subscriber.
* If subscribing requirements are met, will return true and trigger SDK to start subscribe. Otherwise, will return false.
- * Actual subscribing success will be reported by {@link Subscriber.Listener#onSubscribed()}.
+ * Actual subscribing success will be reported by {@link SubListener#onConnected()}.
*/
- private boolean startSubMc() {
+ private Promise startSubMc() {
String logTag = "[Pub][Start][Mc] ";
// Set Subscriber Options
@@ -4110,29 +4119,23 @@ private boolean startSubMc() {
logD(TAG, logTag + "Options set.");
// Subscribe to Millicast
- boolean success = subscriber.subscribe();
- if (success) {
- logD(TAG, logTag + "OK. Starting subscribe to Millicast.");
- } else {
- logD(TAG, logTag + "Failed! Check current states and any Millicast error message.");
- }
- return success;
+ logD(TAG, logTag + "Starting subscribe...");
+ return subscriber.subscribe();
}
/**
* Millicast methods to stop subscribing.
*/
- private boolean stopSubMc() {
+ private void stopSubMc() {
String logTag = "[Sub][Stop][Mc] ";
// Stop subscribing to Millicast.
- boolean success = subscriber.unsubscribe();
- if (success) {
+ subscriber.unsubscribe().then(success -> {
logD(TAG, logTag + "OK. Stopped subscribing to Millicast.");
- } else {
+ }).error(e -> {
logD(TAG, logTag + "Failed!");
- }
- return success;
+ logD(TAG, logTag + e.getLocalizedMessage());
+ });
}
/**
@@ -4155,7 +4158,7 @@ private boolean isSubscribing() {
/**
* Get a String that describes a MCVideoSource.
*/
- private String getAudioSourceStr(com.millicast.Source audioSource, boolean longForm) {
+ private String getAudioSourceStr(AudioSource audioSource, boolean longForm) {
String name = "Audio:";
if (audioSource == null) {
name += "NULL!";
@@ -4169,6 +4172,20 @@ private String getAudioSourceStr(com.millicast.Source audioSource, boolean longF
return name;
}
+ private String getAudioPlaybackStr(AudioPlayback audioPlayback, boolean longForm) {
+ String name = "Audio:";
+ if (audioPlayback == null) {
+ name += "NULL!";
+ return name;
+ }
+
+ name = "Audio:" + audioPlayback.getName();
+ if (longForm) {
+ name += " (" + audioPlayback.getType() + ") " + "id:" + audioPlayback.getId();
+ }
+ return name;
+ }
+
/**
* Get a String that describes a MCVideoSource.
*/
@@ -4195,12 +4212,12 @@ private String getCapabilityStr(VideoCapabilities cap) {
name = "Cap: N.A.";
} else {
// Note: FPS given in frames per 1000 seconds (FPKS).
- name = cap.width + "x" + cap.height + " fps:" + cap.fps / 1000;
+ name = cap.getWidth() + "x" + cap.getHeight() + " fps:" + cap.getFps() / 1000;
}
return name;
}
- public boolean isRecordingEnabledPub(){
+ public boolean isRecordingEnabledPub() {
return recordingEnabledPub;
}
diff --git a/app/src/main/java/com/millicast/android_app/PubListener.java b/app/src/main/java/com/millicast/android_app/PubListener.java
index 30f4763..a6bd7e9 100644
--- a/app/src/main/java/com/millicast/android_app/PubListener.java
+++ b/app/src/main/java/com/millicast/android_app/PubListener.java
@@ -1,6 +1,15 @@
package com.millicast.android_app;
import com.millicast.Publisher;
+import com.millicast.clients.stats.AudioSource;
+import com.millicast.clients.stats.Codecs;
+import com.millicast.clients.stats.InboundRtpStream;
+import com.millicast.clients.stats.OutboundRtpStream;
+import com.millicast.clients.stats.RemoteInboundRtpStream;
+import com.millicast.clients.stats.RemoteOutboundRtpStream;
+import com.millicast.clients.stats.RtsReport;
+import com.millicast.clients.stats.VideoSource;
+import com.millicast.publishers.listener.PublisherListener;
import org.webrtc.RTCStatsReport;
@@ -10,11 +19,15 @@
import static com.millicast.android_app.Utils.logD;
import static com.millicast.android_app.Utils.makeSnackbar;
+import androidx.annotation.NonNull;
+
+import java.util.ArrayList;
+
/**
* Implementation of Publisher's Listener.
* This handles events sent to the Publisher being listened to.
*/
-public class PubListener implements Publisher.Listener {
+public class PubListener implements PublisherListener {
public static final String TAG = "PubListener";
private MillicastManager mcMan;
@@ -50,6 +63,9 @@ public void onConnected() {
@Override
public void onDisconnected() {
String logTag = logTagClass + "[Con][On][X] ";
+ logD(TAG, logTag + "Publish stopped and we have disconnected.");
+ mcMan.setPubState(MCStates.PublisherState.DISCONNECTED);
+ mcMan.getFragmentPub().setUI();
makeSnackbar(logTag, "Disconnected", mcMan.getFragmentPub());
}
@@ -67,11 +83,29 @@ public void onSignalingError(String s) {
makeSnackbar(logTag, "Signaling Error:" + s, mcMan.getFragmentPub());
}
+
@Override
- public void onStatsReport(RTCStatsReport statsReport) {
- String logTag = logTagClass + "[Stat] ";
- String log = statsReport.toString();
- logD(TAG, log, logTag);
+ public void onStatsReport(@NonNull RtsReport rtsReport) {
+ String logTag = logTagClass + "[Stat]";
+ StringBuilder log = new StringBuilder();
+ rtsReport.stats().forEach(stat -> {
+ switch (stat.statsType()){
+ case CODEC -> {
+ log.append("[CODEC] " + ((Codecs)stat).getMimeType());
+ }
+ case INBOUND_RTP -> {
+ log.append("[INBOUND_RTP] " + "FPS :" + ((InboundRtpStream)stat).getFramesPerSecond());
+ }
+ case OUTBOUND_RTP -> {
+ log.append("[OUTBOUND_RTP] " + "FPS :"+ ((OutboundRtpStream)stat).getFramesPerSecond());
+ }
+ case VIDEO_SOURCE -> {
+ log.append("[VIDEO_SOURCE] " + "FPS :"+ ((VideoSource)stat).getFramesPerSecond());
+ }
+ }
+
+ });
+ logD(TAG, log.toString(), logTag);
}
@Override
@@ -107,4 +141,30 @@ private void setUI() {
});
}
+ @Override
+ public void onTransformableFrame(int i, int i1, @NonNull ArrayList arrayList) {
+
+ }
+
+ public void onRecordingStarted(){
+ logD(TAG, "[Rec][Start]", "Started recording");
+ mcMan.setRecordingEnabled(true);
+ mcMan.getFragmentPub().setUI();
+ }
+
+ public void onRecordingStopped(){
+ logD(TAG, "[Rec][Stop]", "Stopped recording");
+ mcMan.setRecordingEnabled(false);
+ mcMan.getFragmentPub().setUI();
+
+ }
+
+ public void onFailedToStartRecording(){
+ logD(TAG, "[Rec][Start]"+"Failed to start recording");
+ }
+
+ public void onFailedToStopRecording(){
+ logD(TAG, "[Rec][Stop]", "Failed to stop recording");
+
+ }
}
diff --git a/app/src/main/java/com/millicast/android_app/PublishFragment.java b/app/src/main/java/com/millicast/android_app/PublishFragment.java
index 2b400cb..dd33470 100644
--- a/app/src/main/java/com/millicast/android_app/PublishFragment.java
+++ b/app/src/main/java/com/millicast/android_app/PublishFragment.java
@@ -23,9 +23,9 @@
import java.util.ArrayList;
-import com.millicast.AudioTrack;
import com.millicast.VideoRenderer;
-import com.millicast.VideoTrack;
+import com.millicast.devices.track.AudioTrack;
+import com.millicast.devices.track.VideoTrack;
import org.webrtc.RendererCommon;
@@ -293,17 +293,8 @@ private void toggleAudioOnly(CompoundButton buttonView, boolean isChecked) {
}
private void toggleRecordingEnabled(View view){
- if(mcMan.isRecordingEnabledPub()){
- if(mcMan.setRecordingEnabled(false)){
- buttonRecording.setText("Recording:F");
- };
-
- }
- else{
- if(mcMan.setRecordingEnabled(true)) {
- buttonRecording.setText("Recording:T");
- }
- }
+ logD(TAG, "[Rec]", "Toggling recording...");
+ mcMan.toggleRecordingEnabled();
}
@@ -477,6 +468,7 @@ private void onStartPublishClicked(View view) {
private void onStopPublishClicked(View view) {
Log.d(TAG, "Stop Publish clicked.");
+
mcMan.stopPub();
setUI();
}
@@ -668,6 +660,14 @@ void setUI() {
buttonPublish.setEnabled(true);
break;
}
+
+ if(mcMan.isRecordingEnabledPub()){
+ buttonRecording.setText("Recording: ON");
+ }
+ else{
+ buttonRecording.setText("Recording: OFF");
+ }
+
setMuteButtons(true);
}
diff --git a/app/src/main/java/com/millicast/android_app/SettingsMediaFragment.java b/app/src/main/java/com/millicast/android_app/SettingsMediaFragment.java
index bcba09f..c797814 100644
--- a/app/src/main/java/com/millicast/android_app/SettingsMediaFragment.java
+++ b/app/src/main/java/com/millicast/android_app/SettingsMediaFragment.java
@@ -18,9 +18,9 @@
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
-import com.millicast.AudioSource;
-import com.millicast.VideoCapabilities;
-import com.millicast.VideoSource;
+import com.millicast.devices.source.audio.AudioSource;
+import com.millicast.devices.source.video.VideoCapabilities;
+import com.millicast.devices.source.video.VideoSource;
import java.util.ArrayList;
diff --git a/app/src/main/java/com/millicast/android_app/SourceInfo.java b/app/src/main/java/com/millicast/android_app/SourceInfo.java
index 9bbab6c..fe2e204 100644
--- a/app/src/main/java/com/millicast/android_app/SourceInfo.java
+++ b/app/src/main/java/com/millicast/android_app/SourceInfo.java
@@ -5,8 +5,9 @@
import androidx.annotation.NonNull;
-import com.millicast.LayerData;
import com.millicast.Subscriber;
+import com.millicast.subscribers.ProjectionData;
+import com.millicast.subscribers.state.LayerData;
import java.util.ArrayList;
import java.util.HashMap;
@@ -80,14 +81,14 @@ public boolean hasVideo() {
}
/**
- * Get an ArrayList of {@link com.millicast.Subscriber.ProjectionData} for all the
+ * Get an ArrayList of {@link ProjectionData} for all the
* audio or video tracks of this MediaInfo.
*
* @param mid The mid onto which this source is requested to be projected onto.
* @param forAudio
* @return
*/
- public ArrayList getProjectionData(String mid, boolean forAudio) {
+ public ArrayList getProjectionData(String mid, boolean forAudio) {
String[] trackList = trackIdAudioList;
String mediaType = "audio";
@@ -96,7 +97,7 @@ public ArrayList getProjectionData(String mid, boolea
mediaType = "video";
}
- ArrayList result = null;
+ ArrayList result = null;
if (trackList == null || trackList.length < 1) {
return result;
}
@@ -104,11 +105,8 @@ public ArrayList getProjectionData(String mid, boolea
result = new ArrayList<>();
for (String trackId : trackList) {
- Subscriber.ProjectionData data = new Subscriber.ProjectionData();
- data.trackId = trackId;
- data.media = mediaType;
- data.mid = mid;
-
+ ProjectionData data = new ProjectionData(trackId,mediaType,mid,null);
+ //data.copy(trackId,mediaType,mid,null);
result.add(data);
}
@@ -347,19 +345,16 @@ public static String getLayerStr(LayerData ld, boolean longForm) {
name += "N.A.";
return name;
}
-
- name += ld.encodingId;
- String temStr = "" + ld.temporalLayerId;
- String spaStr = "" + ld.spatialLayerId;
+ name += ld.getEncodingId();
+ String temStr = "" + ld.getTemporalLayerId();
+ String spaStr = "" + ld.getSpatialLayerId();
if (longForm) {
String maxTemLayer = "-";
- if (ld.maxTemporalLayerId.isPresent()) {
- maxTemLayer = "" + ld.maxTemporalLayerId.get();
- }
+ maxTemLayer = "" + ld.getMaxTemporalLayerId();
+
String maxSpaLayer = "-";
- if (ld.maxSpatialLayerId.isPresent()) {
- maxSpaLayer = "" + ld.maxSpatialLayerId.get();
- }
+
+ maxSpaLayer = "" + ld.getMaxSpatialLayerId();
temStr += "/" + maxTemLayer;
spaStr += "/" + maxSpaLayer;
}
diff --git a/app/src/main/java/com/millicast/android_app/SubListener.java b/app/src/main/java/com/millicast/android_app/SubListener.java
index 1e20b7f..466548c 100644
--- a/app/src/main/java/com/millicast/android_app/SubListener.java
+++ b/app/src/main/java/com/millicast/android_app/SubListener.java
@@ -1,13 +1,20 @@
package com.millicast.android_app;
-import com.millicast.AudioTrack;
-import com.millicast.LayerData;
+import com.millicast.clients.stats.Codecs;
+import com.millicast.clients.stats.InboundRtpStream;
+import com.millicast.clients.stats.OutboundRtpStream;
+import com.millicast.clients.stats.RtsReport;
+import com.millicast.clients.stats.VideoSource;
+import com.millicast.devices.track.AudioTrack;
import com.millicast.Subscriber;
-import com.millicast.VideoTrack;
+import com.millicast.devices.track.VideoTrack;
+import com.millicast.subscribers.SubscriberListener;
+import com.millicast.subscribers.state.LayerData;
import org.webrtc.RTCStatsReport;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Optional;
import static com.millicast.android_app.MCStates.SubscriberState.CONNECTED;
@@ -15,11 +22,13 @@
import static com.millicast.android_app.Utils.logD;
import static com.millicast.android_app.Utils.makeSnackbar;
+import androidx.annotation.NonNull;
+
/**
* Implementation of Subscriber's Listener.
* This handles events sent to the Subscriber being listened to.
*/
-public class SubListener implements Subscriber.Listener {
+public class SubListener implements SubscriberListener {
public static final String TAG = "SubListener";
private final MillicastManager mcMan;
@@ -48,6 +57,7 @@ public void onConnected() {
String logTag = logTagClass + "[Con][On] ";
mcMan.setSubState(CONNECTED);
makeSnackbar(logTag, "Connected", mcMan.getFragmentSub());
+ mcMan.getFragmentSub().setUI();
mcMan.startSub();
}
@@ -69,6 +79,11 @@ public void onConnectionError(int status, String reason) {
public void onStopped() {
String logTag = logTagClass + "[Stop] ";
logD(TAG, logTag + "OK.");
+ logD(TAG, logTag + "Subscribe stopped and we have disconnected.");
+ mcMan.setSubState(MCStates.SubscriberState.DISCONNECTED);
+ setUI();
+ makeSnackbar(logTag, "Subscriber Stopped", mcMan.getFragmentSub());
+
}
@Override
@@ -77,21 +92,41 @@ public void onSignalingError(String s) {
makeSnackbar(logTag, "Signaling Error:" + s, mcMan.getFragmentSub());
}
+
+
@Override
- public void onStatsReport(RTCStatsReport statsReport) {
+ public void onStatsReport(@NonNull RtsReport rtsReport) {
String logTag = logTagClass + "[Stat] ";
- String log = statsReport.toString();
- logD(TAG, log, logTag);
- statsReport.getStatsMap().forEach((key,stat) -> {
- if (stat.getType().equals("codec")){
- String[] codec = stat.getMembers().get("mimeType").toString().split("/");
- ArrayList codecs = mcMan.getCodecList(codec[0].equals("audio"));
- if (!codecs.contains(codec[1])){
- makeSnackbar(logTag, "Unsupported codec: "+codec[1],mcMan.getFragmentSub());
+ StringBuilder log = new StringBuilder();
+ rtsReport.stats().forEach(stat -> {
+ switch (stat.statsType()){
+ case CODEC -> {
+ var codecs = (Codecs)stat;
+ log.append("[CODEC] " + codecs.getMimeType());
+ if(((Codecs) stat).getMimeType().startsWith("video")){
+ try{
+ String videoCodec = codecs.getMimeType().split("/")[1];
+ var supportedCodecs = mcMan.getCodecList(false);
+ if (!supportedCodecs.contains(videoCodec)){
+ makeSnackbar(TAG, "Unsupported Video Codec: "+videoCodec, mcMan.getFragmentSub());
+ }
+ }catch (Exception e){}
+ }
+
+ }
+ case INBOUND_RTP -> {
+ log.append("[INBOUND_RTP] " + "FPS :" + ((InboundRtpStream)stat).getFramesPerSecond());
+ }
+ case OUTBOUND_RTP -> {
+ log.append("[OUTBOUND_RTP] " + "FPS :"+ ((OutboundRtpStream)stat).getFramesPerSecond());
+ }
+ case VIDEO_SOURCE -> {
+ log.append("[VIDEO_SOURCE] " + "FPS :"+ ((VideoSource)stat).getFramesPerSecond());
}
}
- });
+ });
+ logD(TAG, log.toString(), logTag);
}
@@ -202,17 +237,18 @@ public void onInactive(String streamId, Optional sourceId) {
* @param inactiveLayers inactive simulcast/SVC layers
*/
@Override
- public void onLayers(String mid, LayerData[] activeLayers, LayerData[] inactiveLayers) {
+ public void onLayers(String mid, LayerData[] activeLayers, String[] inactiveLayers) {
String logTag = logTagClass + "[Layer] ";
String log = "mid:" + mid + " Active(" + activeLayers.length + "):[" +
SourceInfo.getLayerListStr(activeLayers) + "]," +
" Inactive(" + inactiveLayers.length + "):[" +
- SourceInfo.getLayerListStr(inactiveLayers) + "].";
+ Arrays.stream(inactiveLayers).sequential().reduce((a, b) -> a + " " + b) + "].";
logD(TAG, logTag + log);
mcMan.setLayerActiveList(activeLayers);
}
+
/**
* Called when a source id is being multiplexed into the audio track based on the voice activity level.
*
@@ -255,4 +291,9 @@ private void setUI() {
}
});
}
+
+ @Override
+ public void onFrameMetadata(int i, int i1, @NonNull byte[] bytes) {
+
+ }
}
diff --git a/app/src/main/java/com/millicast/android_app/SubscribeFragment.java b/app/src/main/java/com/millicast/android_app/SubscribeFragment.java
index edb2bde..a5e57e2 100644
--- a/app/src/main/java/com/millicast/android_app/SubscribeFragment.java
+++ b/app/src/main/java/com/millicast/android_app/SubscribeFragment.java
@@ -18,10 +18,10 @@
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
-import com.millicast.AudioTrack;
-import com.millicast.LayerData;
+import com.millicast.devices.track.AudioTrack;
import com.millicast.VideoRenderer;
-import com.millicast.VideoTrack;
+import com.millicast.devices.track.VideoTrack;
+import com.millicast.subscribers.state.LayerData;
import org.webrtc.RendererCommon;
@@ -34,6 +34,7 @@
import java.util.ArrayList;
import java.util.Optional;
+import java.util.concurrent.atomic.AtomicReference;
/**
* UI for subscribing.
@@ -411,46 +412,46 @@ void loadLayerSpinner(ArrayList spinnerList, Utils.GetSelectedIndex lamb
populateSpinner(spinnerList, spinner, lambda, mcMan.getContext(), (int position) -> {
String logTagSet = "[View][Layer][Spinner][Set] ";
- String logSet;
+ AtomicReference logSet = new AtomicReference<>();
String layerId = spinnerList.get(position);
- logSet = "Selected Pos: " + position + " LayerId:" + layerId + ".";
+ logSet.set("Selected Pos: " + position + " LayerId:" + layerId + ".");
logD(TAG, logTagSet + logSet);
// Select the layer and if not possible
- if (!mcMan.selectLayer(layerId)) {
-
- // Reset to currently selected layerId if possible.
- logSet = "Failed! Resetting to currently selected layerId...";
- logD(TAG, logTagSet + logSet);
-
- int previousIndex = lambda.getSelectedIndex(spinnerList);
- if (previousIndex >= 0) {
- String resetSelection = spinnerList.get(previousIndex);
- spinner.setSelection(previousIndex);
- logSet = "OK. Spinner reset to:" + resetSelection + ".";
- logD(TAG, logTagSet + logSet);
- return;
- }
-
- // Otherwise reset to automatic selection.
- logSet = "Failed! Resetting to automatic selection...";
- logD(TAG, logTagSet + logSet);
-
- String resetLayerId = "";
- mcMan.selectLayer("");
- int resetIndex = spinnerList.indexOf(resetLayerId);
- if (resetIndex > 0) {
- spinner.setSelection(resetIndex);
- logSet = "OK. Spinner reset to index:" + resetLayerId + ".";
- } else {
- logSet = "Failed! Unable to find index of resetLayerId(" + resetLayerId +
- ") as it is not in the current spinnerList:" + spinnerList + ".";
- }
- logD(TAG, logTagSet + logSet);
- } else {
- logSet = "OK.";
- logD(TAG, logTagSet + logSet);
- }
+ mcMan.selectLayer(layerId)
+ .then(success -> {
+ logD(TAG, logTagSet + "OK");
+ })
+ .error(e -> {
+ // Reset to currently selected layerId if possible.
+ logSet.set("Failed! Resetting to currently selected layerId...");
+ logD(TAG, logTagSet + logSet);
+
+ int previousIndex = lambda.getSelectedIndex(spinnerList);
+ if (previousIndex >= 0) {
+ String resetSelection = spinnerList.get(previousIndex);
+ spinner.setSelection(previousIndex);
+ logSet.set("OK. Spinner reset to:" + resetSelection + ".");
+ logD(TAG, logTagSet + logSet);
+ return;
+ }
+
+ // Otherwise reset to automatic selection.
+ logSet.set("Failed! Resetting to automatic selection...");
+ logD(TAG, logTagSet + logSet);
+
+ String resetLayerId = "";
+ mcMan.selectLayer("");
+ int resetIndex = spinnerList.indexOf(resetLayerId);
+ if (resetIndex > 0) {
+ spinner.setSelection(resetIndex);
+ logSet.set("OK. Spinner reset to index:" + resetLayerId + ".");
+ } else {
+ logSet.set("Failed! Unable to find index of resetLayerId(" + resetLayerId +
+ ") as it is not in the current spinnerList:" + spinnerList + ".");
+ }
+ logD(TAG, logTagSet + logSet);
+ });
});
}
diff --git a/app/src/main/java/com/millicast/android_app/SwitchHdl.java b/app/src/main/java/com/millicast/android_app/SwitchHdl.java
index f5f593f..274aeb2 100644
--- a/app/src/main/java/com/millicast/android_app/SwitchHdl.java
+++ b/app/src/main/java/com/millicast/android_app/SwitchHdl.java
@@ -1,6 +1,7 @@
package com.millicast.android_app;
-import com.millicast.VideoSource;
+import com.millicast.devices.source.video.VideoSource;
+import com.millicast.devices.type.SwitchCameraHandler;
import static com.millicast.android_app.Utils.logD;
@@ -8,7 +9,7 @@
* Implementation of VideoSource's camera switch listener.
* This handles camera switch events that allows us to know the outcome and details of camera switching.
*/
-class SwitchHdl implements VideoSource.SwitchCameraHandler {
+class SwitchHdl implements SwitchCameraHandler {
public static final String TAG = "SwitchHdl";
private String logTag = "[Video][Source][Cam][Switch][Hdl] ";
diff --git a/app/src/main/java/com/millicast/android_app/VideoSourceEvtHdl.java b/app/src/main/java/com/millicast/android_app/VideoSourceEvtHdl.java
index 0491d2d..d918e48 100644
--- a/app/src/main/java/com/millicast/android_app/VideoSourceEvtHdl.java
+++ b/app/src/main/java/com/millicast/android_app/VideoSourceEvtHdl.java
@@ -2,7 +2,8 @@
import android.os.Handler;
-import com.millicast.VideoSource;
+import com.millicast.devices.source.video.VideoSource;
+import com.millicast.devices.type.EventsHandler;
import static com.millicast.android_app.MCStates.*;
import static com.millicast.android_app.MCTypes.Source.CURRENT;
@@ -13,7 +14,7 @@
* Implementation of VideoSource's event listener.
* This handles camera events that allows us to know the outcome and details of camera operations.
*/
-class VideoSourceEvtHdl implements VideoSource.EventsHandler {
+class VideoSourceEvtHdl implements EventsHandler {
public static final String TAG = "VidSrcEvtHdl";
private MillicastManager mcMan;
diff --git a/app/src/main/java/com/millicast/android_app/compat/CompatBase.kt b/app/src/main/java/com/millicast/android_app/compat/CompatBase.kt
new file mode 100644
index 0000000..483bf6d
--- /dev/null
+++ b/app/src/main/java/com/millicast/android_app/compat/CompatBase.kt
@@ -0,0 +1,49 @@
+package com.millicast.android_app.compat
+
+import android.util.Log
+import com.millicast.publishers.BitrateSettings
+import com.voxeet.promise.Promise
+import com.voxeet.promise.PromiseDebug
+import com.voxeet.promise.solve.Solver
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.async
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import java.util.logging.Logger
+
+open abstract class CompatBase() {
+ val scope = MainScope();
+
+ fun promise(block: suspend () -> T): Promise {
+ return Promise { solver ->
+ run{
+ GlobalScope.launch {
+ try {
+ solver.resolve(block());
+ } catch (err: Throwable) {
+ solver.reject(err)
+ }
+ };
+ }
+ };
+ }
+
+
+}
+class CompatBitrateSettings{
+ var disableBWE: Boolean = false
+
+ var maxBitrateKbps: Int? = null;
+
+ var minBitrateKbps : Int? = null
+
+
+ fun get() : BitrateSettings {
+ return BitrateSettings(
+ this.disableBWE,
+ this.maxBitrateKbps,
+ this.minBitrateKbps
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/millicast/android_app/compat/PublisherCompat.kt b/app/src/main/java/com/millicast/android_app/compat/PublisherCompat.kt
new file mode 100644
index 0000000..906d685
--- /dev/null
+++ b/app/src/main/java/com/millicast/android_app/compat/PublisherCompat.kt
@@ -0,0 +1,145 @@
+package com.millicast.android_app.compat
+
+import android.util.Log
+import com.millicast.Core
+import com.millicast.android_app.PubListener
+import com.millicast.clients.ConnectionOptions
+import com.millicast.clients.state.ConnectionState
+import com.millicast.devices.track.AudioTrack
+import com.millicast.devices.track.Track
+import com.millicast.devices.track.VideoTrack
+import com.millicast.publishers.BitrateSettings
+import com.millicast.publishers.Option
+import com.millicast.publishers.PublisherState
+import com.millicast.publishers.state.PublishingState
+import com.millicast.publishers.state.RecordingState
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.launch
+
+
+class PublisherCompat(pubListener: PubListener) : CompatBase() {
+
+ val publisher = Core.createPublisher();
+ val listener = pubListener;
+ var credentials = CompatPublisherCredentials();
+ var options = CompatPublisherOptions();
+ var state = PublisherState();
+ fun connect() = promise {
+ stateChange();
+ val connOpts = ConnectionOptions(false);
+ publisher.enableStats(true)
+ publisher.setCredentials(credentials.get());
+ onStatsReport()
+ publisher.connect(connectionOptions = connOpts);
+ }
+ fun stateChange() = scope.launch {
+ publisher.state
+ .distinctUntilChanged()
+ .collect { newState: PublisherState ->
+ if(newState.connectionState != state.connectionState){
+ when(newState.connectionState){
+ ConnectionState.Connected -> listener.onConnected()
+ ConnectionState.Connecting -> {}
+ ConnectionState.Default -> {}
+ ConnectionState.Disconnected -> listener.onDisconnected()
+ ConnectionState.Disconnecting -> {}
+ is ConnectionState.DisconnectedError -> {
+ val error = newState.connectionState as ConnectionState.DisconnectedError;
+ listener.onConnectionError(error.httpCode,error.reason);
+ }
+ };
+ }
+
+ if(newState.publishingState != state.publishingState){
+ when(newState.publishingState){
+ is PublishingState.Error -> {
+ val error = newState.publishingState as PublishingState.Error
+ listener.onPublishingError(error.reason)
+ }
+ PublishingState.Started -> listener.onPublishing()
+ PublishingState.Stopped -> {}
+ }
+ }
+
+
+ if(newState.recordingState != state.recordingState){
+ when(newState.recordingState){
+ RecordingState.Started -> listener.onRecordingStarted()
+ RecordingState.StartedFailed -> listener.onFailedToStartRecording()
+ RecordingState.Stopped -> listener.onRecordingStopped()
+ RecordingState.StoppedFailed -> listener.onFailedToStopRecording()
+ }
+ }
+
+ if(newState.viewers != state.viewers){
+ listener.onViewerCount(newState.viewers);
+ }
+ state = newState;
+ }
+ }
+
+ fun getStats(interval: Boolean) = promise { publisher.enableStats(interval) }
+
+ fun isConnected() = promise { publisher.isConnected }
+
+ fun isRecording() = state.recordingState == RecordingState.Started;
+
+ fun release() = publisher.release();
+ fun addTrack(audioTrackPub: AudioTrack) = promise { publisher.addTrack(audioTrackPub as Track); }
+
+ fun addTrack(videoTrackPub: VideoTrack) = promise { publisher.addTrack(videoTrackPub as Track) }
+
+ fun publish() = promise {
+ publisher.setCredentials(credentials.get())
+ publisher.publish(options.get());
+ }
+
+ fun startRecording() = promise { publisher.recording.start() }
+
+ fun stopRecording() = promise { publisher.recording.stop() }
+
+ fun unpublish() = promise { publisher.unpublish() }
+
+ fun isPublishing() = promise { publisher.isPublishing }
+
+ fun onStatsReport() = scope.launch {
+ publisher.rtcStatsReport.distinctUntilChanged().collect{ report -> listener.onStatsReport(report) }
+ }
+}
+
+class CompatPublisherOptions(){
+ var stereo : Boolean = true;
+ var recordStream = false;
+ var sourceId = "";
+ var bitrateSettings = CompatBitrateSettings();
+ var audioCodec = "";
+ var videoCodec = ""
+
+ fun get() : Option {
+ return Option(
+ stereo = this.stereo,
+ recordStream = this.recordStream,
+ sourceId = null,
+ bitrateSettings = BitrateSettings(
+ minBitrateKbps = bitrateSettings.minBitrateKbps,
+ maxBitrateKbps = bitrateSettings.maxBitrateKbps,
+ disableBWE = bitrateSettings.disableBWE
+ ),
+ videoCodec = this.videoCodec,
+ audioCodec = this.audioCodec
+ )
+ }
+}
+
+class CompatPublisherCredentials() {
+ var apiUrl = "";
+ var streamName = "";
+ var token = "";
+
+ fun get() = com.millicast.publishers.Credential(
+ streamName = this.streamName,
+ token = this.token,
+ apiUrl = this.apiUrl,
+
+ );
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/millicast/android_app/compat/SubscriberCompat.kt b/app/src/main/java/com/millicast/android_app/compat/SubscriberCompat.kt
new file mode 100644
index 0000000..da684c9
--- /dev/null
+++ b/app/src/main/java/com/millicast/android_app/compat/SubscriberCompat.kt
@@ -0,0 +1,156 @@
+package com.millicast.android_app.compat
+
+import android.util.Log
+import com.millicast.Core
+import com.millicast.clients.state.ConnectionState
+import com.millicast.devices.track.AudioTrack
+import com.millicast.devices.track.TrackType
+import com.millicast.devices.track.VideoTrack
+import com.millicast.subscribers.Credential
+import com.millicast.subscribers.Option
+import com.millicast.subscribers.ProjectionData
+import com.millicast.subscribers.SubscriberListener
+import com.millicast.subscribers.SubscriberState
+import com.millicast.subscribers.state.ActivityStream
+import com.millicast.subscribers.state.LayerData
+import com.millicast.subscribers.state.SubscriptionState
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.launch
+import java.util.Optional
+
+
+class SubscriberCompat(subscriberListener: SubscriberListener) : CompatBase() {
+ val subscriber = Core.createSubscriber();
+ val listener = subscriberListener;
+ var credentials = CompatSubscriberCreds();
+ var options = CompatSubscriberOptions();
+ var state = SubscriberState();
+ fun connect() = promise {
+ stateChange();
+ activityChange()
+ onTrack();
+ onLayers();
+ onVad();
+ subscriber.setCredentials(credentials.get());
+ subscriber.connect()
+ }
+ fun stateChange() = scope.launch {
+ // given a known subscriber object
+ subscriber.state
+ .distinctUntilChanged()
+ .collect { newState: SubscriberState ->
+ // and then perform any operation that fit our logic
+ // here just logging those
+ Log.d("[Kotlin]", "new state received $newState")
+ if(!newState.connectionState.equals(state.connectionState)){
+ when (newState.connectionState){
+ ConnectionState.Connected -> listener.onConnected()
+ ConnectionState.Connecting -> {}
+ ConnectionState.Default -> {}
+ ConnectionState.Disconnected -> listener.onDisconnected()
+ is ConnectionState.DisconnectedError -> {
+ val error = newState.connectionState as ConnectionState.DisconnectedError;
+ listener.onConnectionError(error.httpCode,error.reason);
+ }
+ ConnectionState.Disconnecting -> {}
+ }
+ }
+ if(newState.subscriptionState != state.subscriptionState){
+ when(newState.subscriptionState){
+ SubscriptionState.Default -> {}
+ is SubscriptionState.Error -> {
+ val error = newState.subscriptionState as SubscriptionState.Error;
+ listener.onSubscribedError(error.reason);
+ }
+ SubscriptionState.Stopped -> listener.onStopped()
+ SubscriptionState.Subscribed -> listener.onSubscribed()
+ }
+ }
+
+ if (newState.viewers != state.viewers){
+ listener.onViewerCount(newState.viewers);
+ }
+
+ state = newState;
+ }
+ }
+
+ fun activityChange() = scope.launch {
+ subscriber.activity.distinctUntilChanged().collect{
+ newActivity ->
+ when(newActivity){
+ is ActivityStream.Active -> newActivity.sourceId?.let { Optional.of(it) }?.let { listener.onActive(newActivity.streamId,newActivity.track, it) }
+ is ActivityStream.Inactive -> newActivity.sourceId?.let { Optional.of(it) }?.let { listener.onInactive(newActivity.streamId,Optional.of(newActivity.sourceId!!)) }
+ }
+ }
+ }
+
+
+ private fun onTrack() = scope.launch {
+ subscriber.track.collect() { holder ->
+ if(holder.track.kind.equals(TrackType.Audio))
+ listener.onTrack(holder.track as AudioTrack, Optional.of(holder.mid.orEmpty()));
+ if(holder.track.kind.equals(TrackType.Video))
+ listener.onTrack(holder.track as VideoTrack, Optional.of(holder.mid.orEmpty()));
+ }
+ }
+
+ private fun onLayers() = scope.launch {
+ subscriber.layers.distinctUntilChanged().collect{
+ layer ->
+ listener.onLayers(layer.mid,layer.activeLayers, layer.inactiveLayersEncodingIds)
+ }
+ }
+
+ fun onVad() = scope.launch {
+ subscriber.vad.distinctUntilChanged().collect{
+ vad ->
+ vad.sourceId?.let { Optional.of(it) }?.let { listener.onVad(vad.mid, it) }
+ }
+ }
+ fun select(layerData: LayerData) = promise { subscriber.select(layerData) }
+
+ fun project(sourceId: String, projectionData: ArrayList) =
+ promise {subscriber.project(sourceId,projectionData)}
+
+ fun addRemoteTrack(trackType: TrackType) = promise{ subscriber.addRemoteTrack(trackType) }
+
+ fun getMid(trackId: String) = promise { subscriber.getMid(trackId) }
+
+ fun getStats(interval: Boolean) = promise { subscriber.enableStats(interval) }
+
+ fun isConnected() = promise { subscriber.isConnected }
+
+ fun release() = subscriber.release();
+
+ fun subscribe() = promise {subscriber.subscribe(options.get())}
+
+ fun unsubscribe() = promise { subscriber.unsubscribe() }
+
+ fun isSubscribed() = subscriber.isSubscribed;
+}
+
+class CompatSubscriberOptions(){
+ var stereo : Boolean = true;
+ fun get() : Option {
+ return Option(
+ stereo = this.stereo
+ )
+ }
+}
+
+
+class CompatSubscriberCreds() {
+ var apiUrl = "";
+ var accountId = ""
+ var streamName = "";
+ var token = "";
+
+ fun get() = Credential(
+ streamName=this.streamName,
+ accountId = this.accountId,
+ token = if (this.token.equals("")) null else this.token,
+ apiUrl = this.apiUrl
+ );
+}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 7778248..a583d68 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,11 +1,16 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
+ ext {
+ kotlin_version = '2.0.0-Beta3'
+ }
repositories {
google()
jcenter()
+ mavenLocal()
}
dependencies {
- classpath 'com.android.tools.build:gradle:7.2.1'
+ classpath 'com.android.tools.build:gradle:8.2.2'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -16,6 +21,7 @@ allprojects {
repositories {
google()
jcenter()
+ mavenLocal()
}
}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 03e8d5f..967e667 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
diff --git a/settings.gradle b/settings.gradle
index da45cd8..b3e5407 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,4 +1,23 @@
+pluginManagement {
+ buildscript {
+ repositories {
+ mavenCentral()
+ maven {
+ // r8 maven
+ url = uri("https://storage.googleapis.com/r8-releases/raw")
+ }
+ }
+ dependencies {
+ // r8 version
+ classpath("com.android.tools:r8:8.2.16-dev")
+ }
+ }
+}
+
+
+
include ':MillicastSDK'
include ':AndroidSDK'
include ':app'
-rootProject.name = "android-app"
\ No newline at end of file
+rootProject.name = "android-app"
+