[*] 日常优化

This commit is contained in:
acgist
2023-06-15 08:24:44 +08:00
parent 021b275717
commit 86f4a5fea9
6 changed files with 291 additions and 130 deletions

View File

@@ -563,6 +563,8 @@ sudo ufw allow 8888/tcp
sudo ufw allow 9999/tcp sudo ufw allow 9999/tcp
# 媒体服务 # 媒体服务
sudo ufw allow 40000:49999/udp sudo ufw allow 40000:49999/udp
# 允许网段
#sudo ufw allow from 192.168.1.0/24 to any
# 删除端口 # 删除端口
#sudo ufw delete allow 443/tcp #sudo ufw delete allow 443/tcp

90
docs/NetworkTopology.md Normal file
View File

@@ -0,0 +1,90 @@
# 拓扑网络
多子网环境下的部署配置
## 两个子网
```
# 配置`TURN`服务
`coturn`
#配置地址重写
`ip-rewrite`
```
## 三个及其以上子网
### 配置端口转发
```
# 配置转发:两个配置一个即可
vim /etc/default/ufw
---
DEFAULT_FORWARD_POLICY="ACCEPT"
---
vim /etc/sysctl.conf | vim /etc/ufw/sysctl.conf
---
net.ipv4.ip_forward=1 | net/ipv4/ip_forward=1
---
sysctl -p
# *filter之前添加
vim /etc/ufw/before.rules
---
*nat
:PREROUTING ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
-A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
-A POSTROUTING -j MASQUERADE
COMMIT
---
ufw reload
# 配置`TURN`服务
`coturn`
#配置地址重写
`ip-rewrite`
```
## 端口转发
* DNAT 目标IP转换
* SNAT 源IP转换
* REDIRECT 端口重定向
* MASQUERADE源地址动态伪装
```
-A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
-A PREROUTING -d 192.168.1.100 -p tcp --dport 80 -j REDIRECT --to-port 8080
-A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 192.168.2.100:8080
-A PREROUTING -d 192.168.1.100 -p tcp --dport 80 -j DNAT --to-destination 192.168.2.100:8080
-A POSTROUTING -p tcp --dport 80 -j SNAT --to-source 192.168.2.100
-A POSTROUTING -s 192.168.1.0/24 -j SNAT --to-source 192.168.2.100
-A POSTROUTING -j MASQUERADE
-A POSTROUTING -s 192.168.1.0/24 -j MASQUERADE
```
## iptables
```
# 查看nat
iptables -L -t nat
# 清理nat
iptables -F -t nat
```
### 四张表
* nat NAT功能端口映射、地址映射等等
* raw 优先级最高
* filter过滤功能
* mangle修改特定数据包
### 五条链
* INPUT 通过路由表后目的地为本机
* OUTPUT 由本机产生向外转发
* FORWARDING 通过路由表后目的地不为本机
* PREROUTING 数据包进入路由表之前
* POSTROUTING发送到网卡接口之前

View File

@@ -155,11 +155,11 @@ public class MediaService extends Service {
resources.getInteger(R.integer.imageQuantity), resources.getInteger(R.integer.imageQuantity),
resources.getString(R.string.audioQuantity), resources.getString(R.string.audioQuantity),
resources.getString(R.string.videoQuantity), resources.getString(R.string.videoQuantity),
resources.getBoolean(R.bool.broadcaster),
resources.getInteger(R.integer.channelCount), resources.getInteger(R.integer.channelCount),
resources.getInteger(R.integer.iFrameInterval), resources.getInteger(R.integer.iFrameInterval),
resources.getString(R.string.imagePath), resources.getString(R.string.imagePath),
resources.getString(R.string.videoPath), resources.getString(R.string.videoPath),
resources.getString(R.string.videoFile),
resources.getString(R.string.watermark), resources.getString(R.string.watermark),
VideoSourceType.valueOf(resources.getString(R.string.videoSourceType)) VideoSourceType.valueOf(resources.getString(R.string.videoSourceType))
); );
@@ -237,7 +237,7 @@ public class MediaService extends Service {
.setContentIntent(pendingIntent); .setContentIntent(pendingIntent);
final Notification notification = notificationBuilder.build(); final Notification notification = notificationBuilder.build();
this.startForeground((int) System.currentTimeMillis(), notification); this.startForeground((int) System.currentTimeMillis(), notification);
MediaManager.getInstance().initScreen(intent.getParcelableExtra("data")); MediaManager.getInstance().initScreenCapturer(intent.getParcelableExtra("data"));
} }
/** /**

View File

@@ -34,8 +34,6 @@
<string name="audioQuantity">fd-audio</string> <string name="audioQuantity">fd-audio</string>
<!-- 视频质量 --> <!-- 视频质量 -->
<string name="videoQuantity">fd-video</string> <string name="videoQuantity">fd-video</string>
<!-- 语音播报 -->
<bool name="broadcaster">false</bool>
<!-- 音频通道数量 --> <!-- 音频通道数量 -->
<integer name="channelCount">1</integer> <integer name="channelCount">1</integer>
<!-- 视频关键帧频率 --> <!-- 视频关键帧频率 -->
@@ -46,6 +44,8 @@
<string name="imagePath">/taoyao</string> <string name="imagePath">/taoyao</string>
<!-- 视频存储目录 --> <!-- 视频存储目录 -->
<string name="videoPath">/taoyao</string> <string name="videoPath">/taoyao</string>
<!-- 分享视频文件路径 -->
<string name="videoFile"></string>
<!-- 水印 --> <!-- 水印 -->
<string name="watermark">"'TAOYAO' yyyy-MM-dd HH:mm:ss"</string> <string name="watermark">"'TAOYAO' yyyy-MM-dd HH:mm:ss"</string>
<!-- 视频来源FILE|BACK|FRONT|SCREEN --> <!-- 视频来源FILE|BACK|FRONT|SCREEN -->

View File

@@ -6,7 +6,6 @@ import android.media.MediaCodecInfo;
import android.media.MediaCodecList; import android.media.MediaCodecList;
import android.media.projection.MediaProjection; import android.media.projection.MediaProjection;
import android.os.Handler; import android.os.Handler;
import android.speech.tts.TextToSpeech;
import android.util.Log; import android.util.Log;
import com.acgist.taoyao.media.client.PhotographClient; import com.acgist.taoyao.media.client.PhotographClient;
@@ -30,6 +29,7 @@ import org.webrtc.CapturerObserver;
import org.webrtc.DefaultVideoDecoderFactory; import org.webrtc.DefaultVideoDecoderFactory;
import org.webrtc.DefaultVideoEncoderFactory; import org.webrtc.DefaultVideoEncoderFactory;
import org.webrtc.EglBase; import org.webrtc.EglBase;
import org.webrtc.FileVideoCapturer;
import org.webrtc.MediaConstraints; import org.webrtc.MediaConstraints;
import org.webrtc.MediaStream; import org.webrtc.MediaStream;
import org.webrtc.PeerConnectionFactory; import org.webrtc.PeerConnectionFactory;
@@ -44,9 +44,8 @@ import org.webrtc.VideoSource;
import org.webrtc.VideoTrack; import org.webrtc.VideoTrack;
import org.webrtc.audio.JavaAudioDeviceModule; import org.webrtc.audio.JavaAudioDeviceModule;
import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Locale;
import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.IntStream; import java.util.stream.IntStream;
@@ -57,24 +56,22 @@ import java.util.stream.IntStream;
*/ */
public final class MediaManager { public final class MediaManager {
private static final MediaManager INSTANCE = new MediaManager();
public static final MediaManager getInstance() {
return INSTANCE;
}
/** /**
* 当前终端数量 * 当前终端数量
*/ */
private volatile int clientCount; private volatile int clientCount;
/** /**
* 视频路径 * 图片路径
*/ */
private String imagePath; private String imagePath;
/** /**
* 图片路径 * 视频路径
*/ */
private String videoPath; private String videoPath;
/**
* 分享视频文件路径
*/
private String videoFile;
/** /**
* 图片质量 * 图片质量
*/ */
@@ -87,10 +84,6 @@ public final class MediaManager {
* 视频质量 * 视频质量
*/ */
private String videoQuantity; private String videoQuantity;
/**
* 语音播报
*/
private boolean broadcaster;
/** /**
* 通道数量 * 通道数量
*/ */
@@ -103,16 +96,12 @@ public final class MediaManager {
* 水印 * 水印
*/ */
private String watermark; private String watermark;
/**
* 视频来源类型
*/
private VideoSourceType videoSourceType;
/** /**
* 信令 * 信令
*/ */
private ITaoyao taoyao; private ITaoyao taoyao;
/** /**
* Handler * MainHandler
*/ */
private Handler mainHandler; private Handler mainHandler;
/** /**
@@ -139,10 +128,6 @@ public final class MediaManager {
* 音频来源 * 音频来源
*/ */
private AudioSource audioSource; private AudioSource audioSource;
/**
* 视频捕获
*/
private VideoCapturer videoCapturer;
/** /**
* 主码流视频来源 * 主码流视频来源
*/ */
@@ -152,11 +137,15 @@ public final class MediaManager {
*/ */
private VideoSource shareVideoSource; private VideoSource shareVideoSource;
/** /**
* 录像终端 * 视频来源类型
*/ */
private RecordClient recordClient; private VideoSourceType videoSourceType;
/** /**
* 视频来源 * 视频捕获
*/
private VideoCapturer videoCapturer;
/**
* SurfaceTextureHelper
*/ */
private SurfaceTextureHelper surfaceTextureHelper; private SurfaceTextureHelper surfaceTextureHelper;
/** /**
@@ -168,32 +157,32 @@ public final class MediaManager {
*/ */
private JavaAudioDeviceModule javaAudioDeviceModule; private JavaAudioDeviceModule javaAudioDeviceModule;
/** /**
* 语音播报 * 录屏等待锁
*/ */
private TextToSpeech textToSpeech; private final Object screenLock;
/**
* 录像终端
*/
private RecordClient recordClient;
/** /**
* 视频处理 * 视频处理
*/ */
private VideoProcesser videoProcesser; private VideoProcesser videoProcesser;
/**
* 录屏等待锁
*/
private final Object screenLock = new Object();
static { static {
// // 设置采样 // 设置采样
// WebRtcAudioUtils.setDefaultSampleRateHz(48000); // WebRtcAudioUtils.setDefaultSampleRateHz(48000);
// // 噪声消除 // 噪声消除
// WebRtcAudioUtils.setWebRtcBasedNoiseSuppressor(true); // WebRtcAudioUtils.setWebRtcBasedNoiseSuppressor(true);
// // 回声消除 // 回声消除
// WebRtcAudioUtils.setWebRtcBasedAcousticEchoCanceler(true); // WebRtcAudioUtils.setWebRtcBasedAcousticEchoCanceler(true);
// // 自动增益 // 自动增益
// WebRtcAudioUtils.setWebRtcBasedAutomaticGainControl(true); // WebRtcAudioUtils.setWebRtcBasedAutomaticGainControl(true);
// // 支持的编码解码器 // 支持的编码解码器
final MediaCodecList mediaCodecList = new MediaCodecList(MediaCodecList.ALL_CODECS); final MediaCodecList mediaCodecList = new MediaCodecList(MediaCodecList.ALL_CODECS);
for (MediaCodecInfo mediaCodecInfo : mediaCodecList.getCodecInfos()) { for (MediaCodecInfo mediaCodecInfo : mediaCodecList.getCodecInfos()) {
// OMX.google = 软编
// OMX.core = 硬编 // OMX.core = 硬编
// OMX.google = 软编
final String[] supportedTypes = mediaCodecInfo.getSupportedTypes(); final String[] supportedTypes = mediaCodecInfo.getSupportedTypes();
final String type = mediaCodecInfo.isEncoder() ? "编码器" : "解码器"; final String type = mediaCodecInfo.isEncoder() ? "编码器" : "解码器";
Log.d(MediaManager.class.getSimpleName(), type + "名称:" + mediaCodecInfo.getName()); Log.d(MediaManager.class.getSimpleName(), type + "名称:" + mediaCodecInfo.getName());
@@ -208,19 +197,27 @@ public final class MediaManager {
} }
} }
private static final MediaManager INSTANCE = new MediaManager();
public static final MediaManager getInstance() {
return INSTANCE;
}
private MediaManager() { private MediaManager() {
this.clientCount = 0; this.clientCount = 0;
this.screenLock = new Object();
} }
/** /**
* @return 是否可用(所有配置加载完成) * @return 是否可用
*/ */
public boolean available() { public boolean available() {
return return
this.taoyao != null && this.taoyao != null &&
this.context != null && this.context != null &&
this.mainHandler != null && this.mainHandler != null &&
this.mediaProperties != null; this.mediaProperties != null &&
this.webrtcProperties != null;
} }
/** /**
@@ -250,19 +247,19 @@ public final class MediaManager {
* @param imageQuantity 图片质量 * @param imageQuantity 图片质量
* @param audioQuantity 音频质量 * @param audioQuantity 音频质量
* @param videoQuantity 视频质量 * @param videoQuantity 视频质量
* @param broadcaster 是否语音播报
* @param channelCount 音频通道数量 * @param channelCount 音频通道数量
* @param iFrameInterval 关键帧频率 * @param iFrameInterval 关键帧频率
* @param imagePath 图片保存路径 * @param imagePath 图片保存路径
* @param videoPath 视频保存路径 * @param videoPath 视频保存路径
* @param videoFile 分享视频文件路径
* @param watermark 水印信息 * @param watermark 水印信息
* @param videoSourceType 视频来源类型 * @param videoSourceType 视频来源类型
*/ */
public void initContext( public void initContext(
Handler mainHandler, Context context, Handler mainHandler, Context context,
int imageQuantity, String audioQuantity, String videoQuantity, int imageQuantity, String audioQuantity, String videoQuantity,
boolean broadcaster, int channelCount, int iFrameInterval, int channelCount, int iFrameInterval,
String imagePath, String videoPath, String imagePath, String videoPath, String videoFile,
String watermark, VideoSourceType videoSourceType String watermark, VideoSourceType videoSourceType
) { ) {
this.mainHandler = mainHandler; this.mainHandler = mainHandler;
@@ -270,11 +267,11 @@ public final class MediaManager {
this.imageQuantity = imageQuantity; this.imageQuantity = imageQuantity;
this.audioQuantity = audioQuantity; this.audioQuantity = audioQuantity;
this.videoQuantity = videoQuantity; this.videoQuantity = videoQuantity;
this.broadcaster = broadcaster;
this.channelCount = channelCount; this.channelCount = channelCount;
this.iFrameInterval = iFrameInterval; this.iFrameInterval = iFrameInterval;
this.imagePath = imagePath; this.imagePath = imagePath;
this.videoPath = videoPath; this.videoPath = videoPath;
this.videoFile = videoFile;
this.watermark = watermark; this.watermark = watermark;
this.videoSourceType = videoSourceType; this.videoSourceType = videoSourceType;
synchronized (this) { synchronized (this) {
@@ -292,18 +289,6 @@ public final class MediaManager {
} }
} }
public synchronized void broadcast(String text) {
if(!this.broadcaster) {
return;
}
if(this.textToSpeech == null) {
this.textToSpeech = new TextToSpeech(this.context, new MediaManager.TextToSpeechInitListener());
}
this.textToSpeech.speak(text, TextToSpeech.QUEUE_FLUSH, null, UUID.randomUUID().toString());
// this.textToSpeech.stop();
// this.textToSpeech.shutdown();
}
/** /**
* 新建终端 * 新建终端
* *
@@ -333,6 +318,7 @@ public final class MediaManager {
/** /**
* 关闭一个终端 * 关闭一个终端
* 所有终端关闭之后释放PeerConnectionFactory
* *
* @return 剩余终端数量 * @return 剩余终端数量
*/ */
@@ -341,7 +327,6 @@ public final class MediaManager {
this.clientCount--; this.clientCount--;
if (this.clientCount <= 0) { if (this.clientCount <= 0) {
Log.i(MediaManager.class.getSimpleName(), "释放PeerConnectionFactory"); Log.i(MediaManager.class.getSimpleName(), "释放PeerConnectionFactory");
// 注意顺序
this.stopVideoCapture(); this.stopVideoCapture();
this.closeMedia(); this.closeMedia();
this.nativeStop(); this.nativeStop();
@@ -351,6 +336,9 @@ public final class MediaManager {
} }
} }
/**
* 加载PeerConnectionFactory
*/
private void initPeerConnectionFactory() { private void initPeerConnectionFactory() {
PeerConnectionFactory.initialize( PeerConnectionFactory.initialize(
PeerConnectionFactory.InitializationOptions.builder(this.context) PeerConnectionFactory.InitializationOptions.builder(this.context)
@@ -364,6 +352,9 @@ public final class MediaManager {
this.eglContext = this.eglBase.getEglBaseContext(); this.eglContext = this.eglBase.getEglBaseContext();
} }
/**
* 释放PeerConnectionFactory
*/
private void stopPeerConnectionFactory() { private void stopPeerConnectionFactory() {
if (this.eglBase != null) { if (this.eglBase != null) {
this.eglBase.release(); this.eglBase.release();
@@ -389,37 +380,41 @@ public final class MediaManager {
// .setAudioEncoderFactoryFactory(new BuiltinAudioEncoderFactoryFactory()) // .setAudioEncoderFactoryFactory(new BuiltinAudioEncoderFactoryFactory())
// .setAudioDecoderFactoryFactory(new BuiltinAudioDecoderFactoryFactory()) // .setAudioDecoderFactoryFactory(new BuiltinAudioDecoderFactoryFactory())
.createPeerConnectionFactory(); .createPeerConnectionFactory();
Arrays.stream(videoEncoderFactory.getSupportedCodecs()).forEach(v -> { Arrays.stream(videoDecoderFactory.getSupportedCodecs()).forEach(v -> {
Log.d(MediaManager.class.getSimpleName(), "支持的视频解码器:" + v.name); Log.d(MediaManager.class.getSimpleName(), "支持的视频解码器:" + v.name);
}); });
Arrays.stream(videoEncoderFactory.getSupportedCodecs()).forEach(v -> {
Log.d(MediaManager.class.getSimpleName(), "支持的视频编码器:" + v.name);
});
this.initAudio(); this.initAudio();
this.initVideo(); this.initVideo();
this.initWatermark(); this.initWatermark();
} }
/**
* @return JavaAudioDeviceModule
*/
private JavaAudioDeviceModule javaAudioDeviceModule() { private JavaAudioDeviceModule javaAudioDeviceModule() {
// 本地声音回调:只有本地音频而且建立媒体之后才有回调
// WebRtcAudioRecord.setOnAudioSamplesReady(audioSamples -> {});
// 配置音频
// final AudioAttributes audioAttributes = new AudioAttributes.Builder() // final AudioAttributes audioAttributes = new AudioAttributes.Builder()
// .setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED) // .setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED)
// .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) // .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
// .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) // .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
// .build(); // .build();
// WebRtcAudioRecord.setOnAudioSamplesReady(audioSamples -> {
// if(this.recordClient != null) {
// this.recordClient.onWebRtcAudioRecordSamplesReady(audioSamples);
// }
// });
final JavaAudioDeviceModule javaAudioDeviceModule = JavaAudioDeviceModule.builder(this.context) final JavaAudioDeviceModule javaAudioDeviceModule = JavaAudioDeviceModule.builder(this.context)
// .setSampleRate(48000) // .setSampleRate(48000)
// .setSampleRate(mediaAudioProperties.getSampleRate()) // .setSampleRate(mediaAudioProperties.getSampleRate())
// .setAudioFormat(AudioFormat.ENCODING_PCM_16BIT) // .setAudioFormat(AudioFormat.ENCODING_PCM_16BIT)
// .setAudioSource(MediaRecorder.AudioSource.MIC) // .setAudioSource(MediaRecorder.AudioSource.MIC)
// .setAudioAttributes(audioAttributes) // .setAudioAttributes(audioAttributes)
// 超低延迟
// .setUseLowLatency() // .setUseLowLatency()
// .setUseStereoInput() // .setUseStereoInput()
// .setUseStereoOutput() // .setUseStereoOutput()
// 本地声音 // .setSamplesReadyCallback(audioSamples -> {})
// .setSamplesReadyCallback() // .setUseHardwareNoiseSuppressor(true)
// .setUseHardwareAcousticEchoCanceler(true)
.setAudioTrackErrorCallback(new JavaAudioDeviceModule.AudioTrackErrorCallback() { .setAudioTrackErrorCallback(new JavaAudioDeviceModule.AudioTrackErrorCallback() {
@Override @Override
public void onWebRtcAudioTrackInitError(String errorMessage) { public void onWebRtcAudioTrackInitError(String errorMessage) {
@@ -468,11 +463,7 @@ public final class MediaManager {
Log.i(MediaManager.class.getSimpleName(), "WebRTC本地音频录像结束"); Log.i(MediaManager.class.getSimpleName(), "WebRTC本地音频录像结束");
} }
}) })
// .setUseHardwareNoiseSuppressor(true)
// .setUseHardwareAcousticEchoCanceler(true)
.createAudioDeviceModule(); .createAudioDeviceModule();
// javaAudioDeviceModule.setSpeakerMute(false);
// javaAudioDeviceModule.setMicrophoneMute(false);
return javaAudioDeviceModule; return javaAudioDeviceModule;
} }
@@ -492,11 +483,11 @@ public final class MediaManager {
// 加载视频 // 加载视频
Log.i(MediaManager.class.getSimpleName(), "加载视频:" + this.videoSourceType); Log.i(MediaManager.class.getSimpleName(), "加载视频:" + this.videoSourceType);
if (this.videoSourceType == VideoSourceType.FILE) { if (this.videoSourceType == VideoSourceType.FILE) {
this.initFile(); this.initFileCapturer();
} else if (this.videoSourceType.isCamera()) { } else if (this.videoSourceType.isCamera()) {
this.initCamera(); this.initCameraCapturer();
} else if (this.videoSourceType == VideoSourceType.SCREEN) { } else if (this.videoSourceType == VideoSourceType.SCREEN) {
this.initScreenPromise(); this.initScreenCapturerPromise();
} else { } else {
// 其他来源 // 其他来源
} }
@@ -505,15 +496,19 @@ public final class MediaManager {
/** /**
* 加载文件采集 * 加载文件采集
*/ */
private void initFile() { private void initFileCapturer() {
// 自己实现 try {
// new FileVideoCapturer(); this.videoCapturer = new FileVideoCapturer(this.videoFile);
} catch (IOException e) {
Log.e(MediaManager.class.getSimpleName(), "加载视频异常:" + this.videoFile, e);
}
this.initVideoSource();
} }
/** /**
* 加载摄像头采集 * 加载摄像头采集
*/ */
private void initCamera() { private void initCameraCapturer() {
final CameraEnumerator cameraEnumerator = new Camera2Enumerator(this.context); final CameraEnumerator cameraEnumerator = new Camera2Enumerator(this.context);
final String[] names = cameraEnumerator.getDeviceNames(); final String[] names = cameraEnumerator.getDeviceNames();
for (String name : names) { for (String name : names) {
@@ -531,7 +526,7 @@ public final class MediaManager {
/** /**
* 加载屏幕采集 * 加载屏幕采集
*/ */
private void initScreenPromise() { private void initScreenCapturerPromise() {
this.mainHandler.obtainMessage(Config.WHAT_SCREEN_CAPTURE).sendToTarget(); this.mainHandler.obtainMessage(Config.WHAT_SCREEN_CAPTURE).sendToTarget();
synchronized (this.screenLock) { synchronized (this.screenLock) {
try { try {
@@ -547,7 +542,7 @@ public final class MediaManager {
* *
* @param intent Intent * @param intent Intent
*/ */
public void initScreen(Intent intent) { public void initScreenCapturer(Intent intent) {
this.videoCapturer = new ScreenCapturerAndroid(intent, new ScreenCallback()); this.videoCapturer = new ScreenCapturerAndroid(intent, new ScreenCallback());
this.initVideoSource(); this.initVideoSource();
synchronized (this.screenLock) { synchronized (this.screenLock) {
@@ -561,18 +556,22 @@ public final class MediaManager {
private void initVideoSource() { private void initVideoSource() {
// 加载视频 // 加载视频
this.surfaceTextureHelper = SurfaceTextureHelper.create("MediaVideoThread", this.eglContext); this.surfaceTextureHelper = SurfaceTextureHelper.create("MediaVideoThread", this.eglContext);
// 主码流
this.mainVideoSource = this.peerConnectionFactory.createVideoSource(this.videoCapturer.isScreencast()); this.mainVideoSource = this.peerConnectionFactory.createVideoSource(this.videoCapturer.isScreencast());
// 次码流
this.shareVideoSource = this.peerConnectionFactory.createVideoSource(this.videoCapturer.isScreencast()); this.shareVideoSource = this.peerConnectionFactory.createVideoSource(this.videoCapturer.isScreencast());
// 视频配置
final MediaVideoProperties mediaVideoProperties = this.mediaProperties.getVideo(); final MediaVideoProperties mediaVideoProperties = this.mediaProperties.getVideo();
this.shareVideoSource.adaptOutputFormat(mediaVideoProperties.getWidth(), mediaVideoProperties.getHeight(), mediaVideoProperties.getFrameRate()); this.shareVideoSource.adaptOutputFormat(mediaVideoProperties.getWidth(), mediaVideoProperties.getHeight(), mediaVideoProperties.getFrameRate());
// 视频捕获 // 视频捕获
this.videoCapturer.initialize(this.surfaceTextureHelper, this.context, new VideoCapturerObserver()); this.videoCapturer.initialize(this.surfaceTextureHelper, this.context, new VideoCapturerObserver());
} }
/**
* 加载水印
*/
private void initWatermark() { private void initWatermark() {
if(StringUtils.isNotEmpty(this.watermark)) { if(StringUtils.isEmpty(this.watermark)) {
return;
}
final MediaVideoProperties mediaVideoProperties = this.mediaProperties.getVideos().get(this.videoQuantity); final MediaVideoProperties mediaVideoProperties = this.mediaProperties.getVideos().get(this.videoQuantity);
if(this.videoProcesser == null) { if(this.videoProcesser == null) {
this.videoProcesser = new WatermarkProcesser(this.watermark, mediaVideoProperties.getWidth(), mediaVideoProperties.getHeight()); this.videoProcesser = new WatermarkProcesser(this.watermark, mediaVideoProperties.getWidth(), mediaVideoProperties.getHeight());
@@ -580,20 +579,31 @@ public final class MediaManager {
this.videoProcesser = new WatermarkProcesser(this.watermark, mediaVideoProperties.getWidth(), mediaVideoProperties.getHeight(), this.videoProcesser); this.videoProcesser = new WatermarkProcesser(this.watermark, mediaVideoProperties.getWidth(), mediaVideoProperties.getHeight(), this.videoProcesser);
} }
} }
}
/**
* 静音远程媒体
*/
public void muteAllRemote() { public void muteAllRemote() {
this.javaAudioDeviceModule.setSpeakerMute(true); this.javaAudioDeviceModule.setSpeakerMute(true);
} }
/**
* 取消远程媒体静音
*/
public void unmuteAllRemote() { public void unmuteAllRemote() {
this.javaAudioDeviceModule.setSpeakerMute(false); this.javaAudioDeviceModule.setSpeakerMute(false);
} }
/**
* 静音本地媒体
*/
public void muteAllLocal() { public void muteAllLocal() {
this.javaAudioDeviceModule.setMicrophoneMute(true); this.javaAudioDeviceModule.setMicrophoneMute(true);
} }
/**
* 取消本地媒体静音
*/
public void unmuteAllLocal() { public void unmuteAllLocal() {
this.javaAudioDeviceModule.setMicrophoneMute(false); this.javaAudioDeviceModule.setMicrophoneMute(false);
} }
@@ -612,32 +622,55 @@ public final class MediaManager {
} }
} }
/**
* 更新音频配置
*
* @param mediaAudioProperties 音频配置
*/
public void updateAudioConfig(MediaAudioProperties mediaAudioProperties) { public void updateAudioConfig(MediaAudioProperties mediaAudioProperties) {
this.mediaProperties.setAudio(mediaAudioProperties); this.mediaProperties.setAudio(mediaAudioProperties);
this.updateAudioConfig(); this.updateAudioConfig();
} }
/**
* 更新音频配置
*/
private void updateAudioConfig() { private void updateAudioConfig() {
MediaAudioProperties mediaAudioProperties = this.mediaProperties.getAudio(); MediaAudioProperties mediaAudioProperties = this.mediaProperties.getAudio();
// TODO调整音频 // TODO调整音频
} }
/**
* 更新视频配置
*
* @param mediaVideoProperties 视频配置
*/
public void updateVideoConfig(MediaVideoProperties mediaVideoProperties) { public void updateVideoConfig(MediaVideoProperties mediaVideoProperties) {
this.mediaProperties.setVideo(mediaVideoProperties); this.mediaProperties.setVideo(mediaVideoProperties);
this.updateVideoConfig(); this.updateVideoConfig();
} }
/**
* 更新视频配置
*/
private void updateVideoConfig() { private void updateVideoConfig() {
// 更新视频采集
if(this.videoCapturer != null) { if(this.videoCapturer != null) {
final MediaVideoProperties mediaVideoProperties = this.mediaProperties.getVideos().get(this.videoQuantity); final MediaVideoProperties mediaVideoProperties = this.mediaProperties.getVideos().get(this.videoQuantity);
this.videoCapturer.changeCaptureFormat(mediaVideoProperties.getWidth(), mediaVideoProperties.getHeight(), mediaVideoProperties.getFrameRate()); this.videoCapturer.changeCaptureFormat(mediaVideoProperties.getWidth(), mediaVideoProperties.getHeight(), mediaVideoProperties.getFrameRate());
} }
// 更新共享视频
if(this.shareVideoSource != null) { if(this.shareVideoSource != null) {
final MediaVideoProperties mediaVideoProperties = this.mediaProperties.getVideo(); final MediaVideoProperties mediaVideoProperties = this.mediaProperties.getVideo();
this.shareVideoSource.adaptOutputFormat(mediaVideoProperties.getWidth(), mediaVideoProperties.getHeight(), mediaVideoProperties.getFrameRate()); this.shareVideoSource.adaptOutputFormat(mediaVideoProperties.getWidth(), mediaVideoProperties.getHeight(), mediaVideoProperties.getFrameRate());
} }
} }
/**
* 更新WebRTC配置
*
* @param webrtcProperties WebRTC配置
*/
public void updateWebrtcConfig(WebrtcProperties webrtcProperties) { public void updateWebrtcConfig(WebrtcProperties webrtcProperties) {
this.webrtcProperties = webrtcProperties; this.webrtcProperties = webrtcProperties;
} }
@@ -647,7 +680,7 @@ public final class MediaManager {
* *
* @param videoSourceType 来源类型 * @param videoSourceType 来源类型
*/ */
public void updateVideoSource(VideoSourceType videoSourceType) { public void switchVideoSource(VideoSourceType videoSourceType) {
if (this.videoSourceType == videoSourceType) { if (this.videoSourceType == videoSourceType) {
return; return;
} }
@@ -668,11 +701,20 @@ public final class MediaManager {
} }
}); });
} else { } else {
// TODO测试
// this.initVideo(); // this.initVideo();
// 切换所有VideoTrack视频来源 // 切换所有VideoTrack视频来源
} }
} }
/**
* 新建本地媒体
*
* @param audioProduce 是否生产音频
* @param videoProduce 是否生产视频
*
* @return 本地媒体
*/
public MediaStream buildLocalMediaStream(boolean audioProduce, boolean videoProduce) { public MediaStream buildLocalMediaStream(boolean audioProduce, boolean videoProduce) {
final long id = Thread.currentThread().getId(); final long id = Thread.currentThread().getId();
final MediaStream mediaStream = this.peerConnectionFactory.createLocalMediaStream("TaoyaoM" + id); final MediaStream mediaStream = this.peerConnectionFactory.createLocalMediaStream("TaoyaoM" + id);
@@ -687,12 +729,17 @@ public final class MediaManager {
if(videoProduce) { if(videoProduce) {
final VideoTrack videoTrack = this.peerConnectionFactory.createVideoTrack("TaoyaoV" + id, this.shareVideoSource); final VideoTrack videoTrack = this.peerConnectionFactory.createVideoTrack("TaoyaoV" + id, this.shareVideoSource);
videoTrack.setEnabled(true); videoTrack.setEnabled(true);
Log.i(MediaManager.class.getSimpleName(), "加载视频(次码流):" + videoTrack.id());
mediaStream.addTrack(videoTrack); mediaStream.addTrack(videoTrack);
Log.i(MediaManager.class.getSimpleName(), "加载视频:" + videoTrack.id());
} }
return mediaStream; return mediaStream;
} }
/**
* 新建媒体约束
*
* @return 媒体约束
*/
public MediaConstraints buildMediaConstraints() { public MediaConstraints buildMediaConstraints() {
final MediaConstraints mediaConstraints = new MediaConstraints(); final MediaConstraints mediaConstraints = new MediaConstraints();
// ================ PC ================ // // ================ PC ================ //
@@ -726,14 +773,21 @@ public final class MediaManager {
return mediaConstraints; return mediaConstraints;
} }
/**
* 开始采集
*/
private void startVideoCapture() { private void startVideoCapture() {
if(this.videoCapturer == null) { if(this.videoCapturer == null) {
return; return;
} }
final MediaVideoProperties mediaVideoProperties = this.mediaProperties.getVideos().get(this.videoQuantity); final MediaVideoProperties mediaVideoProperties = this.mediaProperties.getVideos().get(this.videoQuantity);
this.videoCapturer.startCapture(mediaVideoProperties.getWidth(), mediaVideoProperties.getHeight(), mediaVideoProperties.getFrameRate()); this.videoCapturer.startCapture(mediaVideoProperties.getWidth(), mediaVideoProperties.getHeight(), mediaVideoProperties.getFrameRate());
Log.i(MediaManager.class.getSimpleName(), "开始视频采集:" + mediaVideoProperties.getWidth() + "*" + mediaVideoProperties.getHeight() + " - " + mediaVideoProperties.getFrameRate());
} }
/**
* 关闭采集
*/
private void stopVideoCapture() { private void stopVideoCapture() {
if(this.videoCapturer == null) { if(this.videoCapturer == null) {
return; return;
@@ -741,10 +795,15 @@ public final class MediaManager {
try { try {
this.videoCapturer.stopCapture(); this.videoCapturer.stopCapture();
} catch (InterruptedException e) { } catch (InterruptedException e) {
Log.e(MediaManager.class.getSimpleName(), "关闭视频捕获异常", e); Log.e(MediaManager.class.getSimpleName(), "关闭视频采集异常", e);
} }
} }
/**
* 开始拍照
*
* @return 图片文件地址
*/
public String photograph() { public String photograph() {
synchronized (this) { synchronized (this) {
final PhotographClient photographClient = new PhotographClient(this.imageQuantity, this.imagePath); final PhotographClient photographClient = new PhotographClient(this.imageQuantity, this.imagePath);
@@ -758,6 +817,11 @@ public final class MediaManager {
} }
} }
/**
* 开始录像
*
* @return 录像终端
*/
public RecordClient startRecord() { public RecordClient startRecord() {
synchronized (this) { synchronized (this) {
if(this.recordClient != null) { if(this.recordClient != null) {
@@ -778,6 +842,11 @@ public final class MediaManager {
} }
} }
/**
* 结束录像
*
* @return 视频文件地址
*/
public String stopRecord() { public String stopRecord() {
synchronized (this) { synchronized (this) {
if(this.recordClient == null) { if(this.recordClient == null) {
@@ -793,6 +862,8 @@ public final class MediaManager {
} }
/** /**
* 预览视频
*
* @param flag Config.WHAT_* * @param flag Config.WHAT_*
* @param videoTrack 视频媒体流Track * @param videoTrack 视频媒体流Track
* *
@@ -822,6 +893,9 @@ public final class MediaManager {
return surfaceViewRenderer; return surfaceViewRenderer;
} }
/**
* 关闭媒体
*/
private void closeMedia() { private void closeMedia() {
if(this.audioSource != null) { if(this.audioSource != null) {
this.audioSource.dispose(); this.audioSource.dispose();
@@ -864,7 +938,13 @@ public final class MediaManager {
*/ */
private class VideoCapturerObserver implements CapturerObserver { private class VideoCapturerObserver implements CapturerObserver {
/**
* 主码流观察者
*/
private CapturerObserver mainObserver; private CapturerObserver mainObserver;
/**
* 次码流观察者
*/
private CapturerObserver shareObserver; private CapturerObserver shareObserver;
public VideoCapturerObserver() { public VideoCapturerObserver() {
@@ -963,18 +1043,6 @@ public final class MediaManager {
} }
private class TextToSpeechInitListener implements TextToSpeech.OnInitListener {
@Override
public void onInit(int status) {
Log.i(MediaManager.class.getSimpleName(), "加载语音播报:" + status);
if(status == TextToSpeech.SUCCESS) {
MediaManager.this.textToSpeech.setLanguage(Locale.CANADA);
MediaManager.this.textToSpeech.setPitch(1.0F);
MediaManager.this.textToSpeech.setSpeechRate(1.0F);
}
}
}
/** /**
* 加载MediasoupClient * 加载MediasoupClient
*/ */

View File

@@ -16,6 +16,7 @@ import java.util.TimerTask;
/** /**
* 水印处理器 * 水印处理器
* 只支持时间字符串水印
* *
* 性能优化: * 性能优化:
* 没有水印20~25波动 * 没有水印20~25波动