[*] 多线程

This commit is contained in:
acgist
2024-05-12 10:30:24 +08:00
parent d41100873d
commit 3d2f8e08f9
12 changed files with 310 additions and 146 deletions

View File

@@ -57,10 +57,8 @@ target_include_directories(
target_link_libraries(
${PROJECT_NAME} PUBLIC
# NAPI
libace_napi.z.so
# LOG
libhilog_ndk.z.so
# libuv
libuv.so
# EGL
libEGL.so
# OpenGL ES
@@ -71,6 +69,10 @@ target_link_libraries(
libohaudio.so
# 相机
libohcamera.so
# NAPI
libace_napi.z.so
# LOG
libhilog_ndk.z.so
# 图片
libnative_image.so
# NativeBuffer

View File

@@ -6,11 +6,19 @@
* @author acgist
*
* https://docs.openharmony.cn/pages/v4.1/zh-cn/application-dev/reference/native-lib/napi.md
* https://docs.openharmony.cn/pages/v4.1/zh-cn/application-dev/napi/use-napi-thread-safety.md
* https://docs.openharmony.cn/pages/v4.1/zh-cn/application-dev/performance/native-threads-call-js.md
* https://docs.openharmony.cn/pages/v4.1/zh-cn/application-dev/performance/develop-Native-modules-using-NAPI-safely-and-efficiently.md
*/
#include <map>
#include <mutex>
#include <string>
#include <future>
#include <thread>
#include <chrono>
#include <uv.h>
#include <hilog/log.h>
@@ -42,20 +50,25 @@ static std::recursive_mutex taoyaoMutex;
size_t length; \
char chars[2048] = { 0 }; \
napi_get_value_string_utf8(env, args[0], chars, sizeof(chars), &length); \
OH_LOG_INFO(LOG_APP, "解析JSON%s", chars); \
if(length <= 0) { \
OH_LOG_WARN(LOG_APP, "TAOYAO ERROR JSON: %{public}s", chars); \
napi_create_int32(env, -1, &ret); \
return ret; \
} \
OH_LOG_DEBUG(LOG_APP, "TAOYAO JSON: %{public}s", chars); \
nlohmann::json json = nlohmann::json::parse(chars, chars + length); \
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; \
#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, "TAOYAO ERROR ROOM ID: %{public}s %{public}s", #action, roomId.data()); \
napi_create_int32(env, -1, &ret); \
return ret; \
}
#endif
@@ -64,6 +77,7 @@ namespace acgist {
uint32_t width = 720;
uint32_t height = 480;
uint64_t bitrate = 3'000'000L;
uint32_t iFrameInterval = 5'000;
double frameRate = 30.0;
int32_t samplingRate = 48'000;
int32_t channelCount = 2;
@@ -71,18 +85,30 @@ int32_t bitsPerSample = 16;
std::string clientId = "";
std::string name = "";
// 索引667-999
static uint32_t index = 667;
// 最小索引
static uint32_t minIndex = 667;
// 最大索引
static uint32_t maxIndex = 999;
// 终端索引
static uint32_t clientIndex = 99999;
// ETS环境
static napi_env env = nullptr;
// 是否加载
static bool initTaoyao = false;
// PUSH方法引用
// push方法引用
static napi_ref pushRef = nullptr;
// REQUEST方法引用
// request方法引用
static napi_ref requestRef = nullptr;
// request线程安全方法
static napi_threadsafe_function requestFunction = nullptr;
// 媒体功能
static acgist::MediaManager* mediaManager = nullptr;
// 房间管理
static std::map<std::string, acgist::Room*> roomMap;
// 异步回调
static std::map<uint64_t, std::promise<std::string>*> promiseMap;
/**
* 支持的编解码
@@ -91,58 +117,135 @@ static void printSupportCodec() {
// TODO: 验证是否需要释放
OH_AVCapability* format = nullptr;
format = OH_AVCodec_GetCapability(OH_AVCODEC_MIMETYPE_AUDIO_OPUS, true);
OH_LOG_INFO(LOG_APP, "是否支持OPUS硬件解码%o", OH_AVCapability_IsHardware(format));
OH_LOG_INFO(LOG_APP, "是否支持OPUS硬件解码%{public}o", OH_AVCapability_IsHardware(format));
format = OH_AVCodec_GetCapability(OH_AVCODEC_MIMETYPE_AUDIO_OPUS, false);
OH_LOG_INFO(LOG_APP, "是否支持OPUS硬件解码%o", OH_AVCapability_IsHardware(format));
OH_LOG_INFO(LOG_APP, "是否支持OPUS硬件解码%{public}o", OH_AVCapability_IsHardware(format));
format = OH_AVCodec_GetCapability(OH_AVCODEC_MIMETYPE_AUDIO_G711MU, true);
OH_LOG_INFO(LOG_APP, "是否支持PCMU硬件编码%o", OH_AVCapability_IsHardware(format));
OH_LOG_INFO(LOG_APP, "是否支持PCMU硬件编码%{public}o", OH_AVCapability_IsHardware(format));
format = OH_AVCodec_GetCapability(OH_AVCODEC_MIMETYPE_AUDIO_G711MU, false);
OH_LOG_INFO(LOG_APP, "是否支持PCMU硬件解码%o", OH_AVCapability_IsHardware(format));
OH_LOG_INFO(LOG_APP, "是否支持PCMU硬件解码%{public}o", OH_AVCapability_IsHardware(format));
format = OH_AVCodec_GetCapability(OH_AVCODEC_MIMETYPE_VIDEO_AVC, true);
OH_LOG_INFO(LOG_APP, "是否支持H264硬件编码%o", OH_AVCapability_IsHardware(format));
OH_LOG_INFO(LOG_APP, "是否支持H264硬件编码%{public}o", OH_AVCapability_IsHardware(format));
format = OH_AVCodec_GetCapability(OH_AVCODEC_MIMETYPE_VIDEO_AVC, false);
OH_LOG_INFO(LOG_APP, "是否支持H264硬件解码%o", OH_AVCapability_IsHardware(format));
OH_LOG_INFO(LOG_APP, "是否支持H264硬件解码%{public}o", OH_AVCapability_IsHardware(format));
format = OH_AVCodec_GetCapability(OH_AVCODEC_MIMETYPE_VIDEO_HEVC, true);
OH_LOG_INFO(LOG_APP, "是否支持H265硬件编码%o", OH_AVCapability_IsHardware(format));
OH_LOG_INFO(LOG_APP, "是否支持H265硬件编码%{public}o", OH_AVCapability_IsHardware(format));
format = OH_AVCodec_GetCapability(OH_AVCODEC_MIMETYPE_VIDEO_HEVC, false);
OH_LOG_INFO(LOG_APP, "是否支持H265硬件解码%o", OH_AVCapability_IsHardware(format));
OH_LOG_INFO(LOG_APP, "是否支持H265硬件解码%{public}o", OH_AVCapability_IsHardware(format));
}
struct Message {
uint64_t id;
std::string signal;
std::string body;
};
static void pushCallback(uv_work_t* work) {
// 不能调用ETS函数
}
static void afterPushCallback(uv_work_t* work, int status) {
Message* message = (Message*) work->data;
napi_handle_scope scope = nullptr;
napi_open_handle_scope(acgist::env, &scope);
if(scope == nullptr) {
delete message;
delete work;
return;
};
// 开始执行ETS函数
napi_value ret;
napi_value callback = nullptr;
napi_get_reference_value(acgist::env, acgist::pushRef, &callback);
napi_value data[3];
napi_create_string_utf8(acgist::env, message->signal.data(), NAPI_AUTO_LENGTH, &data[0]);
napi_create_string_utf8(acgist::env, message->body.data(), NAPI_AUTO_LENGTH, &data[1]);
napi_create_int64(acgist::env, message->id, &data[2]);
napi_call_function(acgist::env, nullptr, callback, 3, data, &ret);
napi_get_undefined(acgist::env, &ret);
// 释放资源
napi_close_handle_scope(acgist::env, scope);
delete message;
delete work;
}
/**
* 发送消息
*/
void push(const std::string& signal, const std::string& body, uint64_t id) {
// TODO: 验证是否需要释放
uv_loop_s* loop = nullptr;
napi_get_uv_event_loop(acgist::env, &loop);
uv_work_t* work = new uv_work_t{};
work->data = new Message{ id, signal, body };
uv_queue_work(loop, work, pushCallback, afterPushCallback);
}
static void requestCallback(uv_work_t* work) {
// 不能调用ETS函数
}
static void afterRequestCallback(uv_work_t* work, int status) {
Message* message = (Message*) work->data;
napi_handle_scope scope = nullptr;
napi_open_handle_scope(acgist::env, &scope);
if(scope == nullptr) {
delete message;
delete work;
return;
};
// 开始执行ETS函数
napi_value ret;
napi_value callback = nullptr;
napi_get_reference_value(env, acgist::pushRef, &callback);
napi_get_reference_value(env, acgist::requestRef, &callback);
napi_value data[3];
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_create_int64(acgist::env, id, &data[2]);
napi_create_string_utf8(acgist::env, message->signal.data(), NAPI_AUTO_LENGTH, &data[0]);
napi_create_string_utf8(acgist::env, message->body.data(), NAPI_AUTO_LENGTH, &data[1]);
napi_create_int64(acgist::env, message->id, &data[2]);
napi_call_function(acgist::env, nullptr, callback, 3, data, &ret);
napi_get_undefined(acgist::env, &ret);
// 释放资源
napi_close_handle_scope(acgist::env, scope);
delete message;
delete work;
}
/**
* 发送请求
*/
std::string request(const std::string& signal, const std::string& body, uint64_t id) {
napi_value ret;
napi_value callback = nullptr;
napi_get_reference_value(env, acgist::requestRef, &callback);
napi_value data[3];
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_create_int64(acgist::env, id, &data[2]);
napi_call_function(acgist::env, nullptr, callback, 3, data, &ret);
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;
uv_loop_s* loop = nullptr;
napi_get_uv_event_loop(acgist::env, &loop);
uv_work_t* work = new uv_work_t{};
if(id <= 0L) {
if (++acgist::index > acgist::maxIndex) {
acgist::index = acgist::minIndex;
}
auto now = std::chrono::system_clock::now();
std::time_t time = std::chrono::system_clock::to_time_t(now);
std::tm* tm = std::localtime(&time);
id =
100000000000000L * tm->tm_mday +
1000000000000 * tm->tm_hour +
10000000000 * tm->tm_min +
100000000 * tm->tm_sec +
1000 * acgist::clientIndex +
acgist::index;
}
std::promise<std::string>* promise = new std::promise<std::string>{};
acgist::promiseMap.insert({ id, promise });
work->data = new Message{ id, signal, body };
uv_queue_work(loop, work, requestCallback, afterRequestCallback);
std::future<std::string> future = promise->get_future();
if(future.wait_for(std::chrono::seconds(5)) == std::future_status::timeout) {
OH_LOG_WARN(LOG_APP, "请求超时:%{public}s %{public}s", signal.data(), body.data());
acgist::promiseMap.erase(id);
delete promise;
return "{}";
} else {
acgist::promiseMap.erase(id);
delete promise;
return future.get();
}
}
/**
@@ -162,11 +265,12 @@ static napi_value init(napi_env env, napi_callback_info info) {
napi_create_reference(env, args[1], 1, &acgist::pushRef);
napi_create_reference(env, args[2], 1, &acgist::requestRef);
printSupportCodec();
acgist::clientId = json["clientId"];
acgist::name = json["name"];
acgist::clientId = json["clientId"];
acgist::name = json["name"];
acgist::clientIndex = json["clientIndex"];
OH_LOG_INFO(LOG_APP, "加载libtaoyao");
std::string version = mediasoupclient::Version();
OH_LOG_INFO(LOG_APP, "加载MediasoupClient%s", version.data());
OH_LOG_INFO(LOG_APP, "加载MediasoupClient%{public}s", version.data());
mediasoupclient::Initialize();
OH_LOG_INFO(LOG_APP, "加载媒体功能");
mediaManager = new MediaManager();
@@ -212,6 +316,23 @@ static napi_value shutdown(napi_env env, napi_callback_info info) {
return ret;
}
/**
* Promise回调
*/
static napi_value callback(napi_env env, napi_callback_info info) {
TAOYAO_JSON_BODY(1);
nlohmann::json header = json["header"];
uint64_t id = header["id"];
auto promise = acgist::promiseMap.find(id);
if(promise == acgist::promiseMap.end()) {
napi_create_int32(env, -1, &ret);
} else {
napi_create_int32(env, 0, &ret);
promise->second->set_value(chars);
}
return ret;
}
/**
* 房间关闭
*/
@@ -448,6 +569,7 @@ 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 },
{ "callback", nullptr, acgist::callback, 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 },

View File

@@ -20,10 +20,8 @@
#define TAOYAO_CAPTURER_HPP
// OpenGL ES || VULKAN
#define __TAOYAO_VULKAN__ true
#ifndef __TAOYAO_VULKAN__
#define __TAOYAO_VULKAN__ false
#define __TAOYAO_OPENGL__ true
#endif
// 本地音频采集
#define __TAOYAO_AUDIO_LOCAL__ false

View File

@@ -21,6 +21,8 @@ extern uint32_t width;
extern uint32_t height;
// 视频码率
extern uint64_t bitrate;
// 关键帧的频率
extern uint32_t iFrameInterval;
// 视频帧率
extern double frameRate;
// 采样率

View File

@@ -98,7 +98,7 @@ public:
// 视频编码器
OH_AVCodec* avCodec = nullptr;
// 缓冲数据索引
uint32_t index = 0;
uint32_t index = 0;
// 缓冲数据
OH_AVBuffer* buffer = nullptr;
// 编码回调
@@ -108,12 +108,14 @@ public:
TaoyaoVideoEncoder();
virtual ~TaoyaoVideoEncoder() override;
public:
protected:
// 初始配置
void initFormatConfig(OH_AVFormat* format);
public:
// 重新开始
void restart();
// 动态配置
// 动态配置format自己释放
void reset(OH_AVFormat* format);
// 动态配置
void resetIntConfig(const char* key, int32_t value);
@@ -140,6 +142,10 @@ public:
*/
class TaoyaoVideoDecoder : public webrtc::VideoDecoder {
public:
// 是否运行
bool running = false;
public:
TaoyaoVideoDecoder();
virtual ~TaoyaoVideoDecoder() override;

View File

@@ -1,7 +1,3 @@
/**
* 音频采集不用实现(系统已经实现)
* 这里只是用来学习使用
*/
#include "../include/Capturer.hpp"
#include <mutex>

View File

@@ -11,6 +11,8 @@
#include "../include/Capturer.hpp"
#include <mutex>
#include "hilog/log.h"
#include "rtc_base/time_utils.h"
@@ -21,6 +23,8 @@
#include <ohcamera/camera_manager.h>
#include <ohcamera/capture_session.h>
static std::recursive_mutex videoMutex;
// 采集回调
static void onError(Camera_VideoOutput* videoOutput, Camera_ErrorCode errorCode);
static void onFrameEnd(Camera_VideoOutput* videoOutput, int32_t frameCount);
@@ -33,14 +37,19 @@ static void onFrame(void* context);
static bool CheckEglExtension(const char* extensions, const char* extension);
acgist::VideoCapturer::VideoCapturer() {
#if __TAOYAO_VULKAN__
initVulkan();
#endif
#if __TAOYAO_OPENGL__
initOpenGLES();
#endif
Camera_ErrorCode ret = OH_Camera_GetCameraManager(&this->cameraManager);
OH_LOG_INFO(LOG_APP, "获取摄像头管理器:%o", ret);
OH_LOG_INFO(LOG_APP, "配置摄像头管理器:%o", ret);
ret = OH_CameraManager_GetSupportedCameras(this->cameraManager, &this->cameraDevice, &this->cameraSize);
OH_LOG_INFO(LOG_APP, "获取摄像头设备列表:%o %d", ret, this->cameraSize);
ret = OH_CameraManager_GetSupportedCameraOutputCapability(this->cameraManager, &this->cameraDevice[this->cameraIndex], &this->cameraOutputCapability);
OH_LOG_INFO(LOG_APP, "获取摄像头输出功能:%o %d %d", ret, this->cameraIndex, this->cameraOutputCapability->videoProfilesSize);
// 注册相机状态回调
// 注册相机状态回调:可以取消注册
// OH_CameraManager_RegisterCallback(this->cameraManager, CameraManager_Callbacks* callback);
// char surfaceId[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
// uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
@@ -55,7 +64,12 @@ acgist::VideoCapturer::VideoCapturer() {
}
acgist::VideoCapturer::~VideoCapturer() {
#if __TAOYAO_VULKAN__
releaseVulkan();
#endif
#if __TAOYAO_OPENGL__
releaseOpenGLES();
#endif
Camera_ErrorCode ret = OH_CaptureSession_Release(this->cameraCaptureSession);
this->cameraCaptureSession = nullptr;
OH_LOG_INFO(LOG_APP, "释放摄像头视频会话:%o", ret);
@@ -74,6 +88,7 @@ acgist::VideoCapturer::~VideoCapturer() {
}
bool acgist::VideoCapturer::start() {
std::lock_guard<std::recursive_mutex> videoLock(videoMutex);
if (this->running) {
return true;
}
@@ -90,6 +105,7 @@ bool acgist::VideoCapturer::start() {
callbacks.onError = onError;
callbacks.onFrameEnd = onFrameEnd;
callbacks.onFrameStart = onFrameStart;
// :可以取消注册
ret = OH_VideoOutput_RegisterCallback(this->cameraVideoOutput, &callbacks);
OH_LOG_INFO(LOG_APP, "视频捕获回调:%o", ret);
OH_NativeImage_AttachContext(this->nativeImage, this->textureId);
@@ -97,6 +113,7 @@ bool acgist::VideoCapturer::start() {
}
bool acgist::VideoCapturer::stop() {
std::lock_guard<std::recursive_mutex> videoLock(videoMutex);
if (!this->running) {
return true;
}
@@ -247,7 +264,7 @@ void acgist::VideoCapturer::releaseOpenGLES() {
}
static void onError(Camera_VideoOutput* videoOutput, Camera_ErrorCode errorCode) {
OH_LOG_WARN(LOG_APP, "视频捕获数据帧失败:%d", errorCode);
OH_LOG_ERROR(LOG_APP, "视频捕获数据帧失败:%d", errorCode);
}
static void onFrameEnd(Camera_VideoOutput* videoOutput, int32_t frameCount) {

View File

@@ -1,6 +1,8 @@
#include "../include/WebRTC.hpp"
#include "hilog/log.h"
#include <mutex>
#include <hilog/log.h>
#include <native_window/external_window.h>
#include <multimedia/player_framework/native_avbuffer.h>
@@ -12,6 +14,8 @@
#include "api/video_codecs/video_encoder.h"
#include "api/video_codecs/video_decoder.h"
static std::recursive_mutex videoMutex;
// 编码回调
static void OnError(OH_AVCodec* codec, int32_t errorCode, void* userData);
static void OnStreamChanged(OH_AVCodec* codec, OH_AVFormat* format, void* userData);
@@ -22,18 +26,18 @@ acgist::TaoyaoVideoEncoder::TaoyaoVideoEncoder() {
OH_AVCapability* capability = OH_AVCodec_GetCapability(OH_AVCODEC_MIMETYPE_VIDEO_AVC, true);
const char* codecName = OH_AVCapability_GetName(capability);
this->avCodec = OH_VideoEncoder_CreateByName(codecName);
OH_LOG_INFO(LOG_APP, "视频编码格式:%s", codecName);
// 注册回调
OH_LOG_INFO(LOG_APP, "配置视频编码格式:%s", codecName);
// 注册视频编码回调
OH_AVCodecCallback callback = { &OnError, &OnStreamChanged, &OnNeedInputBuffer, &OnNewOutputBuffer };
OH_AVErrCode ret = OH_VideoEncoder_RegisterCallback(this->avCodec, callback, this);
OH_LOG_INFO(LOG_APP, "注册编码回调:%o", ret);
// 配置编码参数
OH_LOG_INFO(LOG_APP, "注册视频编码回调:%o", ret);
// 配置视频编码参数
OH_AVFormat* format = OH_AVFormat_Create();
this->initFormatConfig(format);
ret = OH_VideoEncoder_Configure(this->avCodec, format);
OH_AVFormat_Destroy(format);
OH_LOG_INFO(LOG_APP, "配置编码参数:%o %d %d %f %lld", ret, acgist::width, acgist::height, acgist::frameRate, acgist::bitrate);
// 准备就绪
OH_LOG_INFO(LOG_APP, "配置视频编码参数:%o %d %d %f %d %lld", ret, acgist::width, acgist::height, acgist::frameRate, acgist::iFrameInterval, acgist::bitrate);
// 视频编码准备就绪
ret = OH_VideoEncoder_Prepare(this->avCodec);
OH_LOG_INFO(LOG_APP, "视频编码准备就绪:%o", ret);
}
@@ -48,42 +52,31 @@ acgist::TaoyaoVideoEncoder::~TaoyaoVideoEncoder() {
void acgist::TaoyaoVideoEncoder::initFormatConfig(OH_AVFormat* format) {
// https://docs.openharmony.cn/pages/v4.1/zh-cn/application-dev/media/avcodec/video-encoding.md
// 配置视频宽度
OH_AVFormat_SetIntValue(format, OH_MD_KEY_WIDTH, acgist::width);
// 配置视频高度
OH_AVFormat_SetIntValue(format, OH_MD_KEY_HEIGHT, acgist::height);
// 配置视频比特率
OH_AVFormat_SetLongValue(format, OH_MD_KEY_BITRATE, acgist::bitrate);
// 配置视频帧率
OH_AVFormat_SetDoubleValue(format, OH_MD_KEY_FRAME_RATE, acgist::frameRate);
// 配置视频颜色格式
OH_AVFormat_SetIntValue(format, OH_MD_KEY_PIXEL_FORMAT, AV_PIXEL_FORMAT_YUVI420);
// 配置视频YUV值范围标志
OH_AVFormat_SetIntValue(format, OH_MD_KEY_RANGE_FLAG, false);
// 配置视频原色
OH_AVFormat_SetIntValue(format, OH_MD_KEY_COLOR_PRIMARIES, static_cast<int32_t>(OH_ColorPrimary::COLOR_PRIMARY_BT709));
// 配置传输特性
OH_AVFormat_SetIntValue(format, OH_MD_KEY_TRANSFER_CHARACTERISTICS, static_cast<int32_t>(OH_TransferCharacteristic::TRANSFER_CHARACTERISTIC_BT709));
// 配置最大矩阵系数
OH_AVFormat_SetIntValue(format, OH_MD_KEY_MATRIX_COEFFICIENTS, static_cast<int32_t>(OH_MatrixCoefficient::MATRIX_COEFFICIENT_IDENTITY));
// 配置关键帧的间隔(单位为:毫秒)
OH_AVFormat_SetIntValue(format, OH_MD_KEY_I_FRAME_INTERVAL, 5000);
// 配置编码Profile
OH_AVFormat_SetIntValue(format, OH_MD_KEY_PROFILE, static_cast<int32_t>(OH_AVCProfile::AVC_PROFILE_BASELINE));
// 配置编码比特率模式
OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_ENCODE_BITRATE_MODE, static_cast<int32_t>(OH_VideoEncodeBitrateMode::CBR));
// 配置所需的编码质量:只有在恒定质量模式下配置的编码器才支持此配置
OH_AVFormat_SetIntValue(format, OH_MD_KEY_QUALITY, 0);
OH_AVFormat_SetIntValue(format, OH_MD_KEY_WIDTH, acgist::width);
OH_AVFormat_SetIntValue(format, OH_MD_KEY_HEIGHT, acgist::height);
OH_AVFormat_SetLongValue(format, OH_MD_KEY_BITRATE, acgist::bitrate);
OH_AVFormat_SetDoubleValue(format, OH_MD_KEY_FRAME_RATE, acgist::frameRate);
OH_AVFormat_SetIntValue(format, OH_MD_KEY_I_FRAME_INTERVAL, acgist::iFrameInterval);
OH_AVFormat_SetIntValue(format, OH_MD_KEY_QUALITY, 0);
OH_AVFormat_SetIntValue(format, OH_MD_KEY_RANGE_FLAG, false);
OH_AVFormat_SetIntValue(format, OH_MD_KEY_PIXEL_FORMAT, AV_PIXEL_FORMAT_YUVI420);
OH_AVFormat_SetIntValue(format, OH_MD_KEY_PROFILE, static_cast<int32_t>(OH_AVCProfile::AVC_PROFILE_BASELINE));
OH_AVFormat_SetIntValue(format, OH_MD_KEY_COLOR_PRIMARIES, static_cast<int32_t>(OH_ColorPrimary::COLOR_PRIMARY_BT709));
OH_AVFormat_SetIntValue(format, OH_MD_KEY_MATRIX_COEFFICIENTS, static_cast<int32_t>(OH_MatrixCoefficient::MATRIX_COEFFICIENT_IDENTITY));
OH_AVFormat_SetIntValue(format, OH_MD_KEY_TRANSFER_CHARACTERISTICS, static_cast<int32_t>(OH_TransferCharacteristic::TRANSFER_CHARACTERISTIC_BT709));
OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_ENCODE_BITRATE_MODE, static_cast<int32_t>(OH_VideoEncodeBitrateMode::CBR));
}
void acgist::TaoyaoVideoEncoder::restart() {
std::lock_guard<std::recursive_mutex> videoLock(videoMutex);
OH_AVErrCode ret = OH_VideoEncoder_Flush(this->avCodec);
OH_LOG_INFO(LOG_APP, "清空编码队列:%o", ret);
OH_LOG_INFO(LOG_APP, "清空视频编码队列:%o", ret);
ret = OH_VideoEncoder_Start(this->avCodec);
OH_LOG_INFO(LOG_APP, "开始视频编码:%o", ret);
}
void acgist::TaoyaoVideoEncoder::reset(OH_AVFormat* format) {
std::lock_guard<std::recursive_mutex> videoLock(videoMutex);
OH_AVErrCode ret = OH_VideoEncoder_Reset(this->avCodec);
OH_LOG_INFO(LOG_APP, "重置视频编码:%o", ret);
ret = OH_VideoEncoder_Configure(this->avCodec, format);
@@ -91,6 +84,7 @@ void acgist::TaoyaoVideoEncoder::reset(OH_AVFormat* format) {
}
void acgist::TaoyaoVideoEncoder::resetIntConfig(const char* key, int32_t value) {
std::lock_guard<std::recursive_mutex> videoLock(videoMutex);
OH_AVFormat* format = OH_AVFormat_Create();
OH_AVFormat_SetIntValue(format, key, value);
OH_AVErrCode ret = OH_VideoEncoder_SetParameter(this->avCodec, format);
@@ -99,6 +93,7 @@ void acgist::TaoyaoVideoEncoder::resetIntConfig(const char* key, int32_t value)
}
void acgist::TaoyaoVideoEncoder::resetLongConfig(const char* key, int64_t value) {
std::lock_guard<std::recursive_mutex> videoLock(videoMutex);
OH_AVFormat* format = OH_AVFormat_Create();
OH_AVFormat_SetLongValue(format, key, value);
OH_AVErrCode ret = OH_VideoEncoder_SetParameter(this->avCodec, format);
@@ -107,6 +102,7 @@ void acgist::TaoyaoVideoEncoder::resetLongConfig(const char* key, int64_t value)
}
void acgist::TaoyaoVideoEncoder::resetDoubleConfig(const char* key, double value) {
std::lock_guard<std::recursive_mutex> videoLock(videoMutex);
OH_AVFormat* format = OH_AVFormat_Create();
OH_AVFormat_SetDoubleValue(format, key, value);
OH_AVErrCode ret = OH_VideoEncoder_SetParameter(this->avCodec, format);
@@ -115,6 +111,7 @@ void acgist::TaoyaoVideoEncoder::resetDoubleConfig(const char* key, double value
}
bool acgist::TaoyaoVideoEncoder::start() {
std::lock_guard<std::recursive_mutex> videoLock(videoMutex);
if(this->running) {
return true;
}
@@ -125,20 +122,23 @@ bool acgist::TaoyaoVideoEncoder::start() {
}
bool acgist::TaoyaoVideoEncoder::stop() {
std::lock_guard<std::recursive_mutex> videoLock(videoMutex);
if(!this->running) {
return true;
}
this->running = false;
// Buffer模式
OH_AVErrCode ret = OH_VideoEncoder_Stop(this->avCodec);
OH_LOG_INFO(LOG_APP, "结束视频编码:%o", ret);
// Surface模式
// OH_AVErrCode ret = OH_VideoEncoder_NotifyEndOfStream(this->avCodec);
// OH_LOG_INFO(LOG_APP, "通知视频编码结束:%o", ret);
OH_AVErrCode ret = OH_VideoEncoder_Stop(this->avCodec);
OH_LOG_INFO(LOG_APP, "结束视频编码:%o", ret);
return ret == OH_AVErrCode::AV_ERR_OK;
}
int32_t acgist::TaoyaoVideoEncoder::Release() {
// TODO: 释放资源
// TODO: 释放
delete this;
return 0;
}
@@ -158,20 +158,19 @@ webrtc::VideoEncoder::EncoderInfo acgist::TaoyaoVideoEncoder::GetEncoderInfo() c
}
int32_t acgist::TaoyaoVideoEncoder::Encode(const webrtc::VideoFrame& videoFrame, const std::vector<webrtc::VideoFrameType>* frame_types) {
// frameSize = videoFrame.width * height * 3 / 2
// 配置buffer info信息
OH_AVCodecBufferAttr info;
info.size = 0; // TODO
info.size = videoFrame.width() * videoFrame.height() * 3 / 2;
info.offset = 0;
info.pts = 0;
info.flags = 0; // TODO frame_types
info.flags = 0;
// TODO: videoFrame.video_frame_buffer->
OH_AVErrCode ret = OH_AVBuffer_SetBufferAttr(this->buffer, &info);
ret = OH_VideoEncoder_PushInputBuffer(this->avCodec, index);
return 0;
}
static void OnError(OH_AVCodec* codec, int32_t errorCode, void* userData) {
OH_LOG_WARN(LOG_APP, "视频编码发送错误:%d", errorCode);
OH_LOG_ERROR(LOG_APP, "视频编码发送错误:%d", errorCode);
}
static void OnStreamChanged(OH_AVCodec* codec, OH_AVFormat* format, void* userData) {
@@ -210,6 +209,11 @@ static void OnNewOutputBuffer(OH_AVCodec* codec, uint32_t index, OH_AVBuffer* bu
OH_AVCodecBufferAttr info;
OH_AVErrCode ret = OH_AVBuffer_GetBufferAttr(buffer, &info);
char* data = reinterpret_cast<char*>(OH_AVBuffer_GetAddr(buffer));
// webrtc::VideoFrameType flags =
// info.flags == AVCODEC_BUFFER_FLAGS_SYNC_FRAME ? webrtc::VideoFrameType::kVideoFrameKey :
// info.flags == AVCODEC_BUFFER_FLAGS_INCOMPLETE_FRAME ? webrtc::VideoFrameType::kVideoFrameDelta :
// webrtc::VideoFrameType::kEmptyFrame;
// frame_types->push_back(std::move(flags));
// videoEncoder->encodedImageCallback
// TODO: 继续处理
ret = OH_VideoEncoder_FreeOutputBuffer(codec, index);

View File

@@ -1,9 +1,10 @@
export const init: (
json : string,
push : (signal: string, body: string, id: number) => void,
request: (signal: string, body: string, id: number) => Promise<string>
request: (signal: string, body: string, id: number) => void
) => number;
export const shutdown : (json: string) => number;
export const callback : (json: string) => number;
export const roomClose : (json: string) => number;
export const roomEnter : (json: string) => number;
export const roomExpel : (json: string) => number;

View File

@@ -1,4 +1,4 @@
import { signal } from "../taoyao/TaoyaoSignal";
import { taoyaoSignal } from "../taoyao/TaoyaoSignal";
import { setting } from "../taoyao/Setting";
@@ -13,7 +13,7 @@ struct Index {
.fontSize(20)
.fontWeight(FontWeight.Bold)
.onClick(() => {
signal.connect();
taoyaoSignal.connect();
});
}
.width("100%")
@@ -23,7 +23,7 @@ struct Index {
.fontSize(20)
.fontWeight(FontWeight.Bold)
.onClick(() => {
signal.close();
taoyaoSignal.close();
});
}
.width("100%")
@@ -33,7 +33,7 @@ struct Index {
.fontSize(20)
.fontWeight(FontWeight.Bold)
.onClick(() => {
signal.init();
taoyaoSignal.init();
});
}
.width("100%")
@@ -43,7 +43,7 @@ struct Index {
.fontSize(20)
.fontWeight(FontWeight.Bold)
.onClick(() => {
signal.shutdown();
taoyaoSignal.shutdown();
});
}
.width("100%")

View File

@@ -5,34 +5,37 @@
*/
import fs from '@ohos.file.fs';
import resourceManager from '@ohos.resourceManager';
class Signal {
class Config {
// 终端名称
name : string = "鸿蒙";
name : string = "鸿蒙";
// 终端ID
clientId : string = "harmony";
clientId : string = "harmony";
// 终端类型
clientType: string = "MOBILE";
clientType : string = "MOBILE";
// 信令账号
username : string = "taoyao";
username : string = "taoyao";
// 信令密码
password : string = "taoyao";
password : string = "taoyao";
// 当前终端索引
clientIndex: number = 99999;
};
class Setting {
// 信令配置
config : Config = new Config();
// CA证书
caPath : string = "/cacert.pem";
// 信令版本
version: string = "1.0.0";
// 信令地址
// signalAddress: string = "wss://192.168.8.57:8888/websocket.signal";
signalAddress: string = "wss://192.168.1.100:8888/websocket.signal";
// 信令版本
version: string = "1.0.0";
// 信令配置
signal : Signal = new Signal();
// CA证书
caPath : string = "/cacert.pem";
// 启动自动加载系统
initOnLoad : boolean = true;
};

View File

@@ -24,21 +24,28 @@ class TaoyaoSignal {
heartbeatTimer: number = 0;
// 同步请求
callbackMapping = new Map<number, Function>();
// 当前消息索引
index : number = 0;
// 当前消息索引0-666
index : number = 0;
// 最小消息索引
minIndex: number = 0;
// 最大消息索引
maxIndex : number = 999;
// 当前终端索引
clientIndex: number = 99999;
maxIndex: number = 666;
/**
* 加载系统
* 建议注册成功以后加载
*/
init() {
hilog.info(0x0000, "TaoyaoSignal", "加载系统");
taoyaoModule.init(JSON.stringify(setting.signal), this.nativePush, this.nativeRequest);
taoyaoModule.init(JSON.stringify(setting.config), this.nativePush, this.nativeRequest);
}
/**
* 卸载系统
*/
shutdown() {
hilog.info(0x0000, "TaoyaoSignal", "卸载系统");
taoyaoModule.shutdown(JSON.stringify(setting.signal));
taoyaoModule.shutdown(JSON.stringify(setting.config));
}
/**
@@ -125,19 +132,22 @@ class TaoyaoSignal {
*/
async register() {
const response: Record<string, Object> = await this.request("client::register", {
"name" : setting.signal.name,
"clientId" : setting.signal.clientId,
"clientType": setting.signal.clientType,
"username" : setting.signal.username,
"password" : setting.signal.password,
"name" : setting.config.name,
"clientId" : setting.config.clientId,
"clientType": setting.config.clientType,
"username" : setting.config.username,
"password" : setting.config.password,
"battery" : 100,
"charging" : true
});
const body = response.body as Record<string, Object>;
const index = body.index as number;
this.clientIndex = index;
setting.config.clientIndex = index;
hilog.info(0x0000, "TaoyaoSignal", "信令注册成功:%{public}d", index);
this.heartbeat();
if(setting.initOnLoad) {
taoyaoSignal.init();
}
}
/**
@@ -160,15 +170,15 @@ class TaoyaoSignal {
*/
buildId(): number {
if (++this.index > this.maxIndex) {
this.index = 0;
this.index = this.minIndex;
}
const date = new Date();
return (
100000000000000 * date.getDate() +
1000000000000 * date.getHours() +
10000000000 * date.getMinutes() +
100000000 * date.getSeconds() +
1000 * this.clientIndex +
100000000000000 * date.getDate() +
1000000000000 * date.getHours() +
10000000000 * date.getMinutes() +
100000000 * date.getSeconds() +
1000 * setting.config.clientIndex +
this.index
);
}
@@ -323,7 +333,7 @@ class TaoyaoSignal {
* @param id ID
*/
nativePush(signal: string, body: string, id: number = 0) {
this.push(signal, JSON.parse(body), id);
taoyaoSignal.push(signal, JSON.parse(body), id);
}
/**
@@ -332,18 +342,21 @@ class TaoyaoSignal {
* @param signal 信令
* @param body 主体
* @param id ID
*
* @returns 响应
*/
async nativeRequest(signal: string, body: string, id: number = 0): Promise<string> {
const response = await this.request(signal, JSON.parse(body), id);
return JSON.stringify(response);
nativeRequest(signal: string, body: string, id: number = 0) {
taoyaoSignal.request(signal, JSON.parse(body), id).then(response => {
taoyaoModule.callback(JSON.stringify(response));
});
}
}
const signal = new TaoyaoSignal();
const taoyaoSignal = new TaoyaoSignal();
if(setting.initOnLoad) {
taoyaoSignal.connect();
}
export {
signal
taoyaoSignal
}