Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c982a66425 | ||
|
|
91265b9b23 | ||
|
|
01f7d87141 | ||
|
|
571aec369d | ||
|
|
3b7b03b1c9 | ||
|
|
07de78ce9c | ||
|
|
32a3a1053e | ||
|
|
a5ccc17ecb | ||
|
|
ed6855feef | ||
|
|
d7ee0ee99b | ||
|
|
936f68c714 | ||
|
|
1ab550b002 | ||
|
|
cd74941a36 | ||
|
|
306b60887a | ||
|
|
ebe8a62fed | ||
|
|
8a850c2655 | ||
|
|
402318a080 | ||
|
|
0f165a78c4 | ||
|
|
f4ad326ecf | ||
|
|
d4dab26a05 | ||
|
|
a3644cbb13 | ||
|
|
fb4f64c20a |
3
.github/workflows/build.yml
vendored
3
.github/workflows/build.yml
vendored
@@ -86,8 +86,9 @@ jobs:
|
||||
- name: Checkout libmediasoupclient
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: acgist/libmediasoupclient
|
||||
repository: versatica/libmediasoupclient
|
||||
path: ./taoyao-client-android/taoyao/media/deps/libmediasoupclient
|
||||
ref: '97c42457b6a73a3eeef45dc37f1c90bb7d28cdff'
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
|
||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -1,9 +1,9 @@
|
||||
[submodule "taoyao-client-media/mediasoup"]
|
||||
path = taoyao-client-media/mediasoup
|
||||
url = https://gitee.com/acgist/mediasoup.git
|
||||
url = https://github.com/versatica/mediasoup.git
|
||||
[submodule "taoyao-client-web/mediasoup-client"]
|
||||
path = taoyao-client-web/mediasoup-client
|
||||
url = https://gitee.com/acgist/mediasoup-client.git
|
||||
url = https://github.com/versatica/mediasoup-client.git
|
||||
[submodule "taoyao-client-android/libmediasoupclient"]
|
||||
path = taoyao-client-android/libmediasoupclient
|
||||
url = https://gitee.com/acgist/libmediasoupclient.git
|
||||
url = https://github.com/versatica/libmediasoupclient.git
|
||||
|
||||
@@ -10,14 +10,14 @@ Maven >= 3.8.0
|
||||
CMake >= 3.26.0
|
||||
NodeJS >= v18.16.0
|
||||
Python >= 3.8.0 with PIP
|
||||
ffmpeg >= 4.3.0
|
||||
gcc/g++ >= 10.2.0
|
||||
FFmpeg >= 4.3.0
|
||||
GCC/G++ >= 10.2.0
|
||||
Android >= 9.0
|
||||
```
|
||||
|
||||
## Debian
|
||||
|
||||
`CentOS 7`实在是太旧了,软件更新非常麻烦,所以直接使用`Debian`作为测试,系统配置全部使用`root`用户。
|
||||
`CentOS 7`实在是太旧了,软件更新非常麻烦,所以直接使用`Debian`作为测试。
|
||||
|
||||
### 系统参数
|
||||
|
||||
@@ -66,6 +66,7 @@ set nocompatible
|
||||
vi /etc/network/interfaces
|
||||
|
||||
---
|
||||
auto enp0s3
|
||||
iface enp0s3 inet static
|
||||
address 192.168.1.110
|
||||
gateway 192.168.1.1
|
||||
@@ -80,6 +81,17 @@ ifup enp0s3
|
||||
### 设置国内镜像
|
||||
|
||||
```
|
||||
# DNS
|
||||
sudo vim /etc/systemd/resolved.conf
|
||||
|
||||
---
|
||||
DNS=233.5.5.5 233.6.6.6 114.114.114.114 8.8.8.8
|
||||
---
|
||||
|
||||
sudo systemctl restart systemd-resolved
|
||||
sudo systemctl enable systemd-resolved
|
||||
sudo ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf
|
||||
|
||||
# 配置
|
||||
vi /etc/apt/sources.list
|
||||
|
||||
@@ -96,6 +108,7 @@ deb-src https://mirrors.aliyun.com/debian/ bullseye-backports main non-free cont
|
||||
|
||||
# 更新系统
|
||||
apt update
|
||||
apt upgrade
|
||||
```
|
||||
|
||||
### 安装依赖
|
||||
@@ -177,7 +190,7 @@ sudo apt install git
|
||||
git --version
|
||||
```
|
||||
|
||||
## 安装gcc/g++
|
||||
## 安装GCC/G++
|
||||
|
||||
```
|
||||
# 安装
|
||||
@@ -335,7 +348,7 @@ trusted-host = mirrors.aliyun.com
|
||||
pip config list
|
||||
```
|
||||
|
||||
## 安装ffmpeg
|
||||
## 安装FFmpeg
|
||||
|
||||
```
|
||||
mkdir -p /data/dev/ffmpeg ; cd $_
|
||||
@@ -395,11 +408,11 @@ wget http://www.ffmpeg.org/releases/ffmpeg-5.1.3.tar.xz
|
||||
tar -Jxvf ffmpeg-5.1.3.tar.xz
|
||||
cd ffmpeg-5.1.3/
|
||||
PKG_CONFIG_PATH="/usr/local/lib/pkgconfig/"
|
||||
./configure \
|
||||
--enable-static \
|
||||
--enable-shared \
|
||||
--enable-gpl \
|
||||
--enable-libvpx \
|
||||
./configure \
|
||||
--enable-static \
|
||||
--enable-shared \
|
||||
--enable-gpl \
|
||||
--enable-libvpx \
|
||||
--enable-libopus \
|
||||
--enable-libx264 \
|
||||
--enable-libx265 \
|
||||
@@ -407,6 +420,15 @@ PKG_CONFIG_PATH="/usr/local/lib/pkgconfig/"
|
||||
--enable-encoder=libvpx_vp9 --enable-decoder=vp9 --enable-parser=vp9
|
||||
make && sudo make install
|
||||
|
||||
# 链接文件
|
||||
vim /etc/ld.so.conf
|
||||
|
||||
---
|
||||
/usr/local/lib/
|
||||
---
|
||||
|
||||
ldconfig
|
||||
|
||||
# 验证
|
||||
ffmpeg -version
|
||||
ffmpeg -decoders
|
||||
@@ -488,7 +510,7 @@ pm2 start|stop|restart taoyao-client-media
|
||||
|
||||
> 下载依赖建议备份方便再次编译使用
|
||||
|
||||
### Mediasoup单独编译
|
||||
### Mediasoup单独编译(旧版)
|
||||
|
||||
编译媒体服务时会自动编译`mediasoup`所以忽略单独编译
|
||||
|
||||
@@ -502,6 +524,16 @@ make
|
||||
make clean
|
||||
```
|
||||
|
||||
### Mediasoup单独编译(新版)
|
||||
|
||||
* 需要`python3`和`pip3`
|
||||
* 源码[mediasoup-3.13.16.zip](https://pan.baidu.com/s/1E_DXv32D9ODyj5J-o-ji_g?pwd=hudc)(包含依赖)
|
||||
|
||||
```
|
||||
npm install
|
||||
node npm-scripts.mjs worker:build
|
||||
```
|
||||
|
||||
## 安装Web终端
|
||||
|
||||
`Nginx`和`PM2`选择一种启动即可
|
||||
@@ -625,7 +657,7 @@ openssl pkcs12 -export -clcerts -in server.crt -inkey server.key -out server.p12
|
||||
# 设置密码:-deststorepass 123456
|
||||
```
|
||||
|
||||
## gcc/g++路径配置
|
||||
## GCC/G++路径配置
|
||||
|
||||
```
|
||||
# 安装路径
|
||||
@@ -645,11 +677,11 @@ openssl pkcs12 -export -clcerts -in server.crt -inkey server.key -out server.p12
|
||||
## 清理源码
|
||||
|
||||
```
|
||||
sudo rm -rf \
|
||||
/data/dev/cmake \
|
||||
sudo rm -rf \
|
||||
/data/dev/cmake \
|
||||
/data/dev/ffmpeg \
|
||||
/data/dev/python \
|
||||
/data/dev/maven/apache-maven-3.8.8-bin.tar.gz \
|
||||
/data/dev/maven/apache-maven-3.8.8-bin.tar.gz \
|
||||
/data/dev/nodejs/node-v18.16.0-linux-x64.tar.xz \
|
||||
/data/dev/java/openjdk-17.0.2_linux-x64_bin.tar.gz
|
||||
```
|
||||
|
||||
@@ -37,6 +37,12 @@
|
||||
* WebRtcAudioRecord
|
||||
* WebRtcAudioTrack
|
||||
|
||||
## 动态修改码率
|
||||
|
||||
```
|
||||
HardwareVideoEncoder#updateBitrate
|
||||
```
|
||||
|
||||
## 学习资料
|
||||
|
||||
* https://developer.android.google.cn/docs?hl=zh-cn
|
||||
|
||||
5
taoyao-client-android/taoyao/.gitignore
vendored
5
taoyao-client-android/taoyao/.gitignore
vendored
@@ -2,6 +2,9 @@
|
||||
.idea
|
||||
.gradle
|
||||
|
||||
media/deps
|
||||
media/deps/webrtc
|
||||
media/deps/rnnoise/src
|
||||
media/deps/rnnoise/include
|
||||
media/deps/libmediasoupclient
|
||||
|
||||
local.properties
|
||||
|
||||
@@ -2,20 +2,14 @@ cmake_minimum_required(VERSION 3.22.1)
|
||||
|
||||
project(taoyao VERSION 1.0.0 LANGUAGES C CXX)
|
||||
|
||||
# Debug | Release
|
||||
#-DCMAKE_BUILD_TYPE=Debug
|
||||
#set(CMAKE_BUILD_TYPE Debug)
|
||||
|
||||
# C编译选项
|
||||
set(CMAKE_C_STANDARD 17)
|
||||
#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c17 -O3")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -std=c17 -O0 -g")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -std=c17 -O0 -g")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -std=c17 -O3")
|
||||
|
||||
# C++编译选项
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -O3")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++17 -O0 -g")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++17 -O0 -g")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++17 -O3")
|
||||
|
||||
set(
|
||||
@@ -25,12 +19,9 @@ set(
|
||||
|
||||
set(
|
||||
SOURCE_FILES
|
||||
${SOURCE_DIR}/include/Log.hpp
|
||||
${SOURCE_DIR}/include/MediaManager.hpp
|
||||
${SOURCE_DIR}/include/Room.hpp
|
||||
${SOURCE_DIR}/include/RouterCallback.hpp
|
||||
${SOURCE_DIR}/webrtc/MediaManager.cpp
|
||||
${SOURCE_DIR}/webrtc/Room.cpp
|
||||
${SOURCE_DIR}/webrtc/Rnnoise.cpp
|
||||
${SOURCE_DIR}/webrtc/MediaManager.cpp
|
||||
${SOURCE_DIR}/webrtc/RouterCallback.cpp
|
||||
)
|
||||
|
||||
@@ -50,6 +41,7 @@ endif ()
|
||||
|
||||
add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES})
|
||||
|
||||
add_subdirectory("deps/rnnoise")
|
||||
add_subdirectory("deps/libmediasoupclient")
|
||||
|
||||
set_source_files_properties(
|
||||
@@ -59,6 +51,7 @@ set_source_files_properties(
|
||||
target_include_directories(
|
||||
${PROJECT_NAME} PUBLIC
|
||||
"${SOURCE_DIR}/include"
|
||||
"${PROJECT_SOURCE_DIR}/deps/rnnoise/include"
|
||||
"${PROJECT_SOURCE_DIR}/deps/libmediasoupclient/include"
|
||||
"${PROJECT_SOURCE_DIR}/deps/libmediasoupclient/deps/libsdptransform/include"
|
||||
)
|
||||
@@ -67,6 +60,7 @@ target_link_libraries(
|
||||
${PROJECT_NAME} PUBLIC
|
||||
log
|
||||
android
|
||||
rnnoise
|
||||
OpenSLES
|
||||
mediasoupclient
|
||||
)
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
cmake_minimum_required(VERSION 3.22.1)
|
||||
|
||||
project(rnnoise VERSION 1.0.0 LANGUAGES C)
|
||||
|
||||
set(CMAKE_C_STANDARD 17)
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -std=c17 -O0 -g")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -std=c17 -O3")
|
||||
|
||||
include_directories(include)
|
||||
|
||||
set(
|
||||
SOURCE_FILES
|
||||
src/rnn.c
|
||||
src/nnet.c
|
||||
src/pitch.c
|
||||
src/denoise.c
|
||||
src/celt_lpc.c
|
||||
src/kiss_fft.c
|
||||
src/nnet_default.c
|
||||
src/rnnoise_tables.c
|
||||
src/rnnoise_data.c
|
||||
# src/rnnoise_data_little.c
|
||||
src/parse_lpcnet_weights.c
|
||||
)
|
||||
|
||||
add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES})
|
||||
|
||||
set_source_files_properties(
|
||||
${SOURCE_FILES} PROPERTIES COMPILE_FLAGS -Wall -Wextra -Wpedantic
|
||||
)
|
||||
57
taoyao-client-android/taoyao/media/deps/rnnoise/README.md
Normal file
57
taoyao-client-android/taoyao/media/deps/rnnoise/README.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# RNNOISE模型
|
||||
|
||||
https://github.com/xiph/rnnoise.git
|
||||
|
||||
## FFmpeg
|
||||
|
||||
```
|
||||
ffplay -ar 48000 -ac 1 -f s16le input.pcm
|
||||
ffmpeg.exe -i .\source.wav -ar 48000 -ac 1 -f s16le -c:a pcm_s16le source.raw
|
||||
ffmpeg.exe -i .\source.ts -vn -ar 48000 -ac 1 -f s16le -c:a pcm_s16le source.raw
|
||||
```
|
||||
|
||||
## 环境
|
||||
|
||||
```
|
||||
sudo apt install pip3 python3
|
||||
|
||||
vim ~/.pip/pip.conf
|
||||
|
||||
---
|
||||
[global]
|
||||
index-url=https://pypi.tuna.tsinghua.edu.cn/simple
|
||||
---
|
||||
|
||||
pip3 install tqdm torch
|
||||
```
|
||||
|
||||
## 训练
|
||||
|
||||
训练音频`48000`采样单声道的`PCM`音频数据
|
||||
|
||||
```
|
||||
# 混合数据:mix.pcm
|
||||
# 噪音数据:noise.pcm
|
||||
# 原始数据:speech.pcm
|
||||
|
||||
# 克隆仓库
|
||||
cd /data
|
||||
git clone http://192.168.8.184:9999/dev/hsx/rnnoise.git
|
||||
cd rnnoise
|
||||
|
||||
# 编译代码
|
||||
./autogen.sh
|
||||
./configure
|
||||
make
|
||||
|
||||
# 提取特征
|
||||
./dump_features speech.pcm noise.pcm features.f32 200000
|
||||
./script/dump_features_parallel.sh ./dump_features speech.pcm noise.pcm features.f32 200000 8
|
||||
# 模型训练
|
||||
python3 train_rnnoise.py --gru-size=32 --cond-size=32 --epochs=15 features.f32 ./
|
||||
# 导出权重
|
||||
python3 dump_rnnoise_weights.py --quantize ./checkpoints/rnnoise_1.pth rnnoise_c
|
||||
|
||||
# 验证效果
|
||||
./examples/rnnoise_demo mix.pcm output.pcm
|
||||
```
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "rnnoise.h"
|
||||
|
||||
namespace acgist {
|
||||
|
||||
/**
|
||||
* 降噪配置
|
||||
*/
|
||||
class RnnoiseConfig {
|
||||
|
||||
public:
|
||||
// 采样位深:16
|
||||
int bits = 16;
|
||||
// 数据大小:960
|
||||
int size = 960;
|
||||
// 采样率:48000
|
||||
int rate = 48000;
|
||||
// 降噪数据大小
|
||||
int rnnoiseSize = 480;
|
||||
// 降噪数据
|
||||
float* rnnoiseData = nullptr;
|
||||
// 降噪对象
|
||||
DenoiseState* denoiseState = nullptr;
|
||||
|
||||
public:
|
||||
RnnoiseConfig();
|
||||
virtual ~RnnoiseConfig();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -175,6 +175,14 @@ namespace acgist {
|
||||
* @param env JNIEnv
|
||||
*/
|
||||
void closeRoom(JNIEnv* env);
|
||||
/**
|
||||
* 设置码率
|
||||
*
|
||||
* @param maxFramerate 最大帧率
|
||||
* @param minBitrate 最小码率
|
||||
* @param maxBitrate 最大码率
|
||||
*/
|
||||
void setBitrate(int maxFramerate, int minBitrate, int maxBitrate);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
#include "Rnnoise.hpp"
|
||||
|
||||
#include "jni.h"
|
||||
#include "android/log.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
#ifndef RNNOISE_TAG
|
||||
#define RNNOISE_TAG "rnnoise"
|
||||
#endif
|
||||
|
||||
acgist::RnnoiseConfig::RnnoiseConfig() {
|
||||
}
|
||||
|
||||
acgist::RnnoiseConfig::~RnnoiseConfig() {
|
||||
delete[] this->rnnoiseData;
|
||||
rnnoise_destroy(this->denoiseState);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
Java_com_acgist_taoyao_media_audio_RnnoiseProcesser_Init(
|
||||
JNIEnv* env,
|
||||
jobject processer,
|
||||
jint bits,
|
||||
jint size,
|
||||
jint rate
|
||||
) {
|
||||
__android_log_print(ANDROID_LOG_DEBUG, RNNOISE_TAG, "加载Rnnoise");
|
||||
acgist::RnnoiseConfig* config = new acgist::RnnoiseConfig();
|
||||
config->bits = bits;
|
||||
config->size = size;
|
||||
config->rate = rate;
|
||||
config->rnnoiseSize = size / 2;
|
||||
config->rnnoiseData = new float[config->rnnoiseSize];
|
||||
config->denoiseState = rnnoise_create(NULL);
|
||||
return (jlong) config;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jbyteArray JNICALL
|
||||
Java_com_acgist_taoyao_media_audio_RnnoiseProcesser_Rnnoise(
|
||||
JNIEnv* env,
|
||||
jobject processer,
|
||||
jlong pointer,
|
||||
jbyteArray pcm
|
||||
) {
|
||||
acgist::RnnoiseConfig* config = (acgist::RnnoiseConfig*) pointer;
|
||||
jbyte* srcBytes = env->GetByteArrayElements(pcm, 0);
|
||||
short* srcBuffer = (short*) srcBytes;
|
||||
for (int i = 0; i < config->rnnoiseSize; i++) {
|
||||
config->rnnoiseData[i] = srcBuffer[i];
|
||||
}
|
||||
rnnoise_process_frame(config->denoiseState, config->rnnoiseData, config->rnnoiseData);
|
||||
// 返回值不用释放否则需要手动释放
|
||||
const jbyteArray result = env->NewByteArray(config->size);
|
||||
jbyte dstBytes[config->size];
|
||||
for (int i = 0; i < config->rnnoiseSize; i++) {
|
||||
short v = config->rnnoiseData[i];
|
||||
if(v > std::numeric_limits<short>::max()) {
|
||||
v = std::numeric_limits<short>::max();
|
||||
} else if(v < std::numeric_limits<short>::min()) {
|
||||
v = std::numeric_limits<short>::min();
|
||||
}
|
||||
dstBytes[2 * i] = (int8_t) (v >> 0);
|
||||
dstBytes[2 * i + 1] = (int8_t) (v >> 8);
|
||||
}
|
||||
env->SetByteArrayRegion(result, 0, config->size, dstBytes);
|
||||
env->ReleaseByteArrayElements(pcm, srcBytes, 0);
|
||||
// env->DeleteLocalRef(result);
|
||||
// env->ReleaseByteArrayElements(result, dstBytes, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_acgist_taoyao_media_audio_RnnoiseProcesser_Release(
|
||||
JNIEnv* env,
|
||||
jobject processer,
|
||||
jlong pointer
|
||||
) {
|
||||
__android_log_print(ANDROID_LOG_DEBUG, RNNOISE_TAG, "释放Rnnoise");
|
||||
acgist::RnnoiseConfig* config = (acgist::RnnoiseConfig*) pointer;
|
||||
delete config;
|
||||
}
|
||||
@@ -342,6 +342,8 @@ namespace acgist {
|
||||
}
|
||||
this->factory = factory;
|
||||
this->rtcConfiguration = new webrtc::PeerConnectionInterface::RTCConfiguration(rtcConfiguration);
|
||||
// this->rtcConfiguration->set_cpu_adaptation(false);
|
||||
// this->rtcConfiguration->set_experiment_cpu_load_estimator(false);
|
||||
mediasoupclient::PeerConnection::Options options;
|
||||
options.config = rtcConfiguration;
|
||||
options.factory = factory;
|
||||
@@ -418,7 +420,7 @@ namespace acgist {
|
||||
nlohmann::json codecOptions =
|
||||
{
|
||||
// x-google-start-bitrate
|
||||
{ "videoGoogleStartBitrate", 400 },
|
||||
{ "videoGoogleStartBitrate", 1200 },
|
||||
// x-google-min-bitrate
|
||||
{ "videoGoogleMinBitrate", 800 },
|
||||
// x-google-max-bitrate
|
||||
@@ -552,6 +554,53 @@ namespace acgist {
|
||||
this->closeRoomCallback(env);
|
||||
}
|
||||
|
||||
void Room::setBitrate(int maxFramerate, int minBitrate, int maxBitrate) {
|
||||
// if(
|
||||
// this->sendTransport == nullptr ||
|
||||
// this->sendTransport->sendHandler == nullptr ||
|
||||
// this->sendTransport->sendHandler->pc == nullptr ||
|
||||
// this->sendTransport->sendHandler->pc->pc == nullptr
|
||||
// ) {
|
||||
// return;
|
||||
// }
|
||||
// webrtc::BitrateSettings settings;
|
||||
// settings.min_bitrate_bps = minBitrate;
|
||||
// settings.max_bitrate_bps = maxBitrate;
|
||||
// settings.start_bitrate_bps = minBitrate;
|
||||
// this->sendTransport->sendHandler->pc->pc->SetBitrate(settings);
|
||||
webrtc::RtpSenderInterface* rtpSender;
|
||||
if(
|
||||
this->videoProducer == nullptr ||
|
||||
(rtpSender = this->videoProducer->GetRtpSender()) == nullptr
|
||||
) {
|
||||
return;
|
||||
}
|
||||
webrtc::RtpParameters rtpParameters = rtpSender->GetParameters();
|
||||
auto& encodings = rtpParameters.encodings;
|
||||
for(
|
||||
auto iterator = encodings.begin();
|
||||
iterator != encodings.end();
|
||||
++iterator
|
||||
) {
|
||||
if(maxFramerate > 0) {
|
||||
LOG_I("当前最大帧率:%d - %d", maxFramerate, iterator->max_framerate);
|
||||
iterator->max_framerate = maxFramerate;
|
||||
}
|
||||
if(minBitrate > 0) {
|
||||
LOG_I("当前最小码率:%d - %d", minBitrate, iterator->min_bitrate_bps);
|
||||
iterator->min_bitrate_bps = minBitrate;
|
||||
}
|
||||
if(maxBitrate > 0) {
|
||||
LOG_I("当前最大码率:%d - %d", maxBitrate, iterator->max_bitrate_bps);
|
||||
iterator->max_bitrate_bps = maxBitrate;
|
||||
}
|
||||
// iterator->bitrate_priority = 4.0;
|
||||
// iterator->network_priority = webrtc::Priority::kHigh;
|
||||
// iterator->scale_resolution_down_by = 2;
|
||||
}
|
||||
rtpSender->SetParameters(rtpParameters);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
Java_com_acgist_taoyao_media_client_Room_nativeNewRoom(
|
||||
JNIEnv* env, jobject me,
|
||||
@@ -727,4 +776,13 @@ namespace acgist {
|
||||
env->ReleaseStringUTFChars(jConsumerId, consumerId);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_acgist_taoyao_media_client_Room_nativeSetBitrate(JNIEnv* env, jobject me, jlong nativeRoomPointer, jint maxFramerate, jint minBitrate, jint maxBitrate) {
|
||||
Room* room = (Room*) nativeRoomPointer;
|
||||
if(room == nullptr) {
|
||||
return;
|
||||
}
|
||||
room->setBitrate(maxFramerate, minBitrate, maxBitrate);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
package com.acgist.taoyao.media.audio;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Rnnoise降噪
|
||||
*
|
||||
* 注意:暂时只支持48K采样率,其他采样率需要先重新采样。
|
||||
*
|
||||
* https://github.com/xiph/rnnoise.git
|
||||
*/
|
||||
public class RnnoiseProcesser {
|
||||
|
||||
/**
|
||||
* 声道数据
|
||||
*/
|
||||
private byte[] src;
|
||||
/**
|
||||
* 降噪配置对象指针
|
||||
*/
|
||||
private long pointer;
|
||||
/**
|
||||
* 是否启用
|
||||
*/
|
||||
private final boolean enabled = true;
|
||||
|
||||
private static final int BITS = 16;
|
||||
private static final int RATE = 48000;
|
||||
private static final int SIZE_MONO = 960;
|
||||
private static final int SIZE_STEREO = 1920;
|
||||
private static final int CHANNEL_COUNT = 1;
|
||||
|
||||
/**
|
||||
* @see #init()
|
||||
*/
|
||||
public final void init() {
|
||||
this.init(BITS, SIZE_MONO, RATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #Init(int, int, int)
|
||||
*/
|
||||
public final void init(int bits, int size, int rate) {
|
||||
if(!this.enabled) {
|
||||
return;
|
||||
}
|
||||
if(CHANNEL_COUNT == 1) {
|
||||
this.src = new byte[SIZE_MONO];
|
||||
this.pointer = Init(bits, size, rate);
|
||||
} else {
|
||||
this.src = new byte[SIZE_MONO];
|
||||
this.pointer = Init(bits, size, rate);
|
||||
}
|
||||
Log.i(RnnoiseProcesser.class.getSimpleName(), String.format("配置降噪参数:%d - %d - %d", bits, size, rate));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #rnnoise(int, int, byte[])
|
||||
*/
|
||||
public final byte[] rnnoise(byte[] pcm) {
|
||||
return this.rnnoise(0, pcm.length, pcm);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #Rnnoise(long, byte[])
|
||||
*/
|
||||
public final byte[] rnnoise(final int offset, final int capacity, final byte[] pcm) {
|
||||
if(!this.enabled) {
|
||||
return pcm;
|
||||
}
|
||||
if(this.pointer == 0L) {
|
||||
Log.w(RnnoiseProcesser.class.getSimpleName(), "降噪对象没有初始成功原样返回");
|
||||
return pcm;
|
||||
}
|
||||
if(capacity == SIZE_MONO) {
|
||||
System.arraycopy(pcm, offset, this.src, 0, SIZE_MONO);
|
||||
final byte[] dst = Rnnoise(this.pointer, this.src);
|
||||
System.arraycopy(dst, 0, pcm, offset, SIZE_MONO);
|
||||
return pcm;
|
||||
} else if(capacity == SIZE_STEREO) {
|
||||
// 提取单个声道
|
||||
for (int index = offset, jndex = 0; index < capacity + offset; index += 4, jndex += 2) {
|
||||
this.src[jndex] = pcm[index];
|
||||
this.src[jndex + 1] = pcm[index + 1];
|
||||
}
|
||||
final byte[] dst = Rnnoise(this.pointer, this.src);
|
||||
for (int index = offset, jndex = 0; index < capacity + offset; index += 4, jndex += 2) {
|
||||
pcm[index] = dst[jndex];
|
||||
pcm[index + 1] = dst[jndex + 1];
|
||||
pcm[index + 2] = dst[jndex];
|
||||
pcm[index + 3] = dst[jndex + 1];
|
||||
}
|
||||
return pcm;
|
||||
} else {
|
||||
return pcm;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #Release(long)
|
||||
*/
|
||||
public final void release() {
|
||||
if(!this.enabled) {
|
||||
return;
|
||||
}
|
||||
Log.i(RnnoiseProcesser.class.getSimpleName(), "释放降噪对象");
|
||||
if(this.pointer == 0L) {
|
||||
return;
|
||||
}
|
||||
Release(this.pointer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*
|
||||
* @param bits 采样位深
|
||||
* @param size 数据大小
|
||||
* @param rate 采样率
|
||||
*
|
||||
* @return 降噪配置对象指针
|
||||
*/
|
||||
private native final long Init(int bits, int size, int rate);
|
||||
|
||||
/**
|
||||
* 降噪
|
||||
*
|
||||
* @param pointer 降噪配置对象指针
|
||||
* @param pcm PCM数据
|
||||
*
|
||||
* @return 降噪后的PCM数据
|
||||
*/
|
||||
private native final byte[] Rnnoise(long pointer, byte[] pcm);
|
||||
|
||||
/**
|
||||
* 释放资源
|
||||
*
|
||||
* @param pointer 降噪配置对象指针
|
||||
*/
|
||||
private native final void Release(long pointer);
|
||||
|
||||
}
|
||||
@@ -207,6 +207,7 @@ public class Room extends CloseableClient implements RouterCallback {
|
||||
iceServers = new ArrayList<>();
|
||||
}
|
||||
this.rtcConfiguration = new PeerConnection.RTCConfiguration(iceServers);
|
||||
// this.rtcConfiguration.enableCpuOveruseDetection = true;
|
||||
// 开始协商
|
||||
return this.taoyao.requestFuture(
|
||||
this.taoyao.buildMessage("media::router::rtp::capabilities", "roomId", this.roomId),
|
||||
@@ -288,7 +289,7 @@ public class Room extends CloseableClient implements RouterCallback {
|
||||
"forceTcp", false,
|
||||
"producing", false,
|
||||
"consuming", true,
|
||||
"sctpCapabilities", this.dataProduce ? this.sctpCapabilities : null
|
||||
"sctpCapabilities", this.dataConsume ? this.sctpCapabilities : null
|
||||
),
|
||||
response -> {
|
||||
this.nativeCreateRecvTransport(this.nativeRoomPointer, JSONUtils.toJSON(response.body()));
|
||||
@@ -382,6 +383,19 @@ public class Room extends CloseableClient implements RouterCallback {
|
||||
remoteClient.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态设置帧率码率
|
||||
*
|
||||
* HardwareVideoEncoder#updateBitrate()
|
||||
*
|
||||
* @param maxFramerate 最大帧率
|
||||
* @param minBitrate 最小码率
|
||||
* @param maxBitrate 最大码率
|
||||
*/
|
||||
public void setBitrate(int maxFramerate, int minBitrate, int maxBitrate) {
|
||||
this.nativeSetBitrate(this.nativeRoomPointer, maxFramerate, minBitrate, maxBitrate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
synchronized (this) {
|
||||
@@ -773,4 +787,14 @@ public class Room extends CloseableClient implements RouterCallback {
|
||||
*/
|
||||
private native void nativeMediaConsumerClose(long nativeRoomPointer, String consumerId);
|
||||
|
||||
/**
|
||||
* Mediasoup设置码率
|
||||
*
|
||||
* @param nativeRoomPointer 房间指针
|
||||
* @param maxFramerate 最大帧率
|
||||
* @param minBitrate 最小码率
|
||||
* @param maxBitrate 最大码率
|
||||
*/
|
||||
private native void nativeSetBitrate(long nativeRoomPointer, int maxFramerate, int minBitrate, int maxBitrate);
|
||||
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ import android.os.Process;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import com.acgist.taoyao.media.audio.RnnoiseProcesser;
|
||||
|
||||
import org.webrtc.CalledByNative;
|
||||
import org.webrtc.Logging;
|
||||
import org.webrtc.ThreadUtils;
|
||||
@@ -108,6 +110,7 @@ class WebRtcAudioRecord {
|
||||
private @Nullable SamplesReadyCallback audioSamplesReadyCallback;
|
||||
private final boolean isAcousticEchoCancelerSupported;
|
||||
private final boolean isNoiseSuppressorSupported;
|
||||
private RnnoiseProcesser rnnoiseProcesser;
|
||||
|
||||
/**
|
||||
* 设置录音工具
|
||||
@@ -159,6 +162,8 @@ class WebRtcAudioRecord {
|
||||
if (microphoneMute) {
|
||||
byteBuffer.clear();
|
||||
byteBuffer.put(emptyBytes);
|
||||
} else {
|
||||
WebRtcAudioRecord.this.rnnoiseProcesser.rnnoise(byteBuffer.arrayOffset(), byteBuffer.capacity(), byteBuffer.array());
|
||||
}
|
||||
// It's possible we've been shut down during the read, and stopRecording() tried and
|
||||
// failed to join this thread. To be a bit safer, try to avoid calling any native methods
|
||||
@@ -196,6 +201,7 @@ class WebRtcAudioRecord {
|
||||
try {
|
||||
if (audioRecord != null) {
|
||||
audioRecord.stop();
|
||||
WebRtcAudioRecord.this.rnnoiseProcesser.release();
|
||||
doAudioRecordStateCallback(AUDIO_RECORD_STOP);
|
||||
}
|
||||
} catch (IllegalStateException e) {
|
||||
@@ -408,6 +414,8 @@ class WebRtcAudioRecord {
|
||||
}
|
||||
}
|
||||
try {
|
||||
this.rnnoiseProcesser = new RnnoiseProcesser();
|
||||
this.rnnoiseProcesser.init();
|
||||
audioRecord.startRecording();
|
||||
} catch (IllegalStateException e) {
|
||||
reportWebRtcAudioRecordStartError(AudioRecordStartErrorCode.AUDIO_RECORD_START_EXCEPTION,
|
||||
|
||||
@@ -2329,6 +2329,9 @@ class Taoyao extends RemoteClient {
|
||||
videoSource: this.videoSource
|
||||
},
|
||||
});
|
||||
// let oldParameters = this.videoProducer.rtpSender.getParameters();
|
||||
// oldParameters.encodings[0].maxBitrate = 800000;
|
||||
// this.videoProducer.rtpSender.setParameters(oldParameters);
|
||||
this.callbackTrack(this.clientId, track);
|
||||
if (this.proxy && this.proxy.media) {
|
||||
this.proxy.media(track, this.videoProducer);
|
||||
@@ -2839,7 +2842,7 @@ class Taoyao extends RemoteClient {
|
||||
forceTcp : this.forceTcp,
|
||||
producing : false,
|
||||
consuming : true,
|
||||
sctpCapabilities: this.dataProduce ? this.mediasoupDevice.sctpCapabilities : undefined,
|
||||
sctpCapabilities: this.dataConsume ? this.mediasoupDevice.sctpCapabilities : undefined,
|
||||
}));
|
||||
const {
|
||||
transportId,
|
||||
|
||||
@@ -207,7 +207,7 @@ public final class DateUtils {
|
||||
*
|
||||
* @return 日期字符串
|
||||
*/
|
||||
public static String format(LocalDate localDate, DateStyle format) {
|
||||
public static final String format(LocalDate localDate, DateStyle format) {
|
||||
return localDate != null && format != null ? format.getDateTimeFormatter().format(localDate) : null;
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ public final class DateUtils {
|
||||
*
|
||||
* @return 时间字符串
|
||||
*/
|
||||
public static String format(LocalTime localTime, TimeStyle format) {
|
||||
public static final String format(LocalTime localTime, TimeStyle format) {
|
||||
return localTime != null && format != null ? format.getDateTimeFormatter().format(localTime) : null;
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ public final class DateUtils {
|
||||
*
|
||||
* @return 日期时间字符串
|
||||
*/
|
||||
public static String format(LocalDateTime localDateTime, DateTimeStyle format) {
|
||||
public static final String format(LocalDateTime localDateTime, DateTimeStyle format) {
|
||||
return localDateTime != null && format != null ? format.getDateTimeFormatter().format(localDateTime) : null;
|
||||
}
|
||||
|
||||
|
||||
@@ -102,11 +102,14 @@ public final class ErrorUtils {
|
||||
final MessageCode messageCode = messageCodeException.getMessageCode();
|
||||
status = messageCode.getStatus();
|
||||
message = Message.fail(messageCode, messageCodeException.getMessage());
|
||||
} else if(rootError instanceof Throwable throwable) {
|
||||
} else if(
|
||||
rootError instanceof Throwable rootThrowable &&
|
||||
globalError instanceof Throwable globalThrowable
|
||||
) {
|
||||
// 未知异常:异常转换
|
||||
final MessageCode messageCode = ErrorUtils.messageCode(status, throwable);
|
||||
final MessageCode messageCode = ErrorUtils.messageCode(status, globalThrowable, rootThrowable);
|
||||
status = messageCode.getStatus();
|
||||
message = Message.fail(messageCode, ErrorUtils.message(messageCode, throwable));
|
||||
message = Message.fail(messageCode, ErrorUtils.message(messageCode, rootThrowable));
|
||||
} else {
|
||||
// 没有异常
|
||||
final MessageCode messageCode = MessageCode.of(status);
|
||||
@@ -193,20 +196,35 @@ public final class ErrorUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param status 原始状态编码
|
||||
* @param throwable 异常
|
||||
* @see #messageCode(int, Throwable, Throwable)
|
||||
*/
|
||||
public static final MessageCode messageCode(int status, Throwable throwable) {
|
||||
return ErrorUtils.messageCode(status, throwable, throwable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param status 原始状态
|
||||
* @param globalThrowable 外层异常
|
||||
* @param rootThrowable 原始异常
|
||||
*
|
||||
* @return 状态编码
|
||||
* @return 响应状态
|
||||
*
|
||||
* @see ResponseEntityExceptionHandler
|
||||
* @see DefaultHandlerExceptionResolver
|
||||
*/
|
||||
public static final MessageCode messageCode(int status, Throwable throwable) {
|
||||
final Class<?> clazz = throwable.getClass();
|
||||
public static final MessageCode messageCode(int status, Throwable globalThrowable, Throwable rootThrowable) {
|
||||
if(rootThrowable == null || globalThrowable == null) {
|
||||
return MessageCode.CODE_9999;
|
||||
}
|
||||
final Class<?> rootClazz = rootThrowable.getClass();
|
||||
final Class<?> globalClazz = globalThrowable.getClass();
|
||||
return CODE_MAPPING.entrySet().stream()
|
||||
.filter(entry -> {
|
||||
final Class<?> mappingClazz = entry.getKey();
|
||||
return mappingClazz.equals(clazz) || mappingClazz.isAssignableFrom(clazz);
|
||||
return mappingClazz.equals(globalClazz) ||
|
||||
mappingClazz.isAssignableFrom(globalClazz) ||
|
||||
mappingClazz.equals(rootClazz) ||
|
||||
mappingClazz.isAssignableFrom(rootClazz);
|
||||
})
|
||||
.map(Map.Entry::getValue)
|
||||
.findFirst()
|
||||
@@ -271,7 +289,7 @@ public final class ErrorUtils {
|
||||
if(cause instanceof MessageCodeException) {
|
||||
return cause;
|
||||
}
|
||||
} while(cause != null && (cause = cause.getCause()) != null);
|
||||
} while(cause != null && cause.getCause() != null && (cause = cause.getCause()) != null);
|
||||
// 返回原始异常
|
||||
return t;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user