[+] 添加屏幕视频来源

This commit is contained in:
acgist
2023-05-06 08:16:01 +08:00
parent 7f23d86df2
commit f59840e2c6
6 changed files with 244 additions and 139 deletions

View File

@@ -1,4 +1,4 @@
# 部署
# 项目部署
## 整体环境
@@ -8,10 +8,11 @@ git >= 1.8.0
pm2 >= 5.2.0
Java >= 17.0.0
Maven >= 3.8.0
CMake >= 3.26.0
nodejs >= v16.18.0
python >= 3.8.0 with PIP
Android >= 10
gcc/g++ >= 10.2.0
node version >= v16.18.0
python version >= 3.8.0 with PIP
```
## 设置Yum源
@@ -119,8 +120,7 @@ cmake -v
mkdir -p /data/dev/nodejs
cd /data/dev/nodejs
wget https://nodejs.org/dist/v16.19.0/node-v16.19.0-linux-x64.tar.xz
xz -d node-v16.19.0-linux-x64.tar.xz
tar -xf node-v16.19.0-linux-x64.tar
tar -xvJf node-v16.19.0-linux-x64.tar.xz
# 连接
ln -sf /data/dev/nodejs/node-v16.19.0-linux-x64/bin/npm /usr/local/bin/
@@ -213,8 +213,7 @@ mkdir -p /data/dev/python
cd /data/dev/python
#wget https://www.python.org/ftp/python/3.8.16/Python-3.8.16.tar.xz
wget https://mirrors.huaweicloud.com/python/3.8.16/Python-3.8.16.tar.xz
xz -d Python-3.8.16.tar.xz
tar -xf Python-3.8.16.tar
tar -xvJf Python-3.8.16.tar.xz
# 安装
cd Python-3.8.16
@@ -226,7 +225,7 @@ ln -sf /usr/local/python3/bin/pip3.8 /usr/bin/pip
ln -sf /usr/local/python3/bin/python3.8 /usr/bin/python
ln -sf /usr/local/python3/bin/python3.8 /usr/bin/python3
# 配置YUM
# 配置Yum
vim /usr/bin/yum
vim /usr/libexec/urlgrabber-ext-down
@@ -328,15 +327,13 @@ pm2 start | stop | restart taoyao-client-media
### Mediasoup编译失败
编译过程中的依赖下载容易失败,
需要进入目录`mediasoup/worker/subprojects`,查看`*.wrap`文件依次下载所需依赖,修改名称放到`packagefiles`目录中,最后注释下载链接。
`package.json`中的`mediasoup`改为本地依赖`file:./mediasoup`,重新编译即可。
编译过程中的依赖下载容易失败,需要进入目录`mediasoup/worker/subprojects`,查看`*.wrap`文件依次下载所需依赖,修改名称放到`packagefiles`目录中,最后注释下载链接。将`package.json`中的`mediasoup`改为本地依赖`file:./mediasoup`,重新编译即可。
> 下载依赖建议备份以备以后编译使用
> 下载依赖建议备份方便再次编译使用
### Mediasoup单独编译
编译媒体服务时会自动编译`mediasoup`所以可以不用单独编译
编译媒体服务时会自动编译`mediasoup`所以忽略单独编译
```
# 编译代码
@@ -350,6 +347,8 @@ make clean
## 安装Web终端
`Nginx``PM2`选择一种启动即可
```
# 编译代码
cd /data/taoyao/taoyao-client-web
@@ -386,13 +385,13 @@ sh ./gradlew --no-daemon assembleRelease
## 配置防火墙
```
# Nginx端口
# 终端服务WebNginx
firewall-cmd --zone=public --add-port=443/tcp --permanent
# 终端服务建议使用Nginx代理
# 终端服务WebPM2
firewall-cmd --zone=public --add-port=8443/tcp --permanent
# 信令服务WebSocket
firewall-cmd --zone=public --add-port=8888/tcp --permanent
# 信令服务Socket:没有启用不用添加规则
# 信令服务Socket
firewall-cmd --zone=public --add-port=9999/tcp --permanent
# 媒体服务
firewall-cmd --zone=public --add-port=40000-49999/udp --permanent

View File

@@ -11,11 +11,10 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;
import android.view.Display;
import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.GridLayout;
@@ -31,7 +30,8 @@ import com.acgist.taoyao.media.VideoSourceType;
import com.acgist.taoyao.media.config.Config;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.io.Serializable;
import org.apache.commons.lang3.RandomUtils;
import java.util.stream.Stream;
/**
@@ -39,9 +39,9 @@ import java.util.stream.Stream;
*
* @author acgist
*/
public class MainActivity extends AppCompatActivity implements Serializable {
public class MainActivity extends AppCompatActivity {
private Handler threadHandler;
private Handler actionHandler;
private MainHandler mainHandler;
private ActivityMainBinding binding;
private MediaProjectionManager mediaProjectionManager;
@@ -51,17 +51,20 @@ public class MainActivity extends AppCompatActivity implements Serializable {
protected void onCreate(Bundle bundle) {
Log.i(MainActivity.class.getSimpleName(), "onCreate");
super.onCreate(bundle);
final Window window = this.getWindow();
// 强制横屏
// this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
this.buildAction();
this.requestPermission();
this.launchMediaService();
this.registerMediaProjection();
this.setTurnScreenOn(true);
this.setShowWhenLocked(true);
this.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
this.binding = ActivityMainBinding.inflate(this.getLayoutInflater());
this.setContentView(this.binding.getRoot());
this.binding.getRoot().setZ(100F);
this.registerMediaProjection();
final View root = this.binding.getRoot();
root.setZ(100F);
this.setContentView(root);
this.binding.action.setOnClickListener(this::action);
this.binding.record.setOnClickListener(this::record);
this.binding.settings.setOnClickListener(this::settings);
@@ -86,13 +89,25 @@ public class MainActivity extends AppCompatActivity implements Serializable {
super.onDestroy();
}
/**
* 设置按钮线程
* 防止按钮任务主线程等待导致主线程死锁
*/
private void buildAction() {
if(this.actionHandler != null) {
return;
}
final HandlerThread handlerThread = new HandlerThread("ActionThread");
handlerThread.start();
this.actionHandler = new Handler(handlerThread.getLooper());
}
/**
* 请求权限
*/
private void requestPermission() {
final String[] permissions = new String[]{
final String[] permissions = new String[] {
Manifest.permission.CAMERA,
Manifest.permission.REBOOT,
Manifest.permission.INTERNET,
Manifest.permission.RECORD_AUDIO,
Manifest.permission.ACCESS_WIFI_STATE,
@@ -107,7 +122,7 @@ public class MainActivity extends AppCompatActivity implements Serializable {
if (Stream.of(permissions).map(this.getApplicationContext()::checkSelfPermission).allMatch(v -> v == PackageManager.PERMISSION_GRANTED)) {
Log.i(MediaService.class.getSimpleName(), "授权成功");
} else {
ActivityCompat.requestPermissions(this, permissions, 0);
ActivityCompat.requestPermissions(this, permissions, RandomUtils.nextInt());
}
}
@@ -118,7 +133,6 @@ public class MainActivity extends AppCompatActivity implements Serializable {
if (this.mainHandler != null) {
return;
}
int waitCount = 0;
this.mainHandler = new MainHandler();
final Resources resources = this.getResources();
MediaManager.getInstance().initContext(
@@ -133,24 +147,19 @@ public class MainActivity extends AppCompatActivity implements Serializable {
resources.getString(R.string.watermark),
VideoSourceType.valueOf(resources.getString(R.string.videoSourceType))
);
final Display display = this.getWindow().getContext().getDisplay();
while (Display.STATE_ON != display.getState() && waitCount++ < 10) {
SystemClock.sleep(100);
}
if (display.STATE_ON == display.getState()) {
Log.i(MainActivity.class.getSimpleName(), "拉起媒体服务");
final Intent intent = new Intent(this, MediaService.class);
intent.setAction(MediaService.Action.CONNECT.name());
// 注意不能使用intent传递
MediaService.mainHandler = this.mainHandler;
this.startService(intent);
} else {
Log.w(MainActivity.class.getSimpleName(), "拉起媒体服务失败");
}
// 注意不能使用intent传递
MediaService.mainHandler = this.mainHandler;
Log.i(MainActivity.class.getSimpleName(), "拉起媒体服务");
final Intent intent = new Intent(this, MediaService.class);
intent.setAction(MediaService.Action.CONNECT.name());
this.startService(intent);
}
/**
* 注册捕获屏幕功能
*/
private void registerMediaProjection() {
if (this.activityResultLauncher != null && this.mediaProjectionManager != null) {
if (this.mediaProjectionManager != null && this.activityResultLauncher != null) {
return;
}
this.mediaProjectionManager = this.getApplicationContext().getSystemService(MediaProjectionManager.class);
@@ -172,58 +181,75 @@ public class MainActivity extends AppCompatActivity implements Serializable {
}
/**
* 功能测试按钮根据实际情况设置功能
* 功能按钮(测试使用)
*
* @param view View
*/
private void action(View view) {
if (this.threadHandler == null) {
final HandlerThread handlerThread = new HandlerThread("ActionThread");
handlerThread.start();
this.threadHandler = new Handler(handlerThread.getLooper());
}
this.threadHandler.post(() -> {
this.actionHandler.post(() -> {
// 进入房间
// Taoyao.taoyao.roomEnter("4f19f6fc-1763-499b-a352-d8c955af5a6e", null);
// 监控终端
// Taoyao.taoyao.sessionCall("taoyao");
});
}
/**
* 录像按钮
*
* @param view View
*/
private void record(View view) {
final MediaManager mediaManager = MediaManager.getInstance();
if (mediaManager.isRecording()) {
mediaManager.stopRecord();
} else {
mediaManager.startRecord();
}
}
private void settings(View view) {
final Intent intent = new Intent(this, SettingsActivity.class);
this.startActivity(intent);
}
private void photograph(View view) {
MediaManager.getInstance().photograph();
this.actionHandler.post(() -> {
final MediaManager mediaManager = MediaManager.getInstance();
if (mediaManager.isRecording()) {
mediaManager.stopRecord();
} else {
mediaManager.startRecord();
}
});
}
/**
* Handler
* 设置按钮
*
* @param view View
*/
private void settings(View view) {
this.actionHandler.post(() -> {
final Intent intent = new Intent(this, SettingsActivity.class);
this.startActivity(intent);
});
}
/**
* 拍照按钮
*
* @param view View
*/
private void photograph(View view) {
this.actionHandler.post(() -> {
MediaManager.getInstance().photograph();
});
}
/**
* MainHandler
*
* @author acgist
*/
public class MainHandler extends Handler implements Serializable {
public class MainHandler extends Handler {
@Override
public void handleMessage(@NonNull Message message) {
super.handleMessage(message);
Log.d(MainHandler.class.getSimpleName(), "Handler消息" + message.what);
Log.d(MainHandler.class.getSimpleName(), "MainHandler消息" + message.what);
switch (message.what) {
case Config.WHAT_SCREEN_CAPTURE -> MainActivity.this.screenCapture(message);
case Config.WHAT_RECORD -> MainActivity.this.record(message);
case Config.WHAT_NEW_LOCAL_VIDEO,
Config.WHAT_NEW_REMOTE_VIDEO -> MainActivity.this.previewVideo(message);
case Config.WHAT_REMOVE_VIDEO -> MainActivity.this.removeVideo(message);
case Config.WHAT_REMOVE_VIDEO -> MainActivity.this.removePreviewVideo(message);
}
}
@@ -238,27 +264,34 @@ public class MainActivity extends AppCompatActivity implements Serializable {
this.activityResultLauncher.launch(this.mediaProjectionManager.createScreenCaptureIntent());
}
/**
* 录制按钮
*
* @param message 消息
*/
private void record(Message message) {
final Resources resources = this.getResources();
final Resources resources = this.getResources();
final Resources.Theme theme = this.getTheme();
final FloatingActionButton record = this.binding.record;
if(Boolean.TRUE.equals(message.obj)) {
record.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(R.color.purple_500, this.getTheme())));
record.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(R.color.purple_500, theme)));
} else {
record.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(R.color.teal_200, this.getTheme())));
record.setBackgroundTintList(ColorStateList.valueOf(resources.getColor(R.color.teal_200, theme)));
}
}
/**
* 预览用户视频
* 视频预览
*
* @param message 消息
*/
private void previewVideo(Message message) {
private synchronized void previewVideo(Message message) {
final GridLayout video = this.binding.video;
final int count = video.getChildCount();
final GridLayout.Spec rowSpec = GridLayout.spec(count / 2, 1.0F);
final GridLayout.Spec columnSpec = GridLayout.spec(count % 2, 1.0F);
GridLayout.LayoutParams layoutParams = new GridLayout.LayoutParams(rowSpec, columnSpec);
final int count = video.getChildCount();
final GridLayout.LayoutParams layoutParams = new GridLayout.LayoutParams(
GridLayout.spec(count / 2, 1.0F),
GridLayout.spec(count % 2, 1.0F)
);
layoutParams.width = 0;
layoutParams.height = 0;
final SurfaceView surfaceView = (SurfaceView) message.obj;
@@ -266,16 +299,19 @@ public class MainActivity extends AppCompatActivity implements Serializable {
video.addView(surfaceView, layoutParams);
}
private void removeVideo(Message message) {
synchronized (this) {
final GridLayout video = this.binding.video;
final SurfaceView surfaceView = (SurfaceView) message.obj;
final int index = video.indexOfChild(surfaceView);
if(index < 0) {
return;
}
video.removeViewAt(index);
/**
* 移除视频预览
*
* @param message 消息
*/
private synchronized void removePreviewVideo(Message message) {
final GridLayout video = this.binding.video;
final SurfaceView surfaceView = (SurfaceView) message.obj;
final int index = video.indexOfChild(surfaceView);
if(index < 0) {
return;
}
video.removeViewAt(index);
}
}

View File

@@ -158,6 +158,10 @@ public final class MediaManager {
* 视频处理
*/
private VideoProcesser videoProcesser;
/**
* 录屏等待锁
*/
private final Object screenLock = new Object();
static {
// // 设置采样
@@ -189,7 +193,7 @@ public final class MediaManager {
}
/**
* @return 是否可用所有配置加载完成
* @return 是否可用所有配置加载完成
*/
public boolean available() {
return
@@ -199,8 +203,38 @@ public final class MediaManager {
}
/**
* @param mainHandler Handler
* @param context 上下文
* @return 是否正在录制
*/
public boolean isRecording() {
return this.recordClient != null;
}
/**
* @return MediaProperties
*/
public MediaProperties getMediaProperties() {
return this.mediaProperties;
}
/**
* @return WebrtcProperties
*/
public WebrtcProperties getWebrtcProperties() {
return this.webrtcProperties;
}
/**
* @param mainHandler MainHandler
* @param context 上下文
* @param imageQuantity 图片质量
* @param audioQuantity 音频质量
* @param videoQuantity 视频质量
* @param channelCount 音频通道数量
* @param iFrameInterval 关键帧频率
* @param imagePath 图片保存路径
* @param videoPath 视频保存路径
* @param watermark 水印信息
* @param videoSourceType 视频来源类型
*/
public void initContext(
Handler mainHandler, Context context,
@@ -223,7 +257,7 @@ public final class MediaManager {
}
/**
* @param taoyao 信令
* @param taoyao 信令
*/
public void initTaoyao(ITaoyao taoyao) {
this.taoyao = taoyao;
@@ -276,18 +310,6 @@ public final class MediaManager {
}
}
public boolean isRecording() {
return this.recordClient != null;
}
public MediaProperties getMediaProperties() {
return this.mediaProperties;
}
public WebrtcProperties getWebrtcProperties() {
return this.webrtcProperties;
}
private void initPeerConnectionFactory() {
PeerConnectionFactory.initialize(
PeerConnectionFactory.InitializationOptions.builder(this.context)
@@ -407,17 +429,22 @@ public final class MediaManager {
} else if (this.videoSourceType.isCamera()) {
this.initCamera();
} else if (this.videoSourceType == VideoSourceType.SCREEN) {
this.initSharePromise();
this.initScreenPromise();
} else {
// 其他来源
}
}
/**
* 加载文件采集
*/
private void initFile() {
// 自己实现
// new FileVideoCapturer();
}
/**
* 加载摄像头
* 加载摄像头采集
*/
private void initCamera() {
final CameraEnumerator cameraEnumerator = new Camera2Enumerator(this.context);
@@ -434,28 +461,39 @@ public final class MediaManager {
this.initVideoSource();
}
private void initSharePromise() {
/**
* 加载屏幕采集
*/
private void initScreenPromise() {
this.mainHandler.obtainMessage(Config.WHAT_SCREEN_CAPTURE).sendToTarget();
synchronized (this.screenLock) {
try {
this.screenLock.wait();
} catch (InterruptedException e) {
Log.e(MediaManager.class.getSimpleName(), "等待录屏授权异常", e);
}
}
}
/**
* 加载屏幕
* 加载屏幕采集
*
* @param intent Intent
*/
public void initScreen(Intent intent) {
this.videoCapturer = new ScreenCapturerAndroid(intent, new ScreenCallback());
this.initVideoSource();
synchronized (this.screenLock) {
this.screenLock.notifyAll();
}
}
/**
* 加载视频
* 加载视频来源
*/
private void initVideoSource() {
// 加载视频
this.surfaceTextureHelper = SurfaceTextureHelper.create("MediaVideoThread", this.eglContext);
// this.surfaceTextureHelper.setTextureSize();
// this.surfaceTextureHelper.setFrameRotation();
// 主码流
this.mainVideoSource = this.peerConnectionFactory.createVideoSource(this.videoCapturer.isScreencast());
// 次码流
@@ -464,8 +502,6 @@ public final class MediaManager {
this.shareVideoSource.adaptOutputFormat(mediaVideoProperties.getWidth(), mediaVideoProperties.getHeight(), mediaVideoProperties.getFrameRate());
// 视频捕获
this.videoCapturer.initialize(this.surfaceTextureHelper, this.context, new VideoCapturerObserver());
// 次码流视频处理
// this.shareVideoSource.setVideoProcessor();
}
private void initWatermark() {
@@ -541,13 +577,16 @@ public final class MediaManager {
cameraVideoCapturer.switchCamera(new CameraVideoCapturer.CameraSwitchHandler() {
@Override
public void onCameraSwitchDone(boolean success) {
Log.d(MediaManager.class.getSimpleName(), "切换镜头成功");
}
@Override
public void onCameraSwitchError(String message) {
Log.e(MediaManager.class.getSimpleName(), "切换镜头失败:" + message);
}
});
} else {
this.initVideo();
// this.initVideo();
// 切换所有VideoTrack视频来源
}
}
@@ -625,18 +664,14 @@ public final class MediaManager {
public String photograph() {
synchronized (this) {
String filepath;
final PhotographClient photographClient = new PhotographClient(this.imageQuantity, this.imagePath);
if(this.clientCount <= 0) {
final MediaVideoProperties mediaVideoProperties = this.mediaProperties.getVideos().get(this.videoQuantity);
photographClient.photograph(mediaVideoProperties.getWidth(), mediaVideoProperties.getHeight(), mediaVideoProperties.getFrameRate(), this.videoSourceType, this.context);
filepath = photographClient.waitForPhotograph();
} else {
final VideoTrack videoTrack = this.peerConnectionFactory.createVideoTrack("TaoyaoVP", this.mainVideoSource);
photographClient.photograph(videoTrack);
filepath = photographClient.waitForPhotograph();
photographClient.photograph(this.mainVideoSource, this.peerConnectionFactory);
}
return filepath;
return photographClient.waitForPhotograph();
}
}
@@ -654,8 +689,7 @@ public final class MediaManager {
this.videoPath, this.taoyao, this.mainHandler
);
this.recordClient.start();
final VideoTrack videoTrack = this.peerConnectionFactory.createVideoTrack("TaoyaoVR", this.mainVideoSource);
this.recordClient.source(videoTrack);
this.recordClient.record(this.mainVideoSource, this.peerConnectionFactory);
this.mainHandler.obtainMessage(Config.WHAT_RECORD, Boolean.TRUE).sendToTarget();
return this.recordClient;
}

View File

@@ -27,8 +27,10 @@ import android.view.Surface;
import com.acgist.taoyao.boot.utils.DateUtils;
import com.acgist.taoyao.media.VideoSourceType;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.VideoFrame;
import org.webrtc.VideoSink;
import org.webrtc.VideoSource;
import org.webrtc.VideoTrack;
import java.io.ByteArrayOutputStream;
@@ -94,10 +96,17 @@ public class PhotographClient implements VideoSink {
return this.filepath;
}
public void photograph(VideoTrack videoTrack) {
videoTrack.setEnabled(true);
videoTrack.addSink(this);
this.videoTrack = videoTrack;
public void photograph(VideoSource videoSource, PeerConnectionFactory peerConnectionFactory) {
if(this.videoTrack != null) {
return;
}
if(videoSource == null || peerConnectionFactory == null) {
Log.e(PhotographClient.class.getSimpleName(), "数据采集无效");
return;
}
this.videoTrack = peerConnectionFactory.createVideoTrack("TaoyaoVP", videoSource);
this.videoTrack.setEnabled(true);
this.videoTrack.addSink(this);
}
@Override
@@ -178,6 +187,9 @@ public class PhotographClient implements VideoSink {
@SuppressLint("MissingPermission")
public void photograph(int width, int height, int fps, VideoSourceType videoSourceType, Context context) {
if(this.handlerThread != null) {
return;
}
this.handlerThread = new HandlerThread("PhotographThread");
this.handlerThread.start();
final Handler handler = new Handler(this.handlerThread.getLooper());
@@ -200,6 +212,11 @@ public class PhotographClient implements VideoSink {
// TODO截屏
}
}
if(cameraId == null) {
Log.e(PhotographClient.class.getSimpleName(), "拍照失败没有适配:" + videoSourceType);
PhotographClient.this.notifyWait();
return;
}
cameraManager.openCamera(cameraId, this.cameraDeviceStateCallback, null);
} catch (CameraAccessException e) {
Log.e(PhotographClient.class.getSimpleName(), "拍照异常", e);

View File

@@ -13,8 +13,10 @@ import com.acgist.taoyao.boot.utils.DateUtils;
import com.acgist.taoyao.media.MediaManager;
import com.acgist.taoyao.media.signal.ITaoyao;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.VideoFrame;
import org.webrtc.VideoSink;
import org.webrtc.VideoSource;
import org.webrtc.VideoTrack;
import org.webrtc.YuvHelper;
import org.webrtc.audio.JavaAudioDeviceModule;
@@ -127,7 +129,8 @@ public class RecordClient extends Client implements VideoSink, JavaAudioDeviceMo
public RecordClient(
int audioBitRate, int sampleRate, int channelCount,
int videoBitRate, int frameRate, int iFrameInterval, int width, int height,
int videoBitRate, int frameRate, int iFrameInterval,
int width, int height,
String path, ITaoyao taoyao, Handler mainHandler
) {
super("本地录像", "LocalRecordClient", taoyao, mainHandler);
@@ -140,8 +143,8 @@ public class RecordClient extends Client implements VideoSink, JavaAudioDeviceMo
this.width = width;
this.height = height;
this.yuvSize = width * height * 3 / 2;
this.filename = DateUtils.format(LocalDateTime.now(), DateUtils.DateTimeStyle.YYYYMMDDHH24MMSS) + ".mp4";
this.filepath = Paths.get(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath(), path, this.filename).toString();
this.filename = DateUtils.format(LocalDateTime.now(), DateUtils.DateTimeStyle.YYYYMMDDHH24MMSS) + ".mp4";
this.filepath = Paths.get(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath(), path, this.filename).toString();
}
public void start() {
@@ -366,8 +369,15 @@ public class RecordClient extends Client implements VideoSink, JavaAudioDeviceMo
}
}
public void source(VideoTrack videoTrack) {
this.videoTrack = videoTrack;
public void record(VideoSource videoSource, PeerConnectionFactory peerConnectionFactory) {
if(this.videoTrack != null) {
return;
}
if(videoSource == null || peerConnectionFactory == null) {
Log.e(RecordClient.class.getSimpleName(), "数据采集无效");
return;
}
this.videoTrack = peerConnectionFactory.createVideoTrack("TaoyaoVR", videoSource);
this.videoTrack.setEnabled(true);
this.videoTrack.addSink(this);
}
@@ -380,13 +390,18 @@ public class RecordClient extends Client implements VideoSink, JavaAudioDeviceMo
}
super.close();
Log.i(RecordClient.class.getSimpleName(), "结束录制:" + this.filepath);
this.videoTrack.removeSink(this);
this.videoTrack.dispose();
if(this.videoTrack != null) {
this.videoTrack.removeSink(this);
this.videoTrack.dispose();
this.videoTrack = null;
}
if (this.audioThread != null) {
this.audioThread.quitSafely();
this.audioThread = null;
}
if (this.videoThread != null) {
this.videoThread.quitSafely();
this.videoThread = null;
}
final File file = new File(this.filepath);
if(file.length() <= 0) {

View File

@@ -10,19 +10,19 @@ public class Config {
/**
* 屏幕捕获
*/
public static final int WHAT_SCREEN_CAPTURE = 1000;
public static final int WHAT_SCREEN_CAPTURE = 1000;
/**
* 视频录制
*/
public static final int WHAT_RECORD = 1001;
public static final int WHAT_RECORD = 1001;
/**
* 新建本地音频
*/
public static final int WHAT_NEW_LOCAL_AUDIO = 2000;
public static final int WHAT_NEW_LOCAL_AUDIO = 2000;
/**
* 新建本地视频
*/
public static final int WHAT_NEW_LOCAL_VIDEO = 2001;
public static final int WHAT_NEW_LOCAL_VIDEO = 2001;
/**
* 新建远程音频
*/
@@ -31,13 +31,17 @@ public class Config {
* 新建远程视频
*/
public static final int WHAT_NEW_REMOTE_VIDEO = 2003;
/**
* 移除远程音频
*/
public static final int WHAT_REMOVE_AUDIO = 2998;
/**
* 移除远程视频
*/
public static final int WHAT_REMOVE_VIDEO = 2999;
public static final int WHAT_REMOVE_VIDEO = 2999;
/**
* 默认声音大小
*/
public static final double DEFAULT_VOLUME = 10.0D;
public static final double DEFAULT_VOLUME = 10.0D;
}