[*] 提高稳定性

1. 解决web终端踢人黑屏问题
2. 处理安卓终端会议房间资源释放问题
3. 优化安卓远程终端进入逻辑
This commit is contained in:
acgist
2023-04-29 14:42:24 +08:00
parent f5f19bc4c2
commit 59df04faaf
12 changed files with 188 additions and 52 deletions

View File

@@ -78,6 +78,7 @@ public class MediaService extends Service {
super.onCreate();
this.mkdir(R.string.storagePathImage);
this.mkdir(R.string.storagePathVideo);
this.buildNotificationChannel();
}
@Override
@@ -89,6 +90,7 @@ public class MediaService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(MediaService.class.getSimpleName(), "onStartCommand" + intent.getAction());
this.cleanAllNotification();
if (Action.LAUNCH.name().equals(intent.getAction())) {
this.launch(intent);
this.openConnect(intent);
@@ -114,18 +116,13 @@ public class MediaService extends Service {
private void launch(Intent intent) {
final Intent notificationIntent = new Intent(this, MediaService.class);
final PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
final NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, "NOTIFICATION_CHANNEL_ID")
final NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, "TAOYAO")
.setSmallIcon(R.mipmap.ic_launcher_foreground)
.setLargeIcon(BitmapFactory.decodeResource(this.getResources(), R.mipmap.ic_launcher_foreground))
.setTicker("NOTIFICATION_TICKER")
.setContentTitle("屏幕录制")
.setContentText("屏幕录制共享")
.setContentTitle("桃夭后台")
.setContentText("桃夭正在后台运行")
.setContentIntent(pendingIntent);
final Notification notification = notificationBuilder.build();
final NotificationChannel channel = new NotificationChannel("NOTIFICATION_CHANNEL_ID", "NOTIFICATION_CHANNEL_NAME", NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription("NOTIFICATION_CHANNEL_DESC");
final NotificationManager notificationManager = this.getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
this.startForeground((int) System.currentTimeMillis(), notification);
}
@@ -182,18 +179,15 @@ public class MediaService extends Service {
public void screenRecord(Intent intent) {
final Intent notificationIntent = new Intent(this, MediaService.class);
final PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
final NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, "NOTIFICATION_CHANNEL_ID")
final NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, "TAOYAO")
.setSmallIcon(R.mipmap.ic_launcher_foreground)
.setLargeIcon(BitmapFactory.decodeResource(this.getResources(), R.mipmap.ic_launcher_foreground))
.setTicker("NOTIFICATION_TICKER")
.setContentTitle("屏幕录制")
.setContentText("屏幕录制共享")
// 自动清除
// .setAutoCancel(true)
.setContentTitle("录制屏幕")
.setContentText("桃夭正在录制屏幕")
.setContentIntent(pendingIntent);
final Notification notification = notificationBuilder.build();
final NotificationChannel channel = new NotificationChannel("NOTIFICATION_CHANNEL_ID", "NOTIFICATION_CHANNEL_NAME", NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription("NOTIFICATION_CHANNEL_DESC");
final NotificationManager notificationManager = this.getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
this.startForeground((int) System.currentTimeMillis(), notification);
MediaManager.getInstance().initScreen(intent.getParcelableExtra("data"));
}
@@ -209,4 +203,17 @@ public class MediaService extends Service {
}
}
private void buildNotificationChannel() {
final NotificationChannel channel = new NotificationChannel("TAOYAO", "桃夭通知", NotificationManager.IMPORTANCE_DEFAULT);
channel.setShowBadge(false);
channel.setDescription("桃夭系统通知");
final NotificationManager notificationManager = this.getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
private void cleanAllNotification() {
final NotificationManager notificationManager = this.getSystemService(NotificationManager.class);
notificationManager.cancelAll();
}
}

View File

@@ -13,7 +13,6 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.os.PowerManager;
import android.os.Process;
import android.se.omapi.Session;
import android.util.Log;
import com.acgist.taoyao.boot.model.Header;
@@ -338,7 +337,6 @@ public final class Taoyao implements ITaoyao {
buffer.compact();
final String content = new String(this.decrypt.doFinal(message));
try {
Log.d(Taoyao.class.getSimpleName(), "处理信令:" + content);
Taoyao.this.on(content);
} catch (Exception e) {
Log.e(Taoyao.class.getSimpleName(), "处理信令异常:" + content, e);
@@ -513,6 +511,7 @@ public final class Taoyao implements ITaoyao {
final Long id = header.getId();
final Message request = this.requestMessage.get(id);
if (request != null) {
Log.d(Taoyao.class.getSimpleName(), "处理信令响应:" + content);
// 同步处理:重新设置响应消息
this.requestMessage.put(id, message);
// 唤醒等待线程
@@ -520,6 +519,7 @@ public final class Taoyao implements ITaoyao {
request.notifyAll();
}
} else {
Log.d(Taoyao.class.getSimpleName(), "处理信令异步:" + content);
this.executeHandler.post(() -> {
try {
this.dispatch(content, header, message);
@@ -542,17 +542,18 @@ public final class Taoyao implements ITaoyao {
case "client::reboot" -> this.clientReboot(message, message.body());
case "client::shutdown" -> this.clientShutdown(message, message.body());
case "media::consume" -> this.mediaConsume(message, message.body());
// case "media::audio::volume" -> this.mediaAudioVolume(message, message.body());
case "media::audio::volume" -> this.mediaAudioVolume(message, message.body());
case "media::consumer::close" -> this.mediaConsumerClose(message, message.body());
case "media::consumer::pause" -> this.mediaConsumerPause(message, message.body());
// case "media::consumer::request::key::frame" -> this.mediaConsumerRequestKeyFrame(message, message.body());
case "media::consumer::request::key::frame" -> this.mediaConsumerRequestKeyFrame(message, message.body());
case "media::consumer::resume" -> this.mediaConsumerResume(message, message.body());
// case "media::consumer::set::preferred::layers" -> this.mediaConsumerSetPreferredLayers(message, message.body());
// case "media::consumer::status" -> this.mediaConsumerStatus(message, message.body());
case "media::consumer::set::preferred::layers" -> this.mediaConsumerSetPreferredLayers(message, message.body());
case "media::consumer::status" -> this.mediaConsumerStatus(message, message.body());
case "media::producer::close" -> this.mediaProducerClose(message, message.body());
case "media::producer::pause" -> this.mediaProducerPause(message, message.body());
case "media::producer::resume" -> this.mediaProducerResume(message, message.body());
// case "media::producer::video::orientation:change" -> this.mediaVideoOrientationChange(message, message.body());
case "media::producer::video::orientation:change" -> this.mediaVideoOrientationChange(message, message.body());
case "room::client::list" -> this.roomClientList(message, message.body());
case "room::close" -> this.roomClose(message, message.body());
case "room::enter" -> this.roomEnter(message, message.body());
case "room::expel" -> this.roomExpel(message, message.body());
@@ -645,6 +646,10 @@ public final class Taoyao implements ITaoyao {
room.mediaConsume(message, body);
}
private void mediaAudioVolume(Message message, Map<String, Object> body) {
}
private void mediaConsumerClose(Message message, Map<String, Object> body) {
final String roomId = MapUtils.get(body, "roomId");
final Room room = this.rooms.get(roomId);
@@ -663,6 +668,10 @@ public final class Taoyao implements ITaoyao {
room.mediaConsumerPause(body);
}
private void mediaConsumerRequestKeyFrame(Message message, Map<String, Object> body) {
}
private void mediaConsumerResume(Message message, Map<String, Object> body) {
final String roomId = MapUtils.get(body, "roomId");
final Room room = this.rooms.get(roomId);
@@ -672,6 +681,14 @@ public final class Taoyao implements ITaoyao {
room.mediaConsumerResume(body);
}
private void mediaConsumerSetPreferredLayers(Message message, Map<String, Object> body) {
}
private void mediaConsumerStatus(Message message, Map<String, Object> body) {
}
private void mediaProducerClose(Message message, Map<String, Object> body) {
final String roomId = MapUtils.get(body, "roomId");
final Room room = this.rooms.get(roomId);
@@ -690,6 +707,10 @@ public final class Taoyao implements ITaoyao {
room.mediaProducerPause(body);
}
private void mediaVideoOrientationChange(Message message, Map<String, Object> body) {
}
private void mediaProducerResume(Message message, Map<String, Object> body) {
final String roomId = MapUtils.get(body, "roomId");
final Room room = this.rooms.get(roomId);
@@ -699,6 +720,15 @@ public final class Taoyao implements ITaoyao {
room.mediaProducerResume(body);
}
private void roomClientList(Message message, Map<String, Object> body) {
final String roomId = MapUtils.get(body, "roomId");
final Room room = this.rooms.get(roomId);
if(room == null) {
return;
}
room.newRemoteClientFromRoomClientList(body);
}
private void roomClose(Message message, Map<String, Object> body) {
final String roomId = MapUtils.get(body, "roomId");
final Room room = this.rooms.remove(roomId);
@@ -714,7 +744,7 @@ public final class Taoyao implements ITaoyao {
if(room == null) {
return;
}
room.newRemoteClient(body);
room.newRemoteClientFromRoomEnter(body);
}
public Room roomEnter(String roomId, String password) {

View File

@@ -318,11 +318,12 @@ namespace acgist {
void Room::mediaProducerClose(JNIEnv* env, const std::string& producerId) {
if(this->audioProducer->GetId() == producerId) {
this->audioProducer->Close();
this->producerCloseCallback(env, producerId);
} else if(this->videoProducer->GetId() == producerId) {
this->videoProducer->Close();
this->producerCloseCallback(env, producerId);
} else {
}
this->producerCloseCallback(env, producerId);
}
void Room::mediaConsumerPause(JNIEnv* env, const std::string& consumerId) {
@@ -349,6 +350,7 @@ namespace acgist {
return;
}
consumer->Close();
this->consumers.erase(consumerId);
this->consumerCloseCallback(env, consumerId);
}

View File

@@ -1,12 +1,14 @@
package com.acgist.taoyao.media.client;
import android.os.Handler;
import android.util.Log;
import com.acgist.taoyao.boot.utils.ListUtils;
import com.acgist.taoyao.media.config.Config;
import com.acgist.taoyao.media.signal.ITaoyao;
import org.webrtc.MediaStream;
import org.webrtc.MediaStreamTrack;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -72,8 +74,37 @@ public class LocalClient extends RoomClient {
@Override
public void close() {
Log.i(RemoteClient.class.getSimpleName(), "关闭本地终端:" + this.clientId);
super.close();
this.tracks.clear();
if(this.mediaStream == null) {
return;
}
synchronized (this.mediaStream) {
this.mediaStream.dispose();
}
}
/**
* 关闭生产者
*
* @param producerId 生产者ID
*/
public void close(String producerId) {
Log.i(RemoteClient.class.getSimpleName(), "关闭本地终端生产者者:" + this.clientId + " - " + producerId);
final Long pointer = this.tracks.get(producerId);
if(pointer == null || this.mediaStream == null) {
return;
}
synchronized (this.mediaStream) {
if(pointer.equals(this.audioProducerPointer)) {
this.mediaStream.audioTracks.forEach(MediaStreamTrack::dispose);
this.mediaStream.audioTracks.clear();
} else if(pointer.equals(this.videoProducerPointer)) {
this.mediaStream.videoTracks.forEach(MediaStreamTrack::dispose);
this.mediaStream.videoTracks.clear();
} else {
}
}
}
}

View File

@@ -1,6 +1,7 @@
package com.acgist.taoyao.media.client;
import android.os.Handler;
import android.util.Log;
import com.acgist.taoyao.boot.utils.ListUtils;
import com.acgist.taoyao.media.config.Config;
@@ -23,6 +24,7 @@ public class RemoteClient extends RoomClient {
/**
* 媒体流Track
* 消费者ID = 媒体流Track
* 注意track由mediasoup的consumer释放
*/
protected final Map<String, MediaStreamTrack> tracks;
protected long audioConsumerPointer;
@@ -65,9 +67,25 @@ public class RemoteClient extends RoomClient {
@Override
public void close() {
Log.i(RemoteClient.class.getSimpleName(), "关闭远程终端:" + this.clientId);
super.close();
this.tracks.values().forEach(MediaStreamTrack::dispose);
this.tracks.clear();
synchronized (this.tracks) {
// 注意使用nativeMediaConsumerClose释放
this.tracks.clear();
}
}
/**
* 关闭消费者
*
* @param consumerId 消费者ID
*/
public void close(String consumerId) {
Log.i(RemoteClient.class.getSimpleName(), "关闭远程终端消费者:" + this.clientId + " - " + consumerId);
synchronized (this.tracks) {
// 注意使用nativeMediaConsumerClose释放
this.tracks.remove(consumerId);
}
}
}

View File

@@ -13,6 +13,7 @@ import com.acgist.taoyao.media.config.MediaProperties;
import com.acgist.taoyao.media.config.WebrtcProperties;
import com.acgist.taoyao.media.signal.ITaoyao;
import org.apache.commons.collections4.CollectionUtils;
import org.webrtc.AudioTrack;
import org.webrtc.MediaStream;
import org.webrtc.MediaStreamTrack;
@@ -112,6 +113,9 @@ public class Room extends CloseableClient implements RouterCallback {
this.peerConnectionFactory = this.mediaManager.newClient();
this.localClient = new LocalClient(this.name, this.clientId, this.taoyao, this.mainHandler);
this.localClient.setMediaStream(this.mediaManager.buildLocalMediaStream(this.audioProduce, this.videoProduce));
if(this.preview) {
this.localClient.playVideo();
}
// STUN | TURN
final List<PeerConnection.IceServer> iceServers = new ArrayList<>();
// 不用配置
@@ -200,28 +204,50 @@ public class Room extends CloseableClient implements RouterCallback {
*
* @param body 消息主体
*/
public void newRemoteClient(Map<String, Object> body) {
synchronized (this.remoteClients) {
final String clientId = MapUtils.get(body, "clientId");
final Map<String, Object> status = MapUtils.get(body, "status");
final String name = MapUtils.get(status, "name");
public void newRemoteClientFromRoomEnter(Map<String, Object> body) {
final String clientId = MapUtils.get(body, "clientId");
if(this.clientId.equals(clientId)) {
return;
}
final Map<String, Object> status = MapUtils.get(body, "status");
final String name = MapUtils.get(status, "name");
final RemoteClient remoteClient = new RemoteClient(name, clientId, this.taoyao, this.mainHandler);
final RemoteClient old = this.remoteClients.put(clientId, remoteClient);
if(old != null) {
// 关闭旧的资源
old.close();
}
}
public void newRemoteClientFromRoomClientList(Map<String, Object> body) {
final List<Map<String, Object>> clients = MapUtils.get(body, "clients");
if(CollectionUtils.isEmpty(clients)) {
return;
}
clients.forEach(map -> {
final String name = MapUtils.get(map, "name");
final String clientId = MapUtils.get(map, "clientId");
if(this.clientId.equals(clientId)) {
return;
}
final RemoteClient remoteClient = new RemoteClient(name, clientId, this.taoyao, this.mainHandler);
final RemoteClient old = this.remoteClients.put(clientId, remoteClient);
if(old != null) {
// 关闭旧的资源
old.close();
}
}
});
}
public void closeRemoteClient(String clientId) {
synchronized (this.remoteClients) {
final RemoteClient remoteClient = this.remoteClients.get(clientId);
if(remoteClient == null) {
return;
}
remoteClient.close();
final RemoteClient remoteClient = this.remoteClients.remove(clientId);
if(remoteClient == null) {
return;
}
remoteClient.tracks.keySet().forEach(consumerId -> {
this.nativeMediaConsumerClose(this.nativeRoomPointer, consumerId);
});
remoteClient.close();
}
@Override
@@ -233,8 +259,10 @@ public class Room extends CloseableClient implements RouterCallback {
Log.i(Room.class.getSimpleName(), "关闭房间:" + this.roomId);
super.close();
this.nativeCloseRoom(this.nativeRoomPointer);
// 关闭远程媒体
this.remoteClients.values().forEach(v -> this.closeRemoteClient(v.clientId));
this.remoteClients.clear();
// 关闭本地媒体
this.localClient.close();
this.mediaManager.closeClient();
}
@@ -249,7 +277,9 @@ public class Room extends CloseableClient implements RouterCallback {
}
public void mediaConsumerClose(Map<String, Object> body) {
this.nativeMediaConsumerClose(this.nativeRoomPointer, MapUtils.get(body, "consumerId"));
final String consumerId = MapUtils.get(body, "consumerId");
this.nativeMediaConsumerClose(this.nativeRoomPointer, consumerId);
this.remoteClients.values().forEach(v -> v.close(consumerId));
}
public void mediaConsumerPause(String consumerId) {
@@ -285,7 +315,9 @@ public class Room extends CloseableClient implements RouterCallback {
}
public void mediaProducerClose(Map<String, Object> body) {
this.nativeMediaProducerClose(this.nativeRoomPointer, MapUtils.get(body, "producerId"));
final String producerId = MapUtils.get(body, "producerId");
this.nativeMediaProducerClose(this.nativeRoomPointer, producerId);
this.localClient.close(producerId);
}
public void mediaProducerPause(String producerId) {
@@ -316,7 +348,7 @@ public class Room extends CloseableClient implements RouterCallback {
public void enterRoomCallback(String rtpCapabilities, String sctpCapabilities) {
this.rtpCapabilities = JSONUtils.toJava(rtpCapabilities);
this.sctpCapabilities = JSONUtils.toJava(sctpCapabilities);
this.taoyao.push(this.taoyao.buildMessage(
this.taoyao.request(this.taoyao.buildMessage(
"room::enter",
"roomId", this.roomId,
"password", this.password,
@@ -382,9 +414,9 @@ public class Room extends CloseableClient implements RouterCallback {
final Message response = JSONUtils.toJava(message, Message.class);
final Map<String, Object> body = response.body();
final String kind = MapUtils.get(body, "kind");
final String clientId = MapUtils.get(body, "clientId");
final String sourceId = MapUtils.get(body, "sourceId");
final String consumerId = MapUtils.get(body, "consumerId");
final RemoteClient remoteClient = this.remoteClients.get(clientId);
final RemoteClient remoteClient = this.remoteClients.get(sourceId);
if(remoteClient == null) {
// TODO资源释放
return;

View File

@@ -133,6 +133,9 @@ public class SessionClient extends Client {
this.mediaStream = this.mediaManager.buildLocalMediaStream(this.audioProduce, this.videoProduce);
this.peerConnection = this.peerConnectionFactory.createPeerConnection(configuration, this.observer);
this.peerConnection.addStream(this.mediaStream);
if(this.preview) {
// 实现预览
}
// 设置streamId同步
// final List<String> streamIds = new ArrayList<>();
// ListUtils.getOnlyOne(this.mediaStream.audioTracks, audioTrack -> {
@@ -304,7 +307,8 @@ public class SessionClient extends Client {
}
super.close();
try {
// if(this.mediaStream != null) {
// PeerConnection自动释放
// if(this.mediaStream != null) {
// this.mediaStream.dispose();
// }
// if(this.remoteMediaStream != null) {