[*] 每日优化

This commit is contained in:
acgist
2023-05-12 08:44:01 +08:00
parent 22c6970cbd
commit 825fa907e5
14 changed files with 298 additions and 227 deletions

View File

@@ -30,6 +30,7 @@ getter/setter
## 计划任务
* 开机自启
* 录制底噪
* 分辨率调整
* 降低视频录制大小

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest>
</manifest>

View File

@@ -53,4 +53,11 @@ public final class IdUtils {
IdUtils.clientIndex = clientIndex;
}
/**
* @return 随机INT
*/
public static final int nextInt() {
return (int) (System.nanoTime() % Integer.MAX_VALUE);
}
}

View File

@@ -19,9 +19,10 @@
android:launchMode="singleTask"
android:theme="@style/Theme.Taoyao">
<intent-filter>
<action android:name="android.intent.action.HOME" />
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.DEFAULT" />
<!--
<category android:name="android.intent.category.HOME" />
-->
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

View File

@@ -2,7 +2,6 @@ package com.acgist.taoyao.client;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
@@ -26,14 +25,12 @@ import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import com.acgist.taoyao.boot.utils.IdUtils;
import com.acgist.taoyao.client.databinding.ActivityMainBinding;
import com.acgist.taoyao.media.MediaManager;
import com.acgist.taoyao.media.VideoSourceType;
import com.acgist.taoyao.media.config.Config;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import org.apache.commons.lang3.RandomUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
@@ -129,7 +126,7 @@ public class MainActivity extends AppCompatActivity {
if (Stream.of(permissions).map(this.getApplicationContext()::checkSelfPermission).allMatch(v -> v == PackageManager.PERMISSION_GRANTED)) {
Log.i(MediaService.class.getSimpleName(), "授权成功");
} else {
ActivityCompat.requestPermissions(this, permissions, RandomUtils.nextInt());
ActivityCompat.requestPermissions(this, permissions, IdUtils.nextInt());
}
}
@@ -142,28 +139,10 @@ public class MainActivity extends AppCompatActivity {
}
Log.i(MainActivity.class.getSimpleName(), "拉起媒体服务");
this.mainHandler = new MainHandler();
final Context context = this.getApplicationContext();
final Resources resources = this.getResources();
final MediaManager mediaManager = MediaManager.getInstance();
mediaManager.initContext(
this.mainHandler, context,
resources.getInteger(R.integer.imageQuantity),
resources.getString(R.string.audioQuantity),
resources.getString(R.string.videoQuantity),
resources.getInteger(R.integer.channelCount),
resources.getInteger(R.integer.iFrameInterval),
resources.getString(R.string.storagePathImage),
resources.getString(R.string.storagePathVideo),
resources.getString(R.string.watermark),
VideoSourceType.valueOf(resources.getString(R.string.videoSourceType))
);
if(resources.getBoolean(R.bool.broadcaster)) {
mediaManager.initTTS(context);
}
// 注意不能使用intent传递
MediaService.mainHandler = this.mainHandler;
MediaService.setMainHandler(this.mainHandler);
final Intent intent = new Intent(this, MediaService.class);
intent.setAction(MediaService.Action.CONNECT.name());
intent.setAction(MediaService.Action.LAUNCH.name());
this.startService(intent);
}
@@ -181,7 +160,7 @@ public class MainActivity extends AppCompatActivity {
if (result.getResultCode() == Activity.RESULT_OK) {
Log.i(MediaManager.class.getSimpleName(), "屏幕捕获成功");
final Intent intent = new Intent(this, MediaService.class);
intent.setAction(MediaService.Action.SCREEN_RECORD.name());
intent.setAction(MediaService.Action.SCREEN_CAPTURE.name());
intent.putExtra("data", result.getData());
intent.putExtra("code", result.getResultCode());
this.startService(intent);

View File

@@ -10,6 +10,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.BitmapFactory;
import android.media.AudioManager;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
@@ -19,8 +20,10 @@ import android.widget.Toast;
import androidx.core.app.NotificationCompat;
import com.acgist.taoyao.boot.utils.IdUtils;
import com.acgist.taoyao.client.signal.Taoyao;
import com.acgist.taoyao.media.MediaManager;
import com.acgist.taoyao.media.VideoSourceType;
import com.acgist.taoyao.media.signal.ITaoyaoListener;
import java.io.File;
@@ -35,6 +38,7 @@ import java.nio.file.Paths;
public class MediaService extends Service {
static {
Log.i(MediaService.class.getSimpleName(), "加载C++库文件");
System.loadLibrary("taoyao");
System.loadLibrary("jingle_peerconnection_so");
}
@@ -46,20 +50,28 @@ public class MediaService extends Service {
*/
public enum Action {
// 启动
/**
* 系统启动
*/
BOOT,
/**
* 连接信令
*/
LAUNCH,
// 连接
CONNECT,
// 重连
/**
* 重连信令
*/
RECONNECT,
// 屏幕录制
SCREEN_RECORD;
/**
* 屏幕录制
*/
SCREEN_CAPTURE;
}
public static Handler mainHandler;
private Taoyao taoyao;
private static Handler mainHandler;
private static final String TAOYAO = "TAOYAO";
private final ITaoyaoListener taoyaoListener = new ITaoyaoListener() {
};
@@ -79,6 +91,7 @@ public class MediaService extends Service {
final Resources resources = this.getResources();
this.mkdir(resources.getString(R.string.storagePathImage), Environment.DIRECTORY_PICTURES);
this.mkdir(resources.getString(R.string.storagePathVideo), Environment.DIRECTORY_MOVIES);
this.settingAudio();
this.buildNotificationChannel();
}
@@ -91,16 +104,16 @@ 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);
} else if (Action.CONNECT.name().equals(intent.getAction())) {
this.openConnect(intent);
if (Action.BOOT.name().equals(intent.getAction())) {
this.boot();
this.openConnect();
} else if (Action.LAUNCH.name().equals(intent.getAction())) {
this.launch();
this.openConnect();
} else if (Action.RECONNECT.name().equals(intent.getAction())) {
this.reconnect();
} else if (Action.SCREEN_RECORD.name().equals(intent.getAction())) {
this.screenRecord(intent);
} else if (Action.SCREEN_CAPTURE.name().equals(intent.getAction())) {
this.screenCapture(intent);
} else {
Log.w(MediaService.class.getSimpleName(), "未知动作:" + intent.getAction());
}
@@ -114,20 +127,48 @@ public class MediaService extends Service {
this.close();
}
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, "TAOYAO")
/**
* 启动
*/
private void boot() {
final Intent activityIntent = new Intent(this, MainActivity.class);
final PendingIntent pendingIntent = PendingIntent.getActivity(this, IdUtils.nextInt(), activityIntent, PendingIntent.FLAG_CANCEL_CURRENT);
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))
.setContentTitle("桃夭后台")
.setContentText("桃夭正在后台运行")
.setContentIntent(pendingIntent);
final Notification notification = notificationBuilder.build();
this.startForeground((int) System.currentTimeMillis(), notification);
this.startForeground(IdUtils.nextInt(), notification);
}
private void openConnect(Intent intent) {
/**
* 启动
*/
private void launch() {
final Context context = this.getApplicationContext();
final Resources resources = this.getResources();
final MediaManager mediaManager = MediaManager.getInstance();
mediaManager.initContext(
MediaService.mainHandler, context,
resources.getInteger(R.integer.imageQuantity),
resources.getString(R.string.audioQuantity),
resources.getString(R.string.videoQuantity),
resources.getInteger(R.integer.channelCount),
resources.getInteger(R.integer.iFrameInterval),
resources.getString(R.string.storagePathImage),
resources.getString(R.string.storagePathVideo),
resources.getBoolean(R.bool.broadcaster),
resources.getString(R.string.watermark),
VideoSourceType.valueOf(resources.getString(R.string.videoSourceType))
);
}
/**
* 连接信令
*/
private void openConnect() {
if (this.taoyao == null) {
Log.d(MediaService.class.getSimpleName(), "打开信令连接");
this.connect();
@@ -136,6 +177,9 @@ public class MediaService extends Service {
}
}
/**
* 重连信令
*/
private void reconnect() {
Log.d(MediaService.class.getSimpleName(), "重新连接信令");
this.connect();
@@ -153,9 +197,8 @@ public class MediaService extends Service {
final String clientId = sharedPreferences.getString("settings.clientId", "mobile");
final String username = sharedPreferences.getString("settings.username", "taoyao");
final String password = sharedPreferences.getString("settings.password", "taoyao");
final Resources resources = this.getResources();
// 系统服务
final Context context = this.getApplicationContext();
final Resources resources = this.getResources();
this.close();
// 连接信令
this.taoyao = new Taoyao(
@@ -165,9 +208,31 @@ public class MediaService extends Service {
this.mainHandler, context, this.taoyaoListener
);
MediaManager.getInstance().initTaoyao(this.taoyao);
Toast.makeText(this.getApplicationContext(), "连接信令", Toast.LENGTH_SHORT).show();
Toast.makeText(context, "连接信令", Toast.LENGTH_SHORT).show();
}
/**
* 屏幕录制
*
* @param intent Intent
*/
public void screenCapture(Intent intent) {
final Intent activityIntent = new Intent(this, MainActivity.class);
final PendingIntent pendingIntent = PendingIntent.getActivity(this, IdUtils.nextInt(), activityIntent, PendingIntent.FLAG_CANCEL_CURRENT);
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))
.setContentTitle("录制屏幕")
.setContentText("桃夭正在录制屏幕")
.setContentIntent(pendingIntent);
final Notification notification = notificationBuilder.build();
this.startForeground((int) System.currentTimeMillis(), notification);
MediaManager.getInstance().initScreen(intent.getParcelableExtra("data"));
}
/**
* 关闭连接
*/
private synchronized void close() {
if (this.taoyao == null) {
return;
@@ -177,22 +242,12 @@ public class MediaService extends Service {
this.taoyao = null;
}
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, "TAOYAO")
.setSmallIcon(R.mipmap.ic_launcher_foreground)
.setLargeIcon(BitmapFactory.decodeResource(this.getResources(), R.mipmap.ic_launcher_foreground))
// 自动清除
// .setAutoCancel(true)
.setContentTitle("录制屏幕")
.setContentText("桃夭正在录制屏幕")
.setContentIntent(pendingIntent);
final Notification notification = notificationBuilder.build();
this.startForeground((int) System.currentTimeMillis(), notification);
MediaManager.getInstance().initScreen(intent.getParcelableExtra("data"));
}
/**
* 创建目录
*
* @param path 路径
* @param type 类型
*/
private void mkdir(String path, String type) {
final Path imagePath = Paths.get(
Environment.getExternalStoragePublicDirectory(type).getAbsolutePath(),
@@ -201,23 +256,42 @@ public class MediaService extends Service {
final File file = imagePath.toFile();
if(file.exists()) {
Log.d(MediaService.class.getSimpleName(), "目录已经存在:" + imagePath);
} else if(file.mkdirs()) {
Log.d(MediaService.class.getSimpleName(), "新建目录成功:" + imagePath);
} else {
Log.d(MediaService.class.getSimpleName(), "新建文件目录:" + imagePath);
file.mkdirs();
Log.d(MediaService.class.getSimpleName(), "新建目录失败" + imagePath);
}
}
/**
* 设置音频
*/
private void settingAudio() {
final AudioManager audioManager = this.getApplicationContext().getSystemService(AudioManager.class);
Log.i(MediaService.class.getSimpleName(), "当前音频模式:" + audioManager.getMode());
Log.i(MediaService.class.getSimpleName(), "当前音频音量:" + audioManager.getStreamVolume(audioManager.getMode()));
// Log.i(MediaService.class.getSimpleName(), "当前蓝牙是否打开:" + audioManager.isBluetoothScoOn());
// Log.i(MediaService.class.getSimpleName(), "当前耳机是否打开:" + audioManager.isWiredHeadsetOn());
// Log.i(MediaService.class.getSimpleName(), "当前电话扬声器是否打开:" + audioManager.isSpeakerphoneOn());
// audioManager.setStreamVolume(AudioManager.MODE_IN_COMMUNICATION, audioManager.getStreamMaxVolume(AudioManager.MODE_IN_COMMUNICATION), AudioManager.FLAG_PLAY_SOUND);
}
/**
* 新建通知通道
*/
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);
final NotificationChannel notificationChannel = new NotificationChannel(TAOYAO, "桃夭通知", NotificationManager.IMPORTANCE_DEFAULT);
notificationChannel.setShowBadge(false);
notificationChannel.setDescription("桃夭通知");
notificationManager.createNotificationChannel(notificationChannel);
}
private void cleanAllNotification() {
final NotificationManager notificationManager = this.getSystemService(NotificationManager.class);
notificationManager.cancelAll();
/**
* @param mainHandler MainHandler
*/
public static final void setMainHandler(Handler mainHandler) {
MediaService.mainHandler = mainHandler;
}
}

View File

@@ -87,12 +87,13 @@ public class SettingsActivity extends AppCompatActivity {
editor.putString("settings.username", username);
editor.putString("settings.password", password);
editor.commit();
// 重连
final Intent intent = new Intent(this, MediaService.class);
intent.setAction(MediaService.Action.RECONNECT.name());
this.startService(intent);
// 返回预览页面
this.startActivity(new Intent(this, MainActivity.class));
// 重连信令
final Intent serviceIntent = new Intent(this, MediaService.class);
serviceIntent.setAction(MediaService.Action.RECONNECT.name());
this.startService(serviceIntent);
// 预览页面
final Intent activityIntent = new Intent(this, MainActivity.class);
this.startActivity(activityIntent);
// 结束
this.finish();
}

View File

@@ -16,7 +16,7 @@ public class TaoyaoReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
Log.i(TaoyaoReceiver.class.getSimpleName(), "onReceive" + intent.getAction());
if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
this.launchPreview(context);
this.bootTaoyao(context);
} else {
}
}
@@ -26,11 +26,11 @@ public class TaoyaoReceiver extends BroadcastReceiver {
*
* @param context 上下文
*/
private void launchPreview(Context context) {
final Intent intent = new Intent(context, MediaService.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(MediaService.Action.LAUNCH.name());
context.startForegroundService(intent);
private void bootTaoyao(Context context) {
final Intent serviceIntent = new Intent(context, MediaService.class);
serviceIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
serviceIntent.setAction(MediaService.Action.BOOT.name());
context.startForegroundService(serviceIntent);
}
}

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<resources>
<style name="Theme.Taoyao" parent="Theme.MaterialComponents.Light.DarkActionBar">
<item name="colorPrimary">@color/purple_500</item>

View File

@@ -210,6 +210,7 @@ public final class MediaManager {
return
this.taoyao != null &&
this.context != null &&
this.mainHandler != null &&
this.mediaProperties != null;
}
@@ -244,6 +245,7 @@ public final class MediaManager {
* @param iFrameInterval 关键帧频率
* @param imagePath 图片保存路径
* @param videoPath 视频保存路径
* @param tts 是否加载TTS
* @param watermark 水印信息
* @param videoSourceType 视频来源类型
*/
@@ -251,7 +253,7 @@ public final class MediaManager {
Handler mainHandler, Context context,
int imageQuantity, String audioQuantity, String videoQuantity,
int channelCount, int iFrameInterval,
String imagePath, String videoPath,
String imagePath, String videoPath, boolean tts,
String watermark, VideoSourceType videoSourceType
) {
this.mainHandler = mainHandler;
@@ -265,6 +267,9 @@ public final class MediaManager {
this.videoPath = videoPath;
this.watermark = watermark;
this.videoSourceType = videoSourceType;
if(tts) {
this.initTTS(context);
}
}
/**
@@ -274,7 +279,7 @@ public final class MediaManager {
this.taoyao = taoyao;
}
public void initTTS(Context context) {
private void initTTS(Context context) {
if(this.textToSpeech != null) {
return;
}

View File

@@ -1,5 +1,8 @@
/**
* 音频默认配置
* TODOMediaStreamTrack.applyConstraints()
* 播放音量、采集音量
* https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackSettings
*/
const defaultAudioConfig = {
// 设备