Skip to content

Instantly share code, notes, and snippets.

@jumbo-in-Jap
Last active February 18, 2021 12:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jumbo-in-Jap/7ceb26ed131ff9783517233ba97047e0 to your computer and use it in GitHub Desktop.
Save jumbo-in-Jap/7ceb26ed131ff9783517233ba97047e0 to your computer and use it in GitHub Desktop.
SkyWayStatsManager
package com.ntt.ecl.webrtc.sample_p2p_videochat;
import android.system.ErrnoException;
import android.util.Log;
import org.json.JSONArray;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
class SkyWayStatsConfig{
// min: 5000
int interval = 5000;
double fractionLostUnStableThresholdsRate = 0.8;
double fractionLostCriticalThresholdsRate = 1.0;
double jitterUnStableThresholdsMs = 50;
double jitterCriticalThresholdsMs = 100;
double rttUnStableThresholdsMs = 400;
double rttCriticalThresholdsMs = 800;
}
public class SkyWayStats {
public CodecInfo codecInfo = new CodecInfo();
public List<StatsInfo> audioStatsInfoArray = new ArrayList<>();
public SkyWayStats(){
}
public StatsInfo addStats(SkyWayStatsConfig config,
int bytesReceived,
int bytesSent,
double packetsLost,
double packetsReceived,
double remoteInboundPacketsLost,
double remoteInboundJitter,
double remoteInboundRoundTripTime) {
StatsInfo info = new StatsInfo();
info.bytesReceived = bytesReceived;
info.bytesSent = bytesSent;
info.packetsReceived = packetsReceived;
info.remoteInboundPacketsLost = remoteInboundPacketsLost;
info.remoteInboundJitter = remoteInboundJitter;
info.roundTripTime = remoteInboundRoundTripTime;
info.packetsLost = packetsLost;
if (audioStatsInfoArray.size() > 0){
int lastIndex = this.audioStatsInfoArray.size() - 1;
double bytesReceivedDelta = bytesReceived - this.audioStatsInfoArray.get(lastIndex).bytesReceived;
long timeDelta = info.timestamp.getTime() - this.audioStatsInfoArray.get(lastIndex).timestamp.getTime();
double bitrate = bytesReceivedDelta / timeDelta * 9 * 1000;
info.bitrate = bitrate;
double packetLostDelta = packetsLost - this.audioStatsInfoArray.get(lastIndex).packetsLost;
double packetReceivedDelta = packetsReceived - this.audioStatsInfoArray.get(lastIndex).packetsReceived;
info.fractionLost = packetLostDelta / packetReceivedDelta;
}
// check condition
if (info.fractionLost >= config.fractionLostCriticalThresholdsRate) {
info.condition = Condition.Critical;
info.msg = "bad network condition";
}else if (info.fractionLost >= config.fractionLostUnStableThresholdsRate){
info.condition = Condition.Stable;
info.msg = "bad network condition";
}
log("bytesReceived: " + bytesReceived
+ " bytesSent: " + bytesSent
+ " packetsLost: " + packetsLost
+ " packetsReceived: " + packetsReceived
+ " remoteInboundPacketLoss: " + remoteInboundPacketsLost
+ " remoteInboundJitter: "+ remoteInboundJitter
+ " fractionLost: "+ info.fractionLost
+ " bitrate: " + info.bitrate
+ " remoteInboundRoundTripTime: " + remoteInboundRoundTripTime
);
this.audioStatsInfoArray.add(info);
return info;
}
private void log(String msg){
Log.d("skyway-stats-manager", msg);
}
// internal classes
class CodecInfo{
public String inboundAudioCodec;
public String inboundVideoCodec;
public String outboundAudioCodec;
public String outboundVideoCodec;
}
class StatsInfo {
public Date timestamp = new Date();
public int bytesReceived = 0;
public int bytesSent = 0;
public double packetsLost = 0;
public double packetsReceived = 0;
public double remoteInboundPacketsLost = 0;
public double remoteInboundJitter = 0;
public double fractionLost = 0;
public double bitrate = 0;
public double roundTripTime = 0;
public Condition condition = Condition.Stable;
public String msg;
}
enum Condition {
Stable,
UnStable,
Critical
};
}
package com.ntt.ecl.webrtc.sample_p2p_videochat;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONObject;
import java.util.TimerTask;
import java.util.Timer;
import io.skyway.Peer.Browser.MediaStream;
import io.skyway.Peer.MediaConnection;
import io.skyway.Peer.StatsCollectorCallback;
interface SkyWayStatsCallback {
void on(SkyWayStats.StatsInfo info);
}
public class SkyWayStatsManager {
private MediaConnection _mediaConnection;
public SkyWayStatsConfig _config;
public SkyWayStats skyWayStats;
private SkyWayStatsCallback callback;
public SkyWayStatsManager(MediaConnection mediaConnection, SkyWayStatsConfig config){
this._mediaConnection = mediaConnection;
this._config = config;
this.skyWayStats = new SkyWayStats();
Timer timer = new Timer(false);
TimerTask task = new TimerTask() {
@Override
public void run() {
if (mediaConnection.isOpen()){
getStats();
}
}
};
timer.schedule(task, config.interval, config.interval);
}
private void getStats() {
// log("run stats");
this._mediaConnection.getStats(new StatsCollectorCallback() {
@Override
public void onStatsDelivered(JSONArray jsonArray) {
try{
JSONObject inboundAudioStream = getStatTable(jsonArray, "RTCInboundRTPAudioStream");
JSONObject outboundAudioStream = getStatTable(jsonArray, "RTCOutboundRTPAudioStream");
JSONObject remoteInboundAudioStream = getStatTable(jsonArray, "RTCRemoteInboundRtpAudioStream");
// setCodec
// if (skyWayStats.codecInfo.inboundAudioCodec == null) {
// String inboundAudioCodec = getStatTable(jsonArray, inboundAudioStream.getString("codecId")).getString("mimeType");
// String outboundAudioCodec = getStatTable(jsonArray, outboundAudioStream.getString("codecId")).getString("mimeType");
// skyWayStats.codecInfo.inboundAudioCodec = inboundAudioCodec;
// skyWayStats.codecInfo.outboundAudioCodec = outboundAudioCodec;
// log("inbound codec: " + inboundAudioCodec + " outbound codec: " + outboundAudioCodec);
// }
// addStats
int bytesReceived = inboundAudioStream.getInt("bytesReceived");
int bytesSent = outboundAudioStream.getInt("bytesSent");
double packetsLost = inboundAudioStream.getDouble("packetsLost");
double packetsReceived = inboundAudioStream.getDouble("packetsReceived");
double remoteInboundJitter = remoteInboundAudioStream.getDouble("jitter");
double remoteInboundPacketsLost = remoteInboundAudioStream.getDouble("packetsLost");
double remoteInboundRoundTripTime = remoteInboundAudioStream.getDouble("roundTripTime");
SkyWayStats.StatsInfo statsInfo = skyWayStats.addStats(
_config,
bytesReceived,
bytesSent,
packetsLost,
packetsReceived,
remoteInboundPacketsLost,
remoteInboundJitter,
remoteInboundRoundTripTime
);
callback.on(statsInfo);
}catch(Exception e){
log("failed to get stats");
}
}
});
}
private JSONObject getStatTable(JSONArray jsonArray, String idPrefix) {
try{
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject table = jsonArray.getJSONObject(i);
if (table.getString("id").contains(idPrefix)){
return table;
}
}
}catch(Exception e){
return null;
}
return null;
}
public void setBadConditionListener(SkyWayStatsCallback l) {
this.callback = l;
}
private void log(String msg){
Log.d("skyway-stats-manager", msg);
}
}
SkyWayStatsConfig config = new SkyWayStatsConfig();
config.interval = 5000;
SkyWayStatsManager manager = new SkyWayStatsManager(_mediaConnection, config);
manager.setBadConditionListener(new SkyWayStatsCallback() {
@Override
public void on(SkyWayStats.StatsInfo info) {
handler.post(new Runnable() {
@Override
public void run() {
Button button = (Button)findViewById(R.id.switchCameraAction);
if (info.condition != SkyWayStats.Condition.Stable){
Log.d("skyway-stats-manager", "bad condition: " + info.condition + " / " + info.msg);
button.setText("bad condition");
}else{
button.setText("good condition");
}
}
});
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment