mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-28 00:33:06 +08:00
[feat] add mobile codec status and scrolling codec names
This commit is contained in:
@@ -195,6 +195,10 @@ final class NativeSession implements AutoCloseable {
|
||||
fail(status >= 0 && status < reasons.length ? reasons[status] : "连接已结束");
|
||||
}
|
||||
} else if (type == 4) {
|
||||
if(event.optInt("type",-1)==14){
|
||||
networkStatistics.receiveRemoteEncoder(event.getJSONObject("video_codec").getString("encoder"));
|
||||
return;
|
||||
}
|
||||
JSONObject info=event.optJSONObject("host_info");
|
||||
if(info!=null)supportsTextInput=info.optBoolean("supports_text_input",false);
|
||||
ui(() -> {
|
||||
|
||||
@@ -12,15 +12,18 @@ final class RemoteNetworkStatistics {
|
||||
}
|
||||
static final class Report {
|
||||
final Traffic[] traffic=new Traffic[4];final int mode;final boolean srtp;final double rtt;
|
||||
final String decoder;
|
||||
Report(JSONObject json){
|
||||
String[] keys={"video","audio","data","total"};
|
||||
for(int i=0;i<keys.length;i++){JSONObject item=json.optJSONObject(keys[i]);traffic[i]=item==null?null:new Traffic(item);}
|
||||
mode=json.optInt("mode",2);srtp=json.optBoolean("srtp");rtt=json.optDouble("rtt",Double.NaN);
|
||||
decoder=json.optString("decoder","");
|
||||
}
|
||||
}
|
||||
static final class Snapshot {
|
||||
final Report report;final int fps,width,height;final double latency,rtt;
|
||||
Snapshot(Report report,int fps,int width,int height,double latency,double rtt){this.report=report;this.fps=fps;this.width=width;this.height=height;this.latency=latency;this.rtt=rtt;}
|
||||
final String remoteEncoder,localDecoder;
|
||||
Snapshot(Report report,int fps,int width,int height,double latency,double rtt,String remoteEncoder,String localDecoder){this.report=report;this.fps=fps;this.width=width;this.height=height;this.latency=latency;this.rtt=rtt;this.remoteEncoder=remoteEncoder;this.localDecoder=localDecoder;}
|
||||
String mode(){return report==null?"—":report.mode==0?"P2P 直连":report.mode==1?"TURN 中继":"—";}
|
||||
String resolution(){return width>0&&height>0?width+" × "+height:"—";}
|
||||
String summary(){
|
||||
@@ -32,11 +35,13 @@ final class RemoteNetworkStatistics {
|
||||
final double time,latency;Frame(double time,double latency){this.time=time;this.latency=latency;}
|
||||
}
|
||||
private Report report;
|
||||
private String remoteEncoder="";
|
||||
private double reportTime,rttTime,averageRtt=Double.NaN;
|
||||
private long lastFrameId;
|
||||
private int width,height;
|
||||
private final ArrayDeque<Frame> frames=new ArrayDeque<>();
|
||||
static double now(){return System.nanoTime()/1_000_000_000.0;}
|
||||
synchronized void receiveRemoteEncoder(String encoder){remoteEncoder=encoder;}
|
||||
synchronized void receive(Report value,double now){
|
||||
report=value;reportTime=now;
|
||||
if(!Double.isFinite(value.rtt)||value.rtt<0||value.rtt>2000)return;
|
||||
@@ -53,7 +58,7 @@ final class RemoteNetworkStatistics {
|
||||
synchronized Snapshot snapshot(double now){
|
||||
prune(now);double total=0;int count=0;
|
||||
for(Frame frame:frames)if(Double.isFinite(frame.latency)){total+=frame.latency;count++;}
|
||||
return new Snapshot(now-reportTime<3?report:null,frames.size(),width,height,count==0?Double.NaN:total/count,now-rttTime<3?averageRtt:Double.NaN);
|
||||
return new Snapshot(now-reportTime<3?report:null,frames.size(),width,height,count==0?Double.NaN:total/count,now-rttTime<3?averageRtt:Double.NaN,remoteEncoder,report==null?"":report.decoder);
|
||||
}
|
||||
private void prune(double now){while(!frames.isEmpty()&&now-frames.peekFirst().time>=1)frames.removeFirst();}
|
||||
static String bitrate(long value){return value<0?"—":value>=1_000_000?String.format(Locale.ROOT,"%.1f Mbps",value/1_000_000.0):value>=1000?String.format(Locale.ROOT,"%.0f Kbps",value/1000.0):value+" bps";}
|
||||
|
||||
@@ -107,7 +107,7 @@ final class SessionControls extends FrameLayout {
|
||||
}
|
||||
void setMuted(boolean value){muted=value;if(menu!=null&&menuPage.equals("controls"))openMenu("controls");}
|
||||
void videoSize(int width,int height){waiting.setVisibility(GONE);positionMouse();}
|
||||
void resetVideoStatistics(){statistics(new RemoteNetworkStatistics.Snapshot(network.report,0,0,0,Double.NaN,network.rtt));}
|
||||
void resetVideoStatistics(){statistics(new RemoteNetworkStatistics.Snapshot(network.report,0,0,0,Double.NaN,network.rtt,network.remoteEncoder,network.localDecoder));}
|
||||
void statistics(RemoteNetworkStatistics.Snapshot value){network=value;renderNetwork();}
|
||||
void videoSettings(RemoteVideoSettings value){settings=value;renderVideoSettings();}
|
||||
void privacyScreen(RemotePrivacyScreen value){privacy=value;renderPrivacy();}
|
||||
@@ -200,10 +200,12 @@ final class SessionControls extends FrameLayout {
|
||||
for(int i=0;i<heading.getChildCount();i++)((TextView)heading.getChildAt(i)).setTextColor(SECONDARY);
|
||||
for(int i=0;i<4;i++)trafficRows[i]=trafficRow(body,names[i],trafficCells[i],i==3);
|
||||
ui.gap(body,10);ui.divider(body);ui.gap(body,10);
|
||||
String[] titles={"帧率","分辨率","画面延时","连接延时(RTT)","连接方式","媒体加密"};networkDetails=new TextView[titles.length];
|
||||
for(int i=0;i<titles.length;i++){LinearLayout row=ui.row();TextView title=ui.text(titles[i],12,false);title.setTextColor(SECONDARY);row.addView(title,new LinearLayout.LayoutParams(0,-2,1));
|
||||
String[] titles={"帧率","分辨率","画面延时","连接延时(RTT)","连接方式","媒体加密","被控端编码器","控制端解码器"};networkDetails=new TextView[titles.length];
|
||||
for(int i=0;i<titles.length;i++){LinearLayout row=ui.row();TextView title=ui.text(titles[i],12,false);title.setTextColor(SECONDARY);row.addView(title,i>=6?new LinearLayout.LayoutParams(-2,-2):new LinearLayout.LayoutParams(0,-2,1));
|
||||
if(i==5){encryptionLock=ui.icon("lock",GREEN);encryptionOpen=ui.icon("unlock",SECONDARY);for(View icon:new View[]{encryptionLock,encryptionOpen}){LinearLayout.LayoutParams p=ui.size(14,14);p.rightMargin=ui.dp(4);row.addView(icon,p);}}
|
||||
TextView value=ui.text("—",12,false);value.setFontFeatureSettings("tnum");row.addView(value);networkDetails[i]=value;body.addView(row);if(i<titles.length-1)ui.gap(body,7);}
|
||||
TextView value=ui.text("—",12,false);value.setFontFeatureSettings("tnum");
|
||||
if(i>=6){value.setGravity(Gravity.END);value.setSingleLine();value.setEllipsize(android.text.TextUtils.TruncateAt.MARQUEE);value.setMarqueeRepeatLimit(-1);value.setSelected(true);LinearLayout.LayoutParams p=new LinearLayout.LayoutParams(0,-2,1);p.leftMargin=ui.dp(8);row.addView(value,p);}else row.addView(value);
|
||||
networkDetails[i]=value;body.addView(row);if(i<titles.length-1)ui.gap(body,7);}
|
||||
renderNetwork();
|
||||
}
|
||||
private LinearLayout trafficRow(LinearLayout body,String title,TextView[] cells,boolean total){
|
||||
@@ -222,8 +224,9 @@ final class SessionControls extends FrameLayout {
|
||||
String in=RemoteNetworkStatistics.bitrate(traffic==null?-1:traffic.inbound),out=RemoteNetworkStatistics.bitrate(traffic==null?-1:traffic.outbound),loss=RemoteNetworkStatistics.loss(traffic==null?Double.NaN:traffic.loss);
|
||||
trafficCells[i][0].setText(in);trafficCells[i][1].setText(out);trafficCells[i][2].setText(loss);trafficRows[i].setContentDescription(names[i]+",接收 "+in+",发送 "+out+",丢包率 "+loss);
|
||||
}
|
||||
String[] values={network.fps+" FPS",network.resolution(),RemoteNetworkStatistics.latency(network.latency),RemoteNetworkStatistics.latency(network.rtt),network.mode(),network.report==null?"—":network.report.srtp?"SRTP 已启用":"未启用"};
|
||||
for(int i=0;i<values.length;i++)networkDetails[i].setText(values[i]);networkDetails[5].setTextColor(network.report!=null&&network.report.srtp?GREEN:SECONDARY);
|
||||
String[] values={network.fps+" FPS",network.resolution(),RemoteNetworkStatistics.latency(network.latency),RemoteNetworkStatistics.latency(network.rtt),network.mode(),network.report==null?"—":network.report.srtp?"SRTP 已启用":"未启用",network.remoteEncoder.isEmpty()?"—":network.remoteEncoder,network.localDecoder.isEmpty()?"—":network.localDecoder};
|
||||
// Reassigning identical text every statistics tick restarts the marquee.
|
||||
for(int i=0;i<values.length;i++)if(!android.text.TextUtils.equals(networkDetails[i].getText(),values[i]))networkDetails[i].setText(values[i]);networkDetails[5].setTextColor(network.report!=null&&network.report.srtp?GREEN:SECONDARY);
|
||||
encryptionLock.setVisibility(network.report!=null&&network.report.srtp?VISIBLE:GONE);encryptionOpen.setVisibility(network.report!=null&&!network.report.srtp?VISIBLE:GONE);
|
||||
}
|
||||
private int controlColumns(){return getMeasuredHeight()<ui.dp(400)&&getMeasuredWidth()>=ui.dp(400)?4:3;}
|
||||
|
||||
@@ -371,6 +371,7 @@ extern "C" JNIEXPORT jboolean JNICALL JNI_METHOD(nConnect)(
|
||||
extern "C" JNIEXPORT void JNICALL JNI_METHOD(nReady)(JNIEnv*, jclass, jlong handle) {
|
||||
auto* s = From(handle); if (!s || !s->controller) return;
|
||||
auto action = MakeHostInformation("CrossDesk Android", {}, true, "1.6.0", HostPlatform::Android);
|
||||
action.i.supports_video_codec_status = true;
|
||||
s->Send(kControlStream, action.to_json()); FreeRemoteAction(action);
|
||||
}
|
||||
extern "C" JNIEXPORT void JNICALL JNI_METHOD(nPointer)(
|
||||
|
||||
Reference in New Issue
Block a user