[+] 控制
This commit is contained in:
@@ -3,8 +3,10 @@
|
||||
## 支持版本
|
||||
|
||||
* SDK 28~32
|
||||
* WebRTC m94
|
||||
* Gradle 7.5
|
||||
* Andoird 9~12
|
||||
* libmediasoupclient 3.4.0
|
||||
|
||||
## C++终端
|
||||
|
||||
|
||||
3
taoyao-client-android/taoyao/.gitignore
vendored
3
taoyao-client-android/taoyao/.gitignore
vendored
@@ -2,6 +2,7 @@
|
||||
.idea
|
||||
.gradle
|
||||
|
||||
media/deps
|
||||
media/deps/webrtc
|
||||
media/deps/libmediasoupclient
|
||||
|
||||
local.properties
|
||||
|
||||
17
taoyao-client-android/taoyao/media/deps/README.md
Normal file
17
taoyao-client-android/taoyao/media/deps/README.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# 依赖
|
||||
|
||||
[WebRTC](https://pan.baidu.com/s/1E_DXv32D9ODyj5J-o-ji_g?pwd=hudc)
|
||||
|
||||
下载文件放到响应目录
|
||||
|
||||
```
|
||||
deps
|
||||
├─libmediasoupclient
|
||||
└─webrtc
|
||||
├─lib
|
||||
│ ├─arm64-v8a
|
||||
│ ├─armeabi-v7a
|
||||
│ ├─x86
|
||||
│ └─x86_64
|
||||
└─src
|
||||
```
|
||||
@@ -3,6 +3,8 @@
|
||||
## 支持版本
|
||||
|
||||
* SDK 11
|
||||
* WebRTC m114
|
||||
* libmediasoupclient m120
|
||||
|
||||
## C++终端
|
||||
|
||||
|
||||
4
taoyao-client-openharmony/taoyao/.gitignore
vendored
4
taoyao-client-openharmony/taoyao/.gitignore
vendored
@@ -8,7 +8,9 @@
|
||||
local.properties
|
||||
oh-package-lock.json5
|
||||
|
||||
deps
|
||||
build
|
||||
oh_modules
|
||||
node_modules
|
||||
|
||||
media/src/main/cpp/deps/webrtc
|
||||
media/src/main/cpp/deps/libmediasoupclient
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"path": "./src/main/cpp/CMakeLists.txt",
|
||||
"cppFlags" : "",
|
||||
"arguments" : "",
|
||||
// 不要自作多情添加一些配置🤡🤡🤡🤡
|
||||
// "cppFlags" : "-D_LIBCPP_STD_VER=17",
|
||||
// "arguments" : "-DOHOS_STL=c++_static",
|
||||
// "arguments" : "-DOHOS_STL=c++_shared",
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"main" : "",
|
||||
"name" : "media",
|
||||
"author": "acgist",
|
||||
"main" : "",
|
||||
"name" : "media",
|
||||
"author" : "acgist",
|
||||
"version": "1.0.0",
|
||||
"license": "Apache-2.0",
|
||||
"description": "桃夭媒体终端",
|
||||
"dependencies" : {
|
||||
"description" : "桃夭媒体终端",
|
||||
"dependencies": {
|
||||
"libtaoyao.so": "file:./src/main/cpp/types/libtaoyao"
|
||||
},
|
||||
"devDependencies" : {},
|
||||
|
||||
@@ -20,10 +20,41 @@
|
||||
#include <multimedia/player_framework/native_avcapability.h>
|
||||
#include <multimedia/player_framework/native_avcodec_base.h>
|
||||
|
||||
static std::mutex taoyaoMutex;
|
||||
static std::mutex roomMutex;
|
||||
|
||||
#ifndef TAOYAO_JSON_SIZE
|
||||
#define TAOYAO_JSON_SIZE 2048
|
||||
#endif
|
||||
|
||||
#ifndef TAOYAO_JSON_BODY
|
||||
#define TAOYAO_JSON_BODY() \
|
||||
napi_value ret; \
|
||||
size_t argc = 1; \
|
||||
napi_value args[1] = {nullptr}; \
|
||||
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); \
|
||||
size_t length; \
|
||||
char chars[2048]; \
|
||||
napi_get_value_string_utf8(env, args[0], chars, sizeof(chars), &length); \
|
||||
nlohmann::json json = nlohmann::json::parse(chars); \
|
||||
nlohmann::json body = json["body"];
|
||||
#endif
|
||||
|
||||
#ifndef TAOYAO_ROOM_CHECK
|
||||
#define TAOYAO_ROOM_CHECK(action) \
|
||||
std::string roomId = body["roomId"]; \
|
||||
auto room = acgist::roomMap.find(roomId); \
|
||||
if(room == acgist::roomMap.end()) { \
|
||||
OH_LOG_WARN(LOG_APP, "房间无效:%s %s", #action, roomId.data()); \
|
||||
napi_create_int32(env, -1, &ret); \
|
||||
return ret; \
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace acgist {
|
||||
|
||||
// 终端ID
|
||||
static std::string clientId = "";
|
||||
// JS环境
|
||||
static napi_env env = nullptr;
|
||||
// 是否加载
|
||||
@@ -55,48 +86,60 @@ static void printSupportCodec() {
|
||||
/**
|
||||
* 加载系统
|
||||
*/
|
||||
static void init() {
|
||||
if(initTaoyao) {
|
||||
return;
|
||||
static void init(napi_env env, napi_callback_info info) {
|
||||
{
|
||||
std::lock_guard<std::mutex> taoyaoLock(taoyaoMutex);
|
||||
if(initTaoyao) {
|
||||
return;
|
||||
}
|
||||
initTaoyao = true;
|
||||
}
|
||||
initTaoyao = true;
|
||||
TAOYAO_JSON_BODY();
|
||||
acgist::clientId = json["clientId"];
|
||||
OH_LOG_INFO(LOG_APP, "加载libtaoyao");
|
||||
// 编码能力
|
||||
printSupportCodec();
|
||||
std::string version = mediasoupclient::Version();
|
||||
OH_LOG_INFO(LOG_APP, "加载MediasoupClient:%s", version.data());
|
||||
mediasoupclient::Initialize();
|
||||
OH_LOG_INFO(LOG_APP, "加载媒体功能");
|
||||
mediaManager = new MediaManager();
|
||||
mediaManager->initPeerConnectionFactory();
|
||||
mediaManager->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 卸载系统
|
||||
*/
|
||||
static napi_value shutdown(napi_env env, napi_callback_info info) {
|
||||
if(!initTaoyao) {
|
||||
OH_LOG_INFO(LOG_APP, "已经卸载libtaoyao");
|
||||
return 0;
|
||||
{
|
||||
std::lock_guard<std::mutex> taoyaoLock(taoyaoMutex);
|
||||
if(!initTaoyao) {
|
||||
OH_LOG_INFO(LOG_APP, "已经卸载libtaoyao");
|
||||
return 0;
|
||||
}
|
||||
initTaoyao = false;
|
||||
}
|
||||
initTaoyao = false;
|
||||
OH_LOG_INFO(LOG_APP, "卸载libtaoyao");
|
||||
OH_LOG_INFO(LOG_APP, "释放mediasoupclient");
|
||||
mediasoupclient::Cleanup();
|
||||
// 删除房间
|
||||
for(auto iterator = roomMap.begin(); iterator != roomMap.end(); ++iterator) {
|
||||
OH_LOG_INFO(LOG_APP, "清空房间");
|
||||
for(auto iterator = acgist::roomMap.begin(); iterator != acgist::roomMap.end(); ++iterator) {
|
||||
delete iterator->second;
|
||||
iterator->second = nullptr;
|
||||
}
|
||||
roomMap.clear();
|
||||
// 关闭媒体
|
||||
acgist::roomMap.clear();
|
||||
OH_LOG_INFO(LOG_APP, "关闭媒体");
|
||||
if (mediaManager != nullptr) {
|
||||
delete mediaManager;
|
||||
mediaManager = nullptr;
|
||||
}
|
||||
OH_LOG_INFO(LOG_APP, "释放全局变量");
|
||||
// napi_delete_reference(env, acgist::sendRef);
|
||||
// napi_delete_reference(env, acgist::requestRef);
|
||||
return 0;
|
||||
env = nullptr;
|
||||
// 返回结果
|
||||
napi_value ret;
|
||||
napi_create_int32(env, 0, &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,9 +167,12 @@ std::string request(const std::string& signal, const std::string& body) {
|
||||
napi_create_string_utf8(acgist::env, signal.data(), NAPI_AUTO_LENGTH, &data[0]);
|
||||
napi_create_string_utf8(acgist::env, body.data(), NAPI_AUTO_LENGTH, &data[1]);
|
||||
napi_call_function(acgist::env, nullptr, callback, 2, data, &ret);
|
||||
char chars[2048];
|
||||
char chars[TAOYAO_JSON_SIZE];
|
||||
size_t length;
|
||||
napi_get_value_string_utf8(env, ret, chars, sizeof(chars), &length);
|
||||
// TODO: promise
|
||||
// napi_create_promise
|
||||
// napi_resolve_deferred
|
||||
return chars;
|
||||
}
|
||||
|
||||
@@ -134,18 +180,23 @@ std::string request(const std::string& signal, const std::string& body) {
|
||||
* 房间关闭
|
||||
*/
|
||||
static napi_value roomClose(napi_env env, napi_callback_info info) {
|
||||
TAOYAO_JSON_BODY();
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(roomMutex);
|
||||
auto iterator = roomMap.find("roomId");
|
||||
if(iterator == roomMap.end()) {
|
||||
|
||||
std::lock_guard<std::mutex> roomLock(roomMutex);
|
||||
std::string roomId = body["roomId"];
|
||||
auto iterator = acgist::roomMap.find(roomId);
|
||||
if(iterator == acgist::roomMap.end()) {
|
||||
OH_LOG_WARN(LOG_APP, "关闭房间无效:%s", roomId.data());
|
||||
napi_create_int32(env, -1, &ret);
|
||||
} else {
|
||||
OH_LOG_INFO(LOG_APP, "关闭房间:%s", roomId.data());
|
||||
delete iterator->second;
|
||||
iterator->second = nullptr;
|
||||
roomMap.erase(iterator);
|
||||
acgist::roomMap.erase(iterator);
|
||||
napi_create_int32(env, 0, &ret);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,44 +204,64 @@ static napi_value roomClose(napi_env env, napi_callback_info info) {
|
||||
* 其他终端进入房间,自己进入房间逻辑参考房间邀请。
|
||||
*/
|
||||
static napi_value roomEnter(napi_env env, napi_callback_info info) {
|
||||
return 0;
|
||||
TAOYAO_JSON_BODY();
|
||||
TAOYAO_ROOM_CHECK("进入房间");
|
||||
std::string clientId = body["clientId"];
|
||||
OH_LOG_INFO(LOG_APP, "进入房间:%s %s", roomId.data(), clientId.data());
|
||||
if(clientId == acgist::clientId) {
|
||||
// 忽略关闭自己
|
||||
napi_create_int32(env, -2, &ret);
|
||||
return ret;
|
||||
}
|
||||
nlohmann::json status = body["status"];
|
||||
std::string name = status["name"];
|
||||
int result = room->newRemoteClient(clientId, name);
|
||||
napi_create_int32(env, result, &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 踢出房间
|
||||
* 踢出房间以后终端离开房间
|
||||
*/
|
||||
static napi_value roomExpel(napi_env env, napi_callback_info info) { return 0; }
|
||||
static napi_value roomExpel(napi_env env, napi_callback_info info) {
|
||||
TAOYAO_JSON_BODY();
|
||||
TAOYAO_ROOM_CHECK("踢出房间");
|
||||
OH_LOG_INFO(LOG_APP, "进入房间:%s", roomId.data());
|
||||
nlohmann::json requestBody = {
|
||||
{ "roomId", roomId },
|
||||
};
|
||||
acgist::send("room::leave", requestBody.dump());
|
||||
delete room->second;
|
||||
room->second = nullptr;
|
||||
acgist::roomMap.erase(room);
|
||||
napi_create_int32(env, 0, &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 房间邀请
|
||||
* 邀请终端进入房间
|
||||
*/
|
||||
static napi_value roomInvite(napi_env env, napi_callback_info info) {
|
||||
size_t argc = 1;
|
||||
napi_value args[1] = { nullptr };
|
||||
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
||||
size_t length;
|
||||
char chars[2048];
|
||||
napi_get_value_string_utf8(env, args[0], chars, sizeof(chars), &length);
|
||||
nlohmann::json json = nlohmann::json::parse(chars);
|
||||
nlohmann::json body = json["body"];
|
||||
// TODO: 试试引用
|
||||
std::string roomId = body["roomId"];
|
||||
std::string password = body["password"];
|
||||
napi_value ret;
|
||||
TAOYAO_JSON_BODY();
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(roomMutex);
|
||||
auto iterator = roomMap.find(roomId);
|
||||
if(iterator == roomMap.end()) {
|
||||
std::lock_guard<std::mutex> roomLock(roomMutex);
|
||||
// TODO: 试试引用
|
||||
std::string roomId = body["roomId"];
|
||||
std::string password = body["password"];
|
||||
auto oldRoom = acgist::roomMap.find(roomId);
|
||||
if(oldRoom == acgist::roomMap.end()) {
|
||||
OH_LOG_INFO(LOG_APP, "进入房间:%s", roomId.data());
|
||||
auto room = new acgist::Room(roomId, mediaManager);
|
||||
roomMap[roomId] = room;
|
||||
int enterRet = room->enter(password);
|
||||
if(enterRet == acgist::SUCCESS_CODE) {
|
||||
int result = room->enter(password);
|
||||
if(result == acgist::SUCCESS_CODE) {
|
||||
acgist::roomMap.insert({ roomId, room });
|
||||
room->produceMedia();
|
||||
} else {
|
||||
delete room;
|
||||
}
|
||||
napi_create_int32(env, enterRet, &ret);
|
||||
napi_create_int32(env, result, &ret);
|
||||
} else {
|
||||
OH_LOG_INFO(LOG_APP, "已经进入房间:%s", roomId.data());
|
||||
napi_create_int32(env, -1, &ret);
|
||||
@@ -203,48 +274,137 @@ static napi_value roomInvite(napi_env env, napi_callback_info info) {
|
||||
* 离开房间
|
||||
* 其他终端离开房间
|
||||
*/
|
||||
static napi_value roomLeave(napi_env env, napi_callback_info info) { return 0; }
|
||||
static napi_value roomLeave(napi_env env, napi_callback_info info) {
|
||||
TAOYAO_JSON_BODY();
|
||||
TAOYAO_ROOM_CHECK("离开房间");
|
||||
std::string clientId = body["clientId"];
|
||||
OH_LOG_INFO(LOG_APP, "离开房间:%s %s", roomId.data(), clientId.data());
|
||||
if(clientId == acgist::clientId) {
|
||||
// 忽略关闭自己
|
||||
napi_create_int32(env, -2, &ret);
|
||||
return ret;
|
||||
}
|
||||
int result = room->second->closeRemoteClient(clientId);
|
||||
napi_create_int32(env, result, &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 终端列表
|
||||
* 房间所有终端列表首次进入方便加载终端列表信息
|
||||
*/
|
||||
static napi_value roomClientList(napi_env env, napi_callback_info info) { return 0; }
|
||||
static napi_value roomClientList(napi_env env, napi_callback_info info) {
|
||||
TAOYAO_JSON_BODY();
|
||||
TAOYAO_ROOM_CHECK("终端列表");
|
||||
nlohmann::json clients = body["clients"];
|
||||
if(clients.empty()) {
|
||||
napi_create_int32(env, 0, &ret);
|
||||
return ret;
|
||||
}
|
||||
for(auto client = clients.begin(); client != clients.end(); ++client) {
|
||||
std::string clientId = (*client)["clientId"];
|
||||
std::string name = (*client)["name"];
|
||||
if(clientId == acgist::clientId) {
|
||||
// 忽略关闭自己
|
||||
continue;
|
||||
}
|
||||
room->second->newRemoteClient(clientId, name);
|
||||
}
|
||||
napi_create_int32(env, 0, &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 媒体消费(被动通知)
|
||||
*/
|
||||
static napi_value mediaConsume(napi_env env, napi_callback_info info) { return 0; }
|
||||
static napi_value mediaConsume(napi_env env, napi_callback_info info) {
|
||||
TAOYAO_JSON_BODY();
|
||||
TAOYAO_ROOM_CHECK();
|
||||
TAOYAO_ROOM_CHECK("媒体消费");
|
||||
int result = room->second->newConsumer(body);
|
||||
acgist::send(chars);
|
||||
// acgist::send(json.dump());
|
||||
napi_create_int32(env, result, &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 消费者关闭(被动通知)
|
||||
*/
|
||||
static napi_value mediaConsumerClose(napi_env env, napi_callback_info info) { return 0; }
|
||||
static napi_value mediaConsumerClose(napi_env env, napi_callback_info info) {
|
||||
TAOYAO_JSON_BODY();
|
||||
TAOYAO_ROOM_CHECK("消费者关闭");
|
||||
std::string consumerId = body["consumerId"];
|
||||
TAOYAO_ROOM_CHECK("消费者关闭:%s", consumerId.data());
|
||||
int result = room->second->closeConsumer(consumerId);
|
||||
napi_create_int32(env, result, &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 消费者暂停(被动通知)
|
||||
*/
|
||||
static napi_value mediaConsumerPause(napi_env env, napi_callback_info info) { return 0; }
|
||||
static napi_value mediaConsumerPause(napi_env env, napi_callback_info info) {
|
||||
TAOYAO_JSON_BODY();
|
||||
TAOYAO_ROOM_CHECK("消费者暂停");
|
||||
std::string consumerId = body["consumerId"];
|
||||
TAOYAO_ROOM_CHECK("消费者暂停:%s", consumerId.data());
|
||||
int result = room->second->pauseConsumer(consumerId);
|
||||
napi_create_int32(env, result, &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 消费者恢复(被动通知)
|
||||
*/
|
||||
static napi_value mediaConsumerResume(napi_env env, napi_callback_info info) { return 0; }
|
||||
static napi_value mediaConsumerResume(napi_env env, napi_callback_info info) {
|
||||
TAOYAO_JSON_BODY();
|
||||
TAOYAO_ROOM_CHECK("消费者恢复");
|
||||
std::string consumerId = body["consumerId"];
|
||||
TAOYAO_ROOM_CHECK("消费者恢复:%s", consumerId.data());
|
||||
int result = room->second->resumeConsumer(consumerId);
|
||||
napi_create_int32(env, result, &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生产者关闭(被动通知)
|
||||
*/
|
||||
static napi_value mediaProducerClose(napi_env env, napi_callback_info info) { return 0; }
|
||||
static napi_value mediaProducerClose(napi_env env, napi_callback_info info) {
|
||||
TAOYAO_JSON_BODY();
|
||||
TAOYAO_ROOM_CHECK("生产者关闭");
|
||||
std::string producerId = body["producerId"];
|
||||
TAOYAO_ROOM_CHECK("生产者关闭:%s", producerId.data());
|
||||
int result = room->second->closeProducer(producerId);
|
||||
napi_create_int32(env, result, &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生产者暂停(被动通知)
|
||||
*/
|
||||
static napi_value mediaProducerPause(napi_env env, napi_callback_info info) { return 0; }
|
||||
static napi_value mediaProducerPause(napi_env env, napi_callback_info info) {
|
||||
TAOYAO_JSON_BODY();
|
||||
TAOYAO_ROOM_CHECK("生产者暂停");
|
||||
std::string producerId = body["producerId"];
|
||||
TAOYAO_ROOM_CHECK("生产者暂停:%s", producerId.data());
|
||||
int result = room->second->pauseProducer(producerId);
|
||||
napi_create_int32(env, result, &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生产者恢复(被动通知)
|
||||
*/
|
||||
static napi_value mediaProducerResume(napi_env env, napi_callback_info info) { return 0; }
|
||||
static napi_value mediaProducerResume(napi_env env, napi_callback_info info) {
|
||||
TAOYAO_JSON_BODY();
|
||||
TAOYAO_ROOM_CHECK("生产者恢复");
|
||||
std::string producerId = body["producerId"];
|
||||
TAOYAO_ROOM_CHECK("生产者恢复:%s", producerId.data());
|
||||
int result = room->second->resumeProducer(producerId);
|
||||
napi_create_int32(env, result, &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册发送回调
|
||||
@@ -265,8 +425,6 @@ static napi_value registerRequest(napi_env env, napi_callback_info info) {
|
||||
napi_value args[1] = { nullptr };
|
||||
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
||||
napi_create_reference(env, args[0], 1, &acgist::requestRef);
|
||||
// napi_create_promise
|
||||
// napi_resolve_deferred
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -276,6 +434,7 @@ EXTERN_C_START
|
||||
static napi_value Init(napi_env env, napi_value exports) {
|
||||
acgist::env = env;
|
||||
napi_property_descriptor desc[] = {
|
||||
{ "init", nullptr, acgist::init, nullptr, nullptr, nullptr, napi_default, nullptr },
|
||||
{ "shutdown", nullptr, acgist::shutdown, nullptr, nullptr, nullptr, napi_default, nullptr },
|
||||
{ "roomClose", nullptr, acgist::roomClose, nullptr, nullptr, nullptr, napi_default, nullptr },
|
||||
{ "roomEnter", nullptr, acgist::roomEnter, nullptr, nullptr, nullptr, napi_default, nullptr },
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# 依赖
|
||||
|
||||
[WebRTC](https://pan.baidu.com/s/1E_DXv32D9ODyj5J-o-ji_g?pwd=hudc)
|
||||
|
||||
下载文件放到响应目录
|
||||
|
||||
```
|
||||
deps
|
||||
├─libmediasoupclient
|
||||
└─webrtc
|
||||
├─lib
|
||||
└─src
|
||||
```
|
||||
@@ -12,8 +12,9 @@
|
||||
* https://docs.openharmony.cn/pages/v4.1/zh-cn/application-dev/media/camera/native-camera-recording.md
|
||||
* https://docs.openharmony.cn/pages/v4.1/zh-cn/application-dev/media/audio/using-ohaudio-for-recording.md
|
||||
*/
|
||||
#ifndef taoyao_Capturer_HPP
|
||||
#define taoyao_Capturer_HPP
|
||||
|
||||
#ifndef TAOYAO_CAPTURER_HPP
|
||||
#define TAOYAO_CAPTURER_HPP
|
||||
|
||||
#include <map>
|
||||
|
||||
@@ -172,4 +173,4 @@ public:
|
||||
|
||||
}
|
||||
|
||||
#endif // taoyao_Capturer_HPP
|
||||
#endif // TAOYAO_CAPTURER_HPP
|
||||
|
||||
@@ -3,11 +3,14 @@
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
#ifndef taoyao_Client_HPP
|
||||
#define taoyao_Client_HPP
|
||||
|
||||
#ifndef TAOYAO_CLIENT_HPP
|
||||
#define TAOYAO_CLIENT_HPP
|
||||
|
||||
#include "MediaManager.hpp"
|
||||
|
||||
#include "mediasoupclient.hpp"
|
||||
|
||||
namespace acgist {
|
||||
|
||||
/**
|
||||
@@ -16,6 +19,10 @@ namespace acgist {
|
||||
class Client {
|
||||
|
||||
public:
|
||||
// 终端ID
|
||||
std::string clientId;
|
||||
// 终端名称
|
||||
std::string name;
|
||||
// 媒体管理
|
||||
acgist::MediaManager* mediaManager = nullptr;
|
||||
// 音频轨道
|
||||
@@ -28,12 +35,6 @@ public:
|
||||
virtual ~Client();
|
||||
|
||||
public:
|
||||
/**
|
||||
* 资源释放
|
||||
*
|
||||
* @return 是否成功
|
||||
*/
|
||||
virtual bool release() = 0;
|
||||
|
||||
};
|
||||
|
||||
@@ -47,7 +48,6 @@ public:
|
||||
virtual ~RoomClient();
|
||||
|
||||
public:
|
||||
virtual bool release() override;
|
||||
|
||||
};
|
||||
|
||||
@@ -61,7 +61,6 @@ public:
|
||||
virtual ~LocalClient();
|
||||
|
||||
public:
|
||||
virtual bool release() override;
|
||||
|
||||
};
|
||||
|
||||
@@ -70,15 +69,26 @@ public:
|
||||
*/
|
||||
class RemoteClient : public RoomClient {
|
||||
|
||||
public:
|
||||
// 消费者列表
|
||||
std::map<std::string, mediasoupclient::Consumer*> consumers;
|
||||
|
||||
public:
|
||||
RemoteClient(acgist::MediaManager* mediaManager);
|
||||
virtual ~RemoteClient();
|
||||
|
||||
public:
|
||||
virtual bool release() override;
|
||||
// 添加消费者
|
||||
bool addConsumer(const std::string& consuemrId, mediasoupclient::Consumer* consumer);
|
||||
// 关闭消费者
|
||||
bool closeConsumer(const std::string& consumerId);
|
||||
// 暂停消费者
|
||||
bool pauseConsumer(const std::string& consumerId);
|
||||
// 恢复消费者
|
||||
bool resumeConsumer(const std::string& consumerId);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // taoyao_Client_HPP
|
||||
#endif // TAOYAO_CLIENT_HPP
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
*
|
||||
* https://docs.openharmony.cn/pages/v4.1/zh-cn/application-dev/media/avcodec/obtain-supported-codecs.md
|
||||
*/
|
||||
#ifndef taoyao_MediaManager_HPP
|
||||
#define taoyao_MediaManager_HPP
|
||||
|
||||
#ifndef TAOYAO_MEDIAMANAGER_HPP
|
||||
#define TAOYAO_MEDIAMANAGER_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
@@ -20,30 +21,36 @@
|
||||
namespace acgist {
|
||||
|
||||
class TaoyaoAudioSink : public webrtc::AudioTrackSinkInterface {
|
||||
|
||||
};
|
||||
|
||||
class TaoyaoVideoSource : public webrtc::VideoTrackSourceInterface {
|
||||
|
||||
};
|
||||
|
||||
class TaoyaoVideoSink : public rtc::VideoSinkInterface<webrtc::RecordableEncodedFrame> {
|
||||
|
||||
};
|
||||
|
||||
class MediaManager {
|
||||
|
||||
public:
|
||||
MediaManager();
|
||||
~MediaManager();
|
||||
virtual ~MediaManager();
|
||||
|
||||
public:
|
||||
int localClientRef = 0;
|
||||
std::unique_ptr<rtc::Thread> networkThread = nullptr;
|
||||
std::unique_ptr<rtc::Thread> signalingThread = nullptr;
|
||||
std::unique_ptr<rtc::Thread> workerThread = nullptr;
|
||||
rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> peerConnectionFactory = nullptr;
|
||||
public:
|
||||
|
||||
protected:
|
||||
// 加载PC工厂
|
||||
bool initPeerConnectionFactory();
|
||||
bool newPeerConnectionFactory();
|
||||
// 释放PC工厂
|
||||
bool releasePeerConnectionFactory();
|
||||
|
||||
public:
|
||||
// 加载媒体
|
||||
bool init();
|
||||
// 新增本地终端
|
||||
int newLocalClient();
|
||||
// 释放本地终端
|
||||
@@ -64,8 +71,9 @@ public:
|
||||
rtc::scoped_refptr<webrtc::AudioTrackInterface> getAudioTrack();
|
||||
// 视频来源
|
||||
rtc::scoped_refptr<webrtc::VideoTrackInterface> getVideoTrack();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // taoyao_MediaManager_HPP
|
||||
#endif // TAOYAO_MEDIAMANAGER_HPP
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* https://docs.openharmony.cn/pages/v4.1/zh-cn/application-dev/media/audio/audio-playback-overview.md
|
||||
* https://docs.openharmony.cn/pages/v4.1/zh-cn/application-dev/media/media/using-ndk-avplayer-for-playerback.md
|
||||
*/
|
||||
|
||||
#ifndef TAOYAO_PALYER_HPP
|
||||
#define TAOYAO_PALYER_HPP
|
||||
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
#ifndef taoyao_Room_HPP
|
||||
#define taoyao_Room_HPP
|
||||
|
||||
#ifndef TAOYAO_ROOM_HPP
|
||||
#define TAOYAO_ROOM_HPP
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
@@ -218,7 +219,9 @@ public:
|
||||
acgist::MediaManager* mediaManager = nullptr;
|
||||
// 本地终端
|
||||
acgist::LocalClient* client = nullptr;
|
||||
// 远程终端
|
||||
// ID映射:consumerId = clientId
|
||||
std::map<std::string, std::string> consumerIdClientId;
|
||||
// 远程终端:clientId = RemoteClient
|
||||
std::map<std::string, acgist::RemoteClient*> clients;
|
||||
// WebRTC配置
|
||||
webrtc::PeerConnectionInterface::RTCConfiguration rtcConfiguration;
|
||||
@@ -240,8 +243,10 @@ public:
|
||||
mediasoupclient::Producer::Listener* producerListener = nullptr;
|
||||
// 消费者监听器
|
||||
mediasoupclient::Consumer::Listener* consumerListener = nullptr;
|
||||
// 消费者列表
|
||||
std::map<std::string, mediasoupclient::Consumer*> consumers;
|
||||
// 本地音频
|
||||
rtc::scoped_refptr<webrtc::AudioTrackInterface> audioTrack = nullptr;
|
||||
// 本地视频
|
||||
rtc::scoped_refptr<webrtc::VideoTrackInterface> videoTrack = nullptr;
|
||||
|
||||
public:
|
||||
Room(const std::string& roomId, acgist::MediaManager* mediaManager);
|
||||
@@ -262,19 +267,47 @@ public:
|
||||
* @return 状态
|
||||
*/
|
||||
int produceMedia();
|
||||
// 创建发送通道
|
||||
int createSendTransport();
|
||||
// 创建接收通道
|
||||
int createRecvTransport();
|
||||
// 生产音频
|
||||
int produceAudio();
|
||||
// 生产视频
|
||||
int produceVideo();
|
||||
// 关闭房间
|
||||
int close();
|
||||
int closeConsumer();
|
||||
// 关闭本地消费者
|
||||
int closeClient();
|
||||
// 关闭远程消费者
|
||||
int closeClients();
|
||||
// 关闭音频生产者
|
||||
int closeAudioProducer();
|
||||
// 关闭视频生产者
|
||||
int closeVideoProducer();
|
||||
// 关闭通道
|
||||
int closeTransport();
|
||||
int newRemoteClient();
|
||||
// 新建远程终端
|
||||
int newRemoteClient(const std::string& clientId, const std::string& name);
|
||||
// 删除远程终端
|
||||
int closeRemoteClient(const std::string& clientId);
|
||||
// 新增消费者
|
||||
int newConsumer(nlohmann::json& body);
|
||||
// 删除消费者
|
||||
int closeConsumer(const std::string& consumerId);
|
||||
// 暂停消费者
|
||||
int pauseConsumer(const std::string& consumerId);
|
||||
// 恢复消费者
|
||||
int resumeConsumer(const std::string& consumerId);
|
||||
// 关闭生产者
|
||||
int closeProducer(const std::string& producerId);
|
||||
// 暂停生产者
|
||||
int pauseProducer(const std::string& producerId);
|
||||
// 恢复生产者
|
||||
int resumeProducer(const std::string& producerId);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // taoyao_Room_HPP
|
||||
#endif // TAOYAO_ROOM_HPP
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/**
|
||||
* 信令
|
||||
*/
|
||||
|
||||
#ifndef TAOYAO_SIGNAL_HPP
|
||||
#define TAOYAO_SIGNAL_HPP
|
||||
|
||||
|
||||
@@ -5,10 +5,5 @@ acgist::LocalClient::LocalClient(acgist::MediaManager* mediaManager) : acgist::R
|
||||
}
|
||||
|
||||
acgist::LocalClient::~LocalClient() {
|
||||
this->release();
|
||||
this->mediaManager->releaseLocalClient();
|
||||
}
|
||||
|
||||
bool acgist::LocalClient::release() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include <api/video_codecs/builtin_video_decoder_factory.h>
|
||||
#include <api/video_codecs/builtin_video_encoder_factory.h>
|
||||
|
||||
static std::mutex refMutex;
|
||||
static std::mutex lockMutex;
|
||||
|
||||
acgist::MediaManager::MediaManager() {
|
||||
}
|
||||
@@ -23,7 +23,11 @@ acgist::MediaManager::~MediaManager() {
|
||||
// TODO:验证是否需要释放线程和工厂
|
||||
}
|
||||
|
||||
bool acgist::MediaManager::initPeerConnectionFactory() {
|
||||
bool acgist::MediaManager::init() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool acgist::MediaManager::newPeerConnectionFactory() {
|
||||
OH_LOG_INFO(LOG_APP, "加载PeerConnectionFactory");
|
||||
this->networkThread = rtc::Thread::CreateWithSocketServer();
|
||||
this->signalingThread = rtc::Thread::Create();
|
||||
@@ -33,6 +37,7 @@ bool acgist::MediaManager::initPeerConnectionFactory() {
|
||||
this->workerThread->SetName("worker_thread", nullptr);
|
||||
if (!this->networkThread->Start() || !this->signalingThread->Start() || !this->workerThread->Start()) {
|
||||
OH_LOG_WARN(LOG_APP, "WebRTC线程启动失败");
|
||||
// TODO: 释放线程
|
||||
return false;
|
||||
}
|
||||
this->peerConnectionFactory = webrtc::CreatePeerConnectionFactory(
|
||||
@@ -47,25 +52,43 @@ bool acgist::MediaManager::initPeerConnectionFactory() {
|
||||
nullptr,
|
||||
nullptr,
|
||||
// TODO: 视频工厂
|
||||
// webrtc::CreateBuiltinVideoEncoderFactory(),
|
||||
// webrtc::CreateBuiltinVideoDecoderFactory(),
|
||||
// webrtc::CreateBuiltinVideoEncoderFactory(),
|
||||
// webrtc::CreateBuiltinVideoDecoderFactory(),
|
||||
nullptr /* audio_mixer */,
|
||||
nullptr /* audio_processing */
|
||||
);
|
||||
return this->peerConnectionFactory != nullptr;
|
||||
}
|
||||
|
||||
bool acgist::MediaManager::releasePeerConnectionFactory() {
|
||||
OH_LOG_INFO(LOG_APP, "释放PeerConnectionFactory");
|
||||
// TODO:释放
|
||||
return true;
|
||||
}
|
||||
|
||||
int acgist::MediaManager::newLocalClient() {
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(refMutex);
|
||||
std::lock_guard<std::mutex> mediaLock(lockMutex);
|
||||
this->localClientRef++;
|
||||
if(this->localClientRef > 0) {
|
||||
this->newPeerConnectionFactory();
|
||||
this->startCapture();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int acgist::MediaManager::releaseLocalClient() {
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(refMutex);
|
||||
std::lock_guard<std::mutex> mediaLock(lockMutex);
|
||||
this->localClientRef--;
|
||||
if(this->localClientRef <= 0) {
|
||||
if(this->localClientRef < 0) {
|
||||
this->localClientRef = 0;
|
||||
} else {
|
||||
this->stopCapture();
|
||||
this->releasePeerConnectionFactory();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +106,18 @@ bool acgist::MediaManager::startVideoCapture() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool acgist::MediaManager::stopCapture() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool acgist::MediaManager::stopAudioCapture() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool acgist::MediaManager::stopVideoCapture() {
|
||||
return true;
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<webrtc::AudioTrackInterface> acgist::MediaManager::getAudioTrack() {
|
||||
cricket::AudioOptions options;
|
||||
options.highpass_filter = true;
|
||||
|
||||
@@ -1,7 +1,74 @@
|
||||
#include "../include/Client.hpp"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "hilog/log.h"
|
||||
|
||||
static std::mutex clientMutex;
|
||||
|
||||
acgist::RemoteClient::RemoteClient(acgist::MediaManager* mediaManager) : RoomClient(mediaManager) {}
|
||||
|
||||
acgist::RemoteClient::~RemoteClient() {}
|
||||
acgist::RemoteClient::~RemoteClient() {
|
||||
for(auto iterator = this->consumers.begin(); iterator != this->consumers.end(); ++iterator) {
|
||||
iterator->second->Close();
|
||||
delete iterator->second;
|
||||
iterator->second = nullptr;
|
||||
}
|
||||
this->consumers.clear();
|
||||
}
|
||||
|
||||
bool acgist::RemoteClient::release() { return true; }
|
||||
bool acgist::RemoteClient::addConsumer(const std::string& consumerId, mediasoupclient::Consumer* consumer) {
|
||||
std::lock_guard<std::mutex> clientLock(lockMutex);
|
||||
auto oldConsumer = this->consumers.find(clientId);
|
||||
if(oldConsumer != this->consumers.end()) {
|
||||
OH_LOG_INFO(LOG_APP, "关闭旧的消费者:%s", consumerId.data());
|
||||
oldConsumer->second->Close();
|
||||
delete oldConsumer->second;
|
||||
oldConsumer->second = nullptr;
|
||||
this->consumers.erase(oldConsumer);
|
||||
}
|
||||
OH_LOG_INFO(LOG_APP, "添加新的消费者:%s", consumerId.data());
|
||||
this->consumers.insert({ consumerId, consumer });
|
||||
webrtc::MediaStreamTrackInterface* trackPointer = consumer->GetTrack();
|
||||
// TODO: 播放
|
||||
return true;
|
||||
}
|
||||
|
||||
bool acgist::RemoteClient::closeConsumer(const std::string& consumerId) {
|
||||
std::lock_guard<std::mutex> clientLock(lockMutex);
|
||||
auto consumer = this->consumers.find(consumerId);
|
||||
if(consumer == this->consumers.end()) {
|
||||
OH_LOG_INFO(LOG_APP, "消费者已经关闭:%s", consumerId.data());
|
||||
return false;
|
||||
}
|
||||
OH_LOG_INFO(LOG_APP, "关闭消费者:%s", consumerId.data());
|
||||
consumer->second->Close();
|
||||
delete consumer->second;
|
||||
consumer->second = nullptr;
|
||||
this->consumers.erase(consumer);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool acgist::RemoteClient::pauseConsumer(const std::string& consumerId) {
|
||||
std::lock_guard<std::mutex> clientLock(lockMutex);
|
||||
auto consumer = this->consumers.find(consumerId);
|
||||
if(consumer == this->consumers.end()) {
|
||||
OH_LOG_INFO(LOG_APP, "无效消费者:%s", consumerId.data());
|
||||
return false;
|
||||
}
|
||||
OH_LOG_INFO(LOG_APP, "暂停消费者:%s", consumerId.data());
|
||||
consumer->second->Pause();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool acgist::RemoteClient::resumeConsumer(const std::string& consumerId) {
|
||||
std::lock_guard<std::mutex> clientLock(lockMutex);
|
||||
auto consumer = this->consumers.find(consumerId);
|
||||
if(consumer == this->consumers.end()) {
|
||||
OH_LOG_INFO(LOG_APP, "无效消费者:%s", consumerId.data());
|
||||
return false;
|
||||
}
|
||||
OH_LOG_INFO(LOG_APP, "恢复消费者:%s", consumerId.data());
|
||||
consumer->second->Resume();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ acgist::Room::Room(const std::string& roomId, acgist::MediaManager* mediaManager
|
||||
|
||||
acgist::Room::~Room() {
|
||||
this->close();
|
||||
this->consumerIdClientId.clear();
|
||||
// rtcConfiguration
|
||||
if (this->device != nullptr) {
|
||||
delete this->device;
|
||||
@@ -53,10 +54,10 @@ acgist::Room::~Room() {
|
||||
delete this->consumerListener;
|
||||
this->consumerListener = nullptr;
|
||||
}
|
||||
if(this->client != nullptr) {
|
||||
delete this->client;
|
||||
this->client = nullptr;
|
||||
}
|
||||
// TODO: delete audio track
|
||||
// TODO: delete video track
|
||||
// TODO: delete local client
|
||||
// TODO: delete remote client
|
||||
}
|
||||
|
||||
int acgist::Room::enter(const std::string& password) {
|
||||
@@ -109,6 +110,7 @@ int acgist::Room::produceMedia() {
|
||||
}
|
||||
|
||||
int acgist::Room::createSendTransport() {
|
||||
OH_LOG_INFO(LOG_APP, "创建发送通道:%s", this->roomId.data());
|
||||
nlohmann::json requestBody = {
|
||||
{ "roomId", this->roomId },
|
||||
{ "forceTcp", false },
|
||||
@@ -135,6 +137,7 @@ int acgist::Room::createSendTransport() {
|
||||
}
|
||||
|
||||
int acgist::Room::createRecvTransport() {
|
||||
OH_LOG_INFO(LOG_APP, "创建接收通道:%s", this->roomId.data());
|
||||
nlohmann::json requestBody = {
|
||||
{ "roomId", this->roomId },
|
||||
{ "forceTcp", false },
|
||||
@@ -161,39 +164,112 @@ int acgist::Room::createRecvTransport() {
|
||||
}
|
||||
|
||||
int acgist::Room::produceAudio() {
|
||||
if(!this->device->CanProduce("audio") || this->audioProducer == nullptr) {
|
||||
OH_LOG_INFO(LOG_APP, "不能生产音频媒体:%s", this->roomId.data());
|
||||
return -1;
|
||||
}
|
||||
// TODO:track
|
||||
if(this->audioTrack->state() == webrtc::MediaStreamTrackInterface::TrackState::kEnded) {
|
||||
OH_LOG_INFO(LOG_APP, "音频媒体状态错误:%s", this->roomId.data());
|
||||
return -2;
|
||||
}
|
||||
OH_LOG_INFO(LOG_APP, "生产音频媒体:%s", this->roomId.data());
|
||||
nlohmann::json codecOptions = {
|
||||
{ "opusStereo", true },
|
||||
{ "opusDtx", true }
|
||||
};
|
||||
this->audioProducer = this->sendTransport->Produce(
|
||||
this->producerListener,
|
||||
this->audioTrack,
|
||||
nullptr,
|
||||
&codecOptions,
|
||||
nullptr
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acgist::Room::produceVideo() {
|
||||
if(!this->device->CanProduce("video") || this->videoProducer == nullptr) {
|
||||
OH_LOG_INFO(LOG_APP, "不能生产视频媒体:%s", this->roomId.data());
|
||||
return -1;
|
||||
}
|
||||
// TODO:track
|
||||
if(this->videoTrack->state() == webrtc::MediaStreamTrackInterface::TrackState::kEnded) {
|
||||
OH_LOG_INFO(LOG_APP, "视频媒体状态错误:%s", this->roomId.data());
|
||||
return -2;
|
||||
}
|
||||
OH_LOG_INFO(LOG_APP, "生产视频媒体:%s", this->roomId.data());
|
||||
// TODO:配置读取同时测试效果
|
||||
nlohmann::json codecOptions = {
|
||||
// x-google-start-bitrate
|
||||
{ "videoGoogleStartBitrate", 1200 },
|
||||
// x-google-min-bitrate
|
||||
{ "videoGoogleMinBitrate", 800 },
|
||||
// x-google-max-bitrate
|
||||
{ "videoGoogleMaxBitrate", 1600 }
|
||||
};
|
||||
// 如果需要使用`Simulcast`打开下面配置
|
||||
// std::vector<webrtc::RtpEncodingParameters> encodings;
|
||||
// webrtc::RtpEncodingParameters min;
|
||||
// webrtc::RtpEncodingParameters mid;
|
||||
// webrtc::RtpEncodingParameters max;
|
||||
// min.active = true;
|
||||
// min.max_framerate = 15;
|
||||
// min.min_bitrate_bps = 400;
|
||||
// min.max_bitrate_bps = 800;
|
||||
// encodings.emplace_back(min);
|
||||
// encodings.emplace_back(mid);
|
||||
// encodings.emplace_back(max);
|
||||
// 强制设置编码器
|
||||
// nlohmann::json codec = this->device->GetRtpCapabilities()["codec"];
|
||||
this->videoProducer = this->sendTransport->Produce(
|
||||
this->producerListener,
|
||||
this->videoTrack,
|
||||
nullptr,
|
||||
&codecOptions,
|
||||
nullptr
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acgist::Room::close() {
|
||||
OH_LOG_INFO(LOG_APP, "关闭房间:%s", this->roomId.data());
|
||||
this->closeConsumer();
|
||||
this->closeClient();
|
||||
this->closeClients();
|
||||
this->closeAudioProducer();
|
||||
this->closeVideoProducer();
|
||||
this->closeTransport();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acgist::Room::closeConsumer() {
|
||||
int acgist::Room::closeClient() {
|
||||
std::lock_guard<std::mutex> lockRoom(roomMutex);
|
||||
for (auto iterator = this->consumers.begin(); iterator != this->consumers.end(); ++iterator) {
|
||||
OH_LOG_INFO(LOG_APP, "关闭本地终端:%s", this->roomId.data());
|
||||
if(this->client != nullptr) {
|
||||
delete this->client;
|
||||
this->client = nullptr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acgist::Room::closeClients() {
|
||||
std::lock_guard<std::mutex> lockRoom(roomMutex);
|
||||
OH_LOG_INFO(LOG_APP, "关闭远程终端:%s", this->roomId.data());
|
||||
for (auto iterator = this->clients.begin(); iterator != this->clients.end(); ++iterator) {
|
||||
if (iterator->second == nullptr) {
|
||||
continue;
|
||||
}
|
||||
OH_LOG_INFO(LOG_APP, "关闭消费者:%s", iterator->second->GetId().data());
|
||||
iterator->second->Close();
|
||||
OH_LOG_INFO(LOG_APP, "关闭消费者:%s", iterator->first.data());
|
||||
delete iterator->second;
|
||||
iterator->second = nullptr;
|
||||
}
|
||||
this->consumers.clear();
|
||||
this->clients.clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acgist::Room::closeAudioProducer() {
|
||||
std::lock_guard<std::mutex> lockRoom(roomMutex);
|
||||
OH_LOG_INFO(LOG_APP, "关闭音频生产者:%s", this->roomId.data());
|
||||
if (this->audioProducer != nullptr) {
|
||||
this->audioProducer->Close();
|
||||
}
|
||||
@@ -202,6 +278,7 @@ int acgist::Room::closeAudioProducer() {
|
||||
|
||||
int acgist::Room::closeVideoProducer() {
|
||||
std::lock_guard<std::mutex> lockRoom(roomMutex);
|
||||
OH_LOG_INFO(LOG_APP, "关闭视频生产者:%s", this->roomId.data());
|
||||
if (this->videoProducer != nullptr) {
|
||||
this->videoProducer->Close();
|
||||
}
|
||||
@@ -210,6 +287,7 @@ int acgist::Room::closeVideoProducer() {
|
||||
|
||||
int acgist::Room::closeTransport() {
|
||||
std::lock_guard<std::mutex> lockRoom(roomMutex);
|
||||
OH_LOG_INFO(LOG_APP, "关闭通道:%s", this->roomId.data());
|
||||
if (this->sendTransport != nullptr) {
|
||||
this->sendTransport->Close();
|
||||
}
|
||||
@@ -219,6 +297,165 @@ int acgist::Room::closeTransport() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acgist::Room::newRemoteClient(const std::string& clientId, const std::string& name) {
|
||||
std::lock_guard<std::mutex> lockRoom(roomMutex);
|
||||
auto oldClient = this->clients.find(clientId);
|
||||
if(oldClient != this->clients.end()) {
|
||||
OH_LOG_INFO(LOG_APP, "已经存在远程终端:%s", clientId.data());
|
||||
delete oldClient->second;
|
||||
oldClient->second = null;
|
||||
this->clients.erase(oldClient);
|
||||
}
|
||||
OH_LOG_INFO(LOG_APP, "新增远程终端:%s", clientId.data());
|
||||
acgist::RemoteClient* client = new acgist::RemoteClient(this->mediaManager);
|
||||
client->clientId = clientId;
|
||||
client->name = name;
|
||||
this->clients.insert({ clientId, client });
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acgist::Room::closeRemoteClient(const std::string& clientId) {
|
||||
std::lock_guard<std::mutex> lockRoom(roomMutex);
|
||||
auto client = this->clients.find(clientId);
|
||||
if(client == this->clients.end()) {
|
||||
OH_LOG_INFO(LOG_APP, "远程终端已经删除:%s", clientId.data());
|
||||
return -1;
|
||||
}
|
||||
OH_LOG_INFO(LOG_APP, "删除远程终端:%s", clientId.data());
|
||||
delete client->second;
|
||||
client->second = nullptr;
|
||||
this->clients->erase(client);
|
||||
// TODO: 清理consumerIdClientId
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acgist::Room::newConsumer(nlohmann::json& body) {
|
||||
std::lock_guard<std::mutex> lockRoom(roomMutex);
|
||||
mediasoupclient::Consumer* consumer = this->recvTransport->Consume(
|
||||
this->consumerListener,
|
||||
body["consumerId"],
|
||||
body["producerId"],
|
||||
body["kind"],
|
||||
&body["rtpParameters"]
|
||||
);
|
||||
std::string kind = body["kind"];
|
||||
std::string sourceId = body["sourceId"];
|
||||
std::string consumerId = body["consumerId"];
|
||||
OH_LOG_INFO(LOG_APP, "新增媒体消费:%s %s %s", kind.data(), sourceId.data(), consumerId.data());
|
||||
auto oldClient = this->clients.find(sourceId);
|
||||
acgist::RemoteClient* client = nullptr;
|
||||
if(oldClient == this->clients.end()) {
|
||||
// 假如媒体上来时间比进入房间消息快:基本上不可能出现这种情况
|
||||
client = new acgist::RemoteClient(this->mediaManager);
|
||||
client->clientId = clientId;
|
||||
client->name = clientId;
|
||||
this->clients.insert({ clientId, client });
|
||||
} else {
|
||||
client = oldClient->second;
|
||||
}
|
||||
client->addConsumer(consumer->GetId(), consumer);
|
||||
this->consumerIdClientId.insert({ consumer->GetId(), sourceId });
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acgist::Room::closeConsumer(const std::string& consumerId) {
|
||||
std::lock_guard<std::mutex> lockRoom(roomMutex);
|
||||
auto clientId = this->consumerIdClientId.find(consumerId);
|
||||
if(clientId == this->consumerIdClientId.end()) {
|
||||
OH_LOG_INFO(LOG_APP, "关闭消费者无效:%s", consumerId.data());
|
||||
return -1;
|
||||
}
|
||||
auto client = this->clients.find(clientId->second);
|
||||
if(client == this->clients.end()) {
|
||||
OH_LOG_INFO(LOG_APP, "关闭消费者无效:%s %s", consumerId.data(), clientId->second.data());
|
||||
return -2;
|
||||
}
|
||||
client->second->closeConsumer(consumerId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acgist::Room::pauseConsumer(const std::string& consumerId) {
|
||||
std::lock_guard<std::mutex> lockRoom(roomMutex);
|
||||
auto clientId = this->consumerIdClientId.find(consumerId);
|
||||
if(clientId == this->consumerIdClientId.end()) {
|
||||
OH_LOG_INFO(LOG_APP, "暂停消费者无效:%s", consumerId.data());
|
||||
return -1;
|
||||
}
|
||||
auto client = this->clients.find(clientId->second);
|
||||
if(client == this->clients.end()) {
|
||||
OH_LOG_INFO(LOG_APP, "暂停消费者无效:%s %s", consumerId.data(), clientId->second.data());
|
||||
return -2;
|
||||
}
|
||||
client->second->pauseConsumer(consumerId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acgist::Room::resumeConsumer(const std::string& consumerId) {
|
||||
std::lock_guard<std::mutex> lockRoom(roomMutex);
|
||||
auto clientId = this->consumerIdClientId.find(consumerId);
|
||||
if(clientId == this->consumerIdClientId.end()) {
|
||||
OH_LOG_INFO(LOG_APP, "恢复消费者无效:%s", consumerId.data());
|
||||
return -1;
|
||||
}
|
||||
auto client = this->clients.find(clientId->second);
|
||||
if(client == this->clients.end()) {
|
||||
OH_LOG_INFO(LOG_APP, "恢复消费者无效:%s %s", consumerId.data(), clientId->second.data());
|
||||
return -2;
|
||||
}
|
||||
client->second->resumeConsumer(consumerId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acgist::Room::closeProducer(const std::string& producerId) {
|
||||
std::lock_guard<std::mutex> lockRoom(roomMutex);
|
||||
if(this->audioProducer != nullptr && this->audioProducer->GetId() == producerId) {
|
||||
OH_LOG_INFO(LOG_APP, "关闭音频生产者:%s", producerId.data());
|
||||
this->audioProducer->Close();
|
||||
delete this->audioProducer;
|
||||
this->audioProducer == nullptr;
|
||||
// TODO: 释放
|
||||
// this->videoTrack->Dspo
|
||||
} else if(this->videoProducer != nullptr && this->videoProducer->GetId() == producerId) {
|
||||
OH_LOG_INFO(LOG_APP, "关闭视频生产者:%s", producerId.data());
|
||||
this->videoProducer->Close();
|
||||
delete this->videoProducer;
|
||||
this->videoProducer == nullptr;
|
||||
// TODO: 释放
|
||||
// this->videoTrack->Dspo
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acgist::Room::pauseProducer(const std::string& producerId) {
|
||||
std::lock_guard<std::mutex> lockRoom(roomMutex);
|
||||
if(this->audioProducer != nullptr && this->audioProducer->GetId() == producerId) {
|
||||
OH_LOG_INFO(LOG_APP, "暂停音频生产者:%s", producerId.data());
|
||||
this->audioProducer->Pause();
|
||||
} else if(this->videoProducer != nullptr && this->videoProducer->GetId() == producerId) {
|
||||
OH_LOG_INFO(LOG_APP, "暂停视频生产者:%s", producerId.data());
|
||||
this->videoProducer->Pause();
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int acgist::Room::resumeProducer(const std::string& producerId) {
|
||||
std::lock_guard<std::mutex> lockRoom(roomMutex);
|
||||
if(this->audioProducer != nullptr && this->audioProducer->GetId() == producerId) {
|
||||
OH_LOG_INFO(LOG_APP, "恢复音频生产者:%s", producerId.data());
|
||||
this->audioProducer->Resume();
|
||||
} else if(this->videoProducer != nullptr && this->videoProducer->GetId() == producerId) {
|
||||
OH_LOG_INFO(LOG_APP, "恢复视频生产者:%s", producerId.data());
|
||||
this->videoProducer->Resume();
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
acgist::SendListener::SendListener(Room* room) : room(room) {
|
||||
}
|
||||
|
||||
|
||||
@@ -3,5 +3,3 @@
|
||||
acgist::RoomClient::RoomClient(acgist::MediaManager* mediaManager) : acgist::Client(mediaManager) {}
|
||||
|
||||
acgist::RoomClient::~RoomClient() {}
|
||||
|
||||
bool acgist::RoomClient::release() { return true; }
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export const init : (json: string) => number;
|
||||
export const shutdown : (json: string) => number;
|
||||
export const roomClose : (json: string) => number;
|
||||
export const roomEnter : (json: string) => number;
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
import hilog from '@ohos.hilog';
|
||||
import window from '@ohos.window';
|
||||
import UIAbility from '@ohos.app.ability.UIAbility';
|
||||
import hilog from "@ohos.hilog";
|
||||
import window from "@ohos.window";
|
||||
import UIAbility from "@ohos.app.ability.UIAbility";
|
||||
|
||||
export default class EntryAbility extends UIAbility {
|
||||
|
||||
onCreate(want, launchParam) {
|
||||
hilog.info(0x0000, 'EntryAbility', 'onCreate');
|
||||
hilog.info(0x0000, "EntryAbility", "onCreate");
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
hilog.info(0x0000, 'EntryAbility', 'onDestroy');
|
||||
hilog.info(0x0000, "EntryAbility", "onDestroy");
|
||||
}
|
||||
|
||||
onWindowStageCreate(windowStage: window.WindowStage) {
|
||||
hilog.info(0x0000, 'EntryAbility', 'onWindowStageCreate');
|
||||
windowStage.loadContent('pages/Index', (err, data) => {
|
||||
hilog.info(0x0000, "EntryAbility", "onWindowStageCreate");
|
||||
windowStage.loadContent("pages/Index", (err, data) => {
|
||||
});
|
||||
}
|
||||
|
||||
onWindowStageDestroy() {
|
||||
hilog.info(0x0000, 'EntryAbility', 'onWindowStageDestroy');
|
||||
hilog.info(0x0000, "EntryAbility", "onWindowStageDestroy");
|
||||
}
|
||||
|
||||
onForeground() {
|
||||
hilog.info(0x0000, 'EntryAbility', 'onForeground');
|
||||
hilog.info(0x0000, "EntryAbility", "onForeground");
|
||||
}
|
||||
|
||||
onBackground() {
|
||||
hilog.info(0x0000, 'EntryAbility', 'onBackground');
|
||||
hilog.info(0x0000, "EntryAbility", "onBackground");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ struct Index {
|
||||
signal.connect();
|
||||
});
|
||||
}
|
||||
.width('50%');
|
||||
.width("50%");
|
||||
Column() {
|
||||
Button("断开信令")
|
||||
.fontSize(20)
|
||||
@@ -23,9 +23,9 @@ struct Index {
|
||||
signal.close();
|
||||
});
|
||||
}
|
||||
.width('50%');
|
||||
.width("50%");
|
||||
}
|
||||
.height('100%');
|
||||
.height("100%");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,26 +5,28 @@
|
||||
*/
|
||||
|
||||
class Signal {
|
||||
|
||||
// 终端名称
|
||||
name: string = "鸿蒙";
|
||||
name : string = "鸿蒙";
|
||||
// 终端ID
|
||||
clientId: string = "harmony";
|
||||
clientId : string = "harmony";
|
||||
// 终端类型
|
||||
clientType: string = "MOBILE";
|
||||
// 信令账号
|
||||
username: string = "taoyao";
|
||||
username : string = "taoyao";
|
||||
// 信令密码
|
||||
password: string = "taoyao";
|
||||
password : string = "taoyao";
|
||||
|
||||
};
|
||||
|
||||
class Setting {
|
||||
|
||||
// 信令地址
|
||||
signalAddress: string = "wss://192.168.8.204:8888/websocket.signal";
|
||||
signalAddress: string = "wss://192.168.1.100:8888/websocket.signal";
|
||||
// 信令版本
|
||||
version: string = "1.0.0";
|
||||
// 信令配置
|
||||
signal: Signal = new Signal();
|
||||
signal : Signal = new Signal();
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* 信令连接
|
||||
* 信令
|
||||
*
|
||||
* @author acgist
|
||||
*/
|
||||
@@ -44,7 +44,6 @@ class TaoyaoSignal {
|
||||
this.socket.close();
|
||||
}
|
||||
this.connected = false;
|
||||
hilog.info(0x0000, "TaoyaoSignal", "连接信令:%s", setting.signalAddress);
|
||||
this.socket = webSocket.createWebSocket();
|
||||
this.socket.on("open", (err: BusinessError, value: Object) => {
|
||||
hilog.info(0x0000, "TaoyaoSignal", "打开信令:%s", setting.signalAddress, value, err);
|
||||
@@ -52,11 +51,12 @@ class TaoyaoSignal {
|
||||
this.connected = true;
|
||||
});
|
||||
this.socket.on("message", (err: BusinessError, value: string | ArrayBuffer) => {
|
||||
hilog.debug(0x0000, "TaoyaoSignal", "信令消息:%s", value, err);
|
||||
const message = value?.toString();
|
||||
hilog.debug(0x0000, "TaoyaoSignal", "信令消息:%s", message, err);
|
||||
try {
|
||||
this.onMessage(value.toString());
|
||||
this.onMessage(message);
|
||||
} catch (error) {
|
||||
hilog.error(0x0000, "TaoyaoSignal", "处理信令消息异常:%s", value, error);
|
||||
hilog.error(0x0000, "TaoyaoSignal", "处理信令消息异常:%s", message, error);
|
||||
}
|
||||
});
|
||||
this.socket.on("close", (err: BusinessError, value: Object) => {
|
||||
@@ -67,6 +67,7 @@ class TaoyaoSignal {
|
||||
hilog.error(0x0000, "TaoyaoSignal", "信令异常:%s", setting.signalAddress, err);
|
||||
this.reconnect();
|
||||
});
|
||||
hilog.info(0x0000, "TaoyaoSignal", "连接信令:%s", setting.signalAddress);
|
||||
this.socket.connect(setting.signalAddress, (err: BusinessError, value: boolean) => {
|
||||
hilog.info(0x0000, "TaoyaoSignal", "信令连接成功:%s", setting.signalAddress, value, err);
|
||||
});
|
||||
@@ -109,15 +110,15 @@ class TaoyaoSignal {
|
||||
"battery" : 100,
|
||||
"charging" : true
|
||||
});
|
||||
const body : Record<string, Object> = response.body as Record<string, Object>;
|
||||
const index : number = body.index as number;
|
||||
const body = response.body as Record<string, Object>;
|
||||
const index = body.index as number;
|
||||
this.clientIndex = index;
|
||||
hilog.info(0x0000, "TaoyaoSignal", "信令注册成功:%d", index);
|
||||
this.heartbeat();
|
||||
}
|
||||
|
||||
/**
|
||||
* 心跳
|
||||
* 心跳信令
|
||||
*/
|
||||
heartbeat() {
|
||||
if(this.heartbeatTimer) {
|
||||
@@ -166,7 +167,7 @@ class TaoyaoSignal {
|
||||
"body" : body
|
||||
};
|
||||
try {
|
||||
hilog.debug(0x0000, "TaoyaoSignal", "发送消息:%o", message);
|
||||
hilog.debug(0x0000, "TaoyaoSignal", "发送消息:%o", message);
|
||||
this.socket?.send(JSON.stringify(message));
|
||||
} catch (error) {
|
||||
hilog.error(0x0000, "TaoyaoSignal", "发送消息异常:%o", message, error);
|
||||
@@ -182,8 +183,8 @@ class TaoyaoSignal {
|
||||
* @returns 响应
|
||||
*/
|
||||
async request(signal: string, body: Record<string, Object>): Promise<Record<string, Object>> {
|
||||
const id = this.buildId();
|
||||
return new Promise<Record<string, Object>>((resolve, reject) => {
|
||||
const id = this.buildId();
|
||||
const header: Record<string, Object> = {
|
||||
"v" : setting.version,
|
||||
"id" : id,
|
||||
@@ -224,13 +225,12 @@ class TaoyaoSignal {
|
||||
onMessage(message: string) {
|
||||
const json : Record<string, Object> = JSON.parse(message);
|
||||
const header: Record<string, Object> = json.header as Record<string, Object>;
|
||||
const body : Record<string, Object> = json.body as Record<string, Object>;
|
||||
const id : number = header.id as number;
|
||||
const signal: string = header.signal as string;
|
||||
if (this.callbackMapping.has(id)) {
|
||||
hilog.debug(0x0000, "TaoyaoSignal", "处理同步消息:%s", message);
|
||||
try {
|
||||
const callback: Function = this.callbackMapping.get(id) as Function;
|
||||
const callback = this.callbackMapping.get(id) as Function;
|
||||
if(callback(json)) {
|
||||
return;
|
||||
}
|
||||
@@ -238,21 +238,73 @@ class TaoyaoSignal {
|
||||
this.callbackMapping.delete(id);
|
||||
}
|
||||
}
|
||||
hilog.debug(0x0000, "TaoyaoSignal", "处理异步消息:%s", message);
|
||||
let ret: number = 0;
|
||||
switch (signal) {
|
||||
case "room::close":
|
||||
ret = taoyaoModule.roomClose(message);
|
||||
break;
|
||||
case "room::enter":
|
||||
ret = taoyaoModule.roomEnter(message);
|
||||
break;
|
||||
case "room::expel":
|
||||
ret = taoyaoModule.roomExpel(message);
|
||||
break;
|
||||
case "room::invite":
|
||||
taoyaoModule.roomInvite(message);
|
||||
ret = taoyaoModule.roomInvite(message);
|
||||
break;
|
||||
case "room::leave":
|
||||
ret = taoyaoModule.roomLeave(message);
|
||||
break;
|
||||
case "room::client::list":
|
||||
ret = taoyaoModule.roomClientList(message);
|
||||
break;
|
||||
case "media::consume":
|
||||
ret = taoyaoModule.mediaConsume(message);
|
||||
break;
|
||||
case "media::consumer::close":
|
||||
ret = taoyaoModule.mediaConsumerClose(message);
|
||||
break;
|
||||
case "media::consumer::pause":
|
||||
ret = taoyaoModule.mediaConsumerPause(message);
|
||||
break;
|
||||
case "media::consumer::resume":
|
||||
ret = taoyaoModule.mediaConsumerResume(message);
|
||||
break;
|
||||
case "media::producer::close":
|
||||
ret = taoyaoModule.mediaProducerClose(message);
|
||||
break;
|
||||
case "media::producer::pause":
|
||||
ret = taoyaoModule.mediaProducerPause(message);
|
||||
break;
|
||||
case "media::producer::resume":
|
||||
ret = taoyaoModule.mediaProducerResume(message);
|
||||
break;
|
||||
default:
|
||||
hilog.error(0x0000, "TaoyaoSignal", "没有适配信令:%s", signal);
|
||||
ret = -1;
|
||||
hilog.warn(0x0000, "TaoyaoSignal", "没有适配信令:%s", signal);
|
||||
break;
|
||||
}
|
||||
hilog.debug(0x0000, "TaoyaoSignal", "处理异步消息:%d %s", ret, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册发送消息
|
||||
*
|
||||
* @param signal 信令
|
||||
* @param body 主体
|
||||
*/
|
||||
nativeSend(signal: string, body: string) {
|
||||
this.send(signal, JSON.parse(body));
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册请求消息
|
||||
*
|
||||
* @param signal 信令
|
||||
* @param body 主体
|
||||
*
|
||||
* @returns 响应
|
||||
*/
|
||||
async nativeRequest(signal: string, body: string): Promise<string> {
|
||||
const response = await this.request(signal, JSON.parse(body));
|
||||
return JSON.stringify(response);
|
||||
@@ -262,6 +314,8 @@ class TaoyaoSignal {
|
||||
|
||||
const signal = new TaoyaoSignal();
|
||||
|
||||
// 加载系统
|
||||
taoyaoModule.init(JSON.stringify(setting.signal));
|
||||
// 注册回调
|
||||
taoyaoModule.registerSend(signal.nativeSend);
|
||||
taoyaoModule.registerRequest(signal.nativeRequest);
|
||||
|
||||
Reference in New Issue
Block a user