diff --git a/taoyao-client-openharmony/taoyao/media/src/main/cpp/CMakeLists.txt b/taoyao-client-openharmony/taoyao/media/src/main/cpp/CMakeLists.txt index ec45a49..daa16f2 100644 --- a/taoyao-client-openharmony/taoyao/media/src/main/cpp/CMakeLists.txt +++ b/taoyao-client-openharmony/taoyao/media/src/main/cpp/CMakeLists.txt @@ -40,6 +40,7 @@ add_library( media/Capturer.cpp media/AudioCapturer.cpp media/VideoCapturer.cpp + media/VideoEncoder.cpp media/MediaManager.cpp ) diff --git a/taoyao-client-openharmony/taoyao/media/src/main/cpp/bind.cpp b/taoyao-client-openharmony/taoyao/media/src/main/cpp/bind.cpp index d4c22d4..ca5150c 100644 --- a/taoyao-client-openharmony/taoyao/media/src/main/cpp/bind.cpp +++ b/taoyao-client-openharmony/taoyao/media/src/main/cpp/bind.cpp @@ -1,5 +1,7 @@ /** - * 方法绑定 + * NAPI(NODE-API) + * + * https://docs.openharmony.cn/pages/v4.1/zh-cn/application-dev/reference/native-lib/napi.md */ #include @@ -71,7 +73,7 @@ static void init() { /** * 卸载系统 */ -static napi_value shutdown(napi_env env, napi_callback_info info) { +static napi_value shutdown(napi_env env, napi_callback_info info) { if(!initTaoyao) { OH_LOG_INFO(LOG_APP, "已经卸载libtaoyao"); return 0; @@ -80,7 +82,13 @@ static napi_value shutdown(napi_env env, napi_callback_info info) { OH_LOG_INFO(LOG_APP, "卸载libtaoyao"); OH_LOG_INFO(LOG_APP, "释放mediasoupclient"); mediasoupclient::Cleanup(); -// this->roomMap + // 删除房间 + for(auto iterator = roomMap.begin(); iterator != roomMap.end(); ++iterator) { + delete iterator->second; + iterator->second = nullptr; + } + roomMap.clear(); + // 关闭媒体 if (mediaManager != nullptr) { delete mediaManager; mediaManager = nullptr; @@ -90,6 +98,9 @@ static napi_value shutdown(napi_env env, napi_callback_info info) { return 0; } +/** + * 发送消息 + */ static void send(const std::string& signal, const std::string& body) { napi_value ret; napi_value callback = nullptr; @@ -101,6 +112,9 @@ static void send(const std::string& signal, const std::string& body) { // napi_get_undefined(acgist::env, &ret); } +/** + * 发送请求 + */ static std::string request(const std::string& signal, const std::string& body) { napi_value ret; napi_value callback = nullptr; @@ -115,34 +129,27 @@ static std::string request(const std::string& signal, const std::string& body) { return chars; } -static napi_value mediaConsume(napi_env env, napi_callback_info info) { return 0; } - -static napi_value mediaConsumerClose(napi_env env, napi_callback_info info) { return 0; } - -static napi_value mediaConsumerPause(napi_env env, napi_callback_info info) { return 0; } - -static napi_value mediaConsumerResume(napi_env env, napi_callback_info info) { return 0; } - -static napi_value mediaProducerClose(napi_env env, napi_callback_info info) { return 0; } - -static napi_value mediaProducerPause(napi_env env, napi_callback_info info) { return 0; } - -static napi_value mediaProducerResume(napi_env env, napi_callback_info info) { return 0; } - -static napi_value roomClientList(napi_env env, napi_callback_info info) { return 0; } - -static napi_value roomClose(napi_env env, napi_callback_info info) { return 0; } +/** + * 房间关闭 + */ +static napi_value roomClose(napi_env env, napi_callback_info info) { return 0; } /** - * 其他终端进入房间 + * 进入房间 + * 其他终端进入房间,自己进入房间逻辑参考房间邀请。 */ static napi_value roomEnter(napi_env env, napi_callback_info info) { return 0; } -static napi_value roomExpel(napi_env env, napi_callback_info info) { return 0; } +/** + * 踢出房间 + * 踢出房间以后终端离开房间 + */ +static napi_value roomExpel(napi_env env, napi_callback_info info) { return 0; } /** + * 房间邀请 * 邀请终端进入房间 */ static napi_value roomInvite(napi_env env, napi_callback_info info) { @@ -174,8 +181,56 @@ static napi_value roomInvite(napi_env env, napi_callback_info info) { return ret; } -static napi_value roomLeave(napi_env env, napi_callback_info info) { return 0; } +/** + * 离开房间 + * 其他终端离开房间 + */ +static napi_value roomLeave(napi_env env, napi_callback_info info) { return 0; } +/** + * 终端列表 + * 房间所有终端列表首次进入方便加载终端列表信息 + */ +static napi_value roomClientList(napi_env env, napi_callback_info info) { return 0; } + +/** + * 媒体消费(被动通知) + */ +static napi_value mediaConsume(napi_env env, napi_callback_info info) { return 0; } + +/** + * 消费者关闭(被动通知) + */ +static napi_value mediaConsumerClose(napi_env env, napi_callback_info info) { return 0; } + +/** + * 消费者暂停(被动通知) + */ +static napi_value mediaConsumerPause(napi_env env, napi_callback_info info) { return 0; } + +/** + * 消费者恢复(被动通知) + */ +static napi_value mediaConsumerResume(napi_env env, napi_callback_info info) { return 0; } + +/** + * 生产者关闭(被动通知) + */ +static napi_value mediaProducerClose(napi_env env, napi_callback_info info) { return 0; } + +/** + * 生产者暂停(被动通知) + */ +static napi_value mediaProducerPause(napi_env env, napi_callback_info info) { return 0; } + +/** + * 生产者恢复(被动通知) + */ +static napi_value mediaProducerResume(napi_env env, napi_callback_info info) { return 0; } + +/** + * 注册发送回调 + */ static napi_value registerSend(napi_env env, napi_callback_info info) { size_t argc = 1; napi_value args[1] = { nullptr }; @@ -184,7 +239,10 @@ static napi_value registerSend(napi_env env, napi_callback_info info) { return 0; } -static napi_value registerRequest(napi_env env, napi_callback_info info) { +/** + * 注册请求回调 + */ +static napi_value registerRequest(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); @@ -200,6 +258,13 @@ EXTERN_C_START static napi_value Init(napi_env env, napi_value exports) { acgist::env = env; napi_property_descriptor desc[] = { + { "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 }, + { "roomExpel", nullptr, acgist::roomExpel, nullptr, nullptr, nullptr, napi_default, nullptr }, + { "roomInvite", nullptr, acgist::roomInvite, nullptr, nullptr, nullptr, napi_default, nullptr }, + { "roomLeave", nullptr, acgist::roomLeave, nullptr, nullptr, nullptr, napi_default, nullptr }, + { "roomClientList", nullptr, acgist::roomClientList, nullptr, nullptr, nullptr, napi_default, nullptr }, { "mediaConsume", nullptr, acgist::mediaConsume, nullptr, nullptr, nullptr, napi_default, nullptr }, { "mediaConsumerClose", nullptr, acgist::mediaConsumerClose, nullptr, nullptr, nullptr, napi_default, nullptr }, { "mediaConsumerPause", nullptr, acgist::mediaConsumerPause, nullptr, nullptr, nullptr, napi_default, nullptr }, @@ -207,13 +272,6 @@ static napi_value Init(napi_env env, napi_value exports) { { "mediaProducerClose", nullptr, acgist::mediaProducerClose, nullptr, nullptr, nullptr, napi_default, nullptr }, { "mediaProducerPause", nullptr, acgist::mediaProducerPause, nullptr, nullptr, nullptr, napi_default, nullptr }, { "mediaProducerResume", nullptr, acgist::mediaProducerResume, nullptr, nullptr, nullptr, napi_default, nullptr }, - { "roomClientList", nullptr, acgist::roomClientList, 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 }, - { "roomExpel", nullptr, acgist::roomExpel, nullptr, nullptr, nullptr, napi_default, nullptr }, - { "roomInvite", nullptr, acgist::roomInvite, nullptr, nullptr, nullptr, napi_default, nullptr }, - { "roomLeave", nullptr, acgist::roomLeave, nullptr, nullptr, nullptr, napi_default, nullptr }, - { "shutdown", nullptr, acgist::shutdown, nullptr, nullptr, nullptr, napi_default, nullptr }, { "registerSend", nullptr, acgist::registerSend, nullptr, nullptr, nullptr, napi_default, nullptr }, { "registerRequest", nullptr, acgist::registerRequest, nullptr, nullptr, nullptr, napi_default, nullptr }, }; diff --git a/taoyao-client-openharmony/taoyao/media/src/main/cpp/include/Capturer.hpp b/taoyao-client-openharmony/taoyao/media/src/main/cpp/include/Capturer.hpp index e7a67f0..dfa68ee 100644 --- a/taoyao-client-openharmony/taoyao/media/src/main/cpp/include/Capturer.hpp +++ b/taoyao-client-openharmony/taoyao/media/src/main/cpp/include/Capturer.hpp @@ -3,16 +3,15 @@ * * @author acgist * - * https://docs.openharmony.cn/pages/v4.0/zh-cn/application-dev/media/audio-encoding.md - * https://docs.openharmony.cn/pages/v4.0/zh-cn/application-dev/media/video-encoding.md + * https://docs.openharmony.cn/pages/v4.1/zh-cn/application-dev/media/avcodec/audio-encoding.md + * https://docs.openharmony.cn/pages/v4.1/zh-cn/application-dev/media/avcodec/video-encoding.md * https://docs.openharmony.cn/pages/v4.1/zh-cn/application-dev/media/media/avscreen-capture.md - * https://docs.openharmony.cn/pages/v4.0/zh-cn/application-dev/media/obtain-supported-codecs.md + * https://docs.openharmony.cn/pages/v4.1/zh-cn/application-dev/reference/native-lib/opengles.md + * https://docs.openharmony.cn/pages/v4.1/zh-cn/application-dev/reference/native-lib/opensles.md + * https://docs.openharmony.cn/pages/v4.1/zh-cn/application-dev/media/avcodec/obtain-supported-codecs.md * 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 - * https://docs.openharmony.cn/pages/v4.0/zh-cn/application-dev/reference/native-lib/third_party_opengl/opengles.md - * https://docs.openharmony.cn/pages/v4.0/zh-cn/application-dev/reference/native-lib/third_party_opensles/opensles.md */ - #ifndef taoyao_Capturer_HPP #define taoyao_Capturer_HPP @@ -26,6 +25,8 @@ #include #include +#include + namespace acgist { /** @@ -37,6 +38,7 @@ template class Capturer { public: + // rtc::scoped_refptr std::map map; public: @@ -44,6 +46,10 @@ public: virtual ~Capturer(); public: + // 添加管道 + virtual bool add(const std::string& id, Sink* sink); + // 删除管道 + virtual bool remove(const std::string& id); // 开始采集 virtual bool start() = 0; // 结束采集 @@ -51,6 +57,38 @@ public: }; +template +acgist::Capturer::Capturer() {} + +template +acgist::Capturer::~Capturer() { + for (auto iterator = this->map.begin(); iterator != this->map.end(); ++iterator) { + // TODO:释放 +// delete iterator->second; +// iterator->second = nullptr; + } + this->map.clear(); +} + +template +bool acgist::Capturer::add(const std::string& id, Sink* sink) { + this->map.insert(std::make_pair(id, sink)); + return true; +} + +template +bool acgist::Capturer::remove(const std::string& id) { + auto iterator = this->map.find(id); + if (iterator == this->map.end()) { + return false; + } + // TODO:释放 +// delete iterator->second; +// iterator->second = nullptr; + this->map.erase(iterator); + return true; +} + /** * 音频采集器 */ @@ -67,30 +105,69 @@ public: virtual ~AudioCapturer(); public: - virtual bool start(); - virtual bool stop(); + virtual bool start() override; + virtual bool stop() override; }; +/** + * 视频编码 + */ +class VideoEncoder { + +public: + // 视频编码器 + OH_AVCodec* avCodec; + // 视频窗口 + OHNativeWindow* nativeWindow; + +public: + VideoEncoder(); + virtual ~VideoEncoder(); + +public: + // 重新开始 + void restart(); + // 动态配置 + void reset(OH_AVFormat *format); + // 动态配置 + void resetIntConfig(const char* key, int32_t value); + // 动态配置 + void resetLongConfig(const char* key, int64_t value); + // 动态配置 + void resetDoubleConfig(const char* key, double value); + // 开始编码 + bool start(); + // 结束编码 + bool stop(); +}; + /** * 视频采集器 */ -class VideoCapturer: public Capturer> { +class VideoCapturer: public Capturer> { public: - Camera_Device* camera_Device; - Camera_Manager* camera_Manager; - Camera_VideoOutput* camera_VideoOutput; - Camera_VideoProfile* camera_VideoProfile; - Camera_OutputCapability* camera_OutputCapability; + // 摄像头设备数量 + uint32_t size = 0; + // 摄像头索引 + uint32_t cameraIndex = 0; + // 摄像头设备列表 + Camera_Device* cameraDevice = nullptr; + // 摄像头管理器 + Camera_Manager* cameraManager = nullptr; + // 摄像头视频输出 + Camera_VideoOutput* cameraVideoOutput = nullptr; + // 摄像头输出能力 + Camera_OutputCapability* cameraOutputCapability = nullptr; public: VideoCapturer(); virtual ~VideoCapturer(); public: - virtual bool start(); - virtual bool stop(); + virtual bool start() override; + virtual bool stop() override; }; } diff --git a/taoyao-client-openharmony/taoyao/media/src/main/cpp/include/Client.hpp b/taoyao-client-openharmony/taoyao/media/src/main/cpp/include/Client.hpp index 70e9b36..42eabdf 100644 --- a/taoyao-client-openharmony/taoyao/media/src/main/cpp/include/Client.hpp +++ b/taoyao-client-openharmony/taoyao/media/src/main/cpp/include/Client.hpp @@ -3,7 +3,6 @@ * * @author acgist */ - #ifndef taoyao_Client_HPP #define taoyao_Client_HPP diff --git a/taoyao-client-openharmony/taoyao/media/src/main/cpp/include/MediaManager.hpp b/taoyao-client-openharmony/taoyao/media/src/main/cpp/include/MediaManager.hpp index 3ccff9a..154bbb7 100644 --- a/taoyao-client-openharmony/taoyao/media/src/main/cpp/include/MediaManager.hpp +++ b/taoyao-client-openharmony/taoyao/media/src/main/cpp/include/MediaManager.hpp @@ -4,9 +4,8 @@ * * @author acgist * - * https://docs.openharmony.cn/pages/v4.0/zh-cn/application-dev/media/obtain-supported-codecs.md + * 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 diff --git a/taoyao-client-openharmony/taoyao/media/src/main/cpp/include/Palyer.hpp b/taoyao-client-openharmony/taoyao/media/src/main/cpp/include/Palyer.hpp index 06c8ee9..97bc365 100644 --- a/taoyao-client-openharmony/taoyao/media/src/main/cpp/include/Palyer.hpp +++ b/taoyao-client-openharmony/taoyao/media/src/main/cpp/include/Palyer.hpp @@ -5,7 +5,6 @@ * 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_H #define TAOYAO_PALYER_H diff --git a/taoyao-client-openharmony/taoyao/media/src/main/cpp/include/Room.hpp b/taoyao-client-openharmony/taoyao/media/src/main/cpp/include/Room.hpp index bf91453..40ffe61 100644 --- a/taoyao-client-openharmony/taoyao/media/src/main/cpp/include/Room.hpp +++ b/taoyao-client-openharmony/taoyao/media/src/main/cpp/include/Room.hpp @@ -3,7 +3,6 @@ * * @author acgist */ - #ifndef taoyao_Room_HPP #define taoyao_Room_HPP diff --git a/taoyao-client-openharmony/taoyao/media/src/main/cpp/media/AudioCapturer.cpp b/taoyao-client-openharmony/taoyao/media/src/main/cpp/media/AudioCapturer.cpp index 21ee1ad..d1d9c12 100644 --- a/taoyao-client-openharmony/taoyao/media/src/main/cpp/media/AudioCapturer.cpp +++ b/taoyao-client-openharmony/taoyao/media/src/main/cpp/media/AudioCapturer.cpp @@ -19,23 +19,22 @@ static int32_t OnStreamEvent(OH_AudioCapturer* capturer, void* userData, OH_Audi static int32_t OnInterruptEvent(OH_AudioCapturer* capturer, void* userData, OH_AudioInterrupt_ForceType type, OH_AudioInterrupt_Hint hint); acgist::AudioCapturer::AudioCapturer() { - OH_AudioStream_Result ret = OH_AudioStreamBuilder_Create(&builder, AUDIOSTREAM_TYPE_RENDERER); + OH_AudioStream_Result ret = OH_AudioStreamBuilder_Create(&this->builder, AUDIOSTREAM_TYPE_RENDERER); OH_LOG_INFO(LOG_APP, "构造音频采集:%o", ret); } acgist::AudioCapturer::~AudioCapturer() { - OH_AudioStream_Result ret = OH_AudioStreamBuilder_Destroy(builder); - // TODO: 是否需要delete - builder = nullptr; + OH_AudioStream_Result ret = OH_AudioStreamBuilder_Destroy(this->builder); + this->builder = nullptr; OH_LOG_INFO(LOG_APP, "释放音频采集:%o", ret); } bool acgist::AudioCapturer::start() { // 配置采集参数 - OH_AudioStreamBuilder_SetSamplingRate(builder, samplingRate); - OH_AudioStreamBuilder_SetChannelCount(builder, channelCount); - OH_AudioStreamBuilder_SetLatencyMode(builder, latencyMode); - OH_AudioStreamBuilder_SetSampleFormat(builder, sampleFormat); + OH_AudioStreamBuilder_SetSamplingRate(this->builder, samplingRate); + OH_AudioStreamBuilder_SetChannelCount(this->builder, channelCount); + OH_AudioStreamBuilder_SetLatencyMode(this->builder, latencyMode); + OH_AudioStreamBuilder_SetSampleFormat(this->builder, sampleFormat); OH_LOG_DEBUG(LOG_APP, "配置音频格式:%d %d %o %o", samplingRate, channelCount, latencyMode, sampleFormat); // 设置回调函数 OH_AudioCapturer_Callbacks callbacks; @@ -43,24 +42,24 @@ bool acgist::AudioCapturer::start() { callbacks.OH_AudioCapturer_OnReadData = OnReadData; callbacks.OH_AudioCapturer_OnStreamEvent = OnStreamEvent; callbacks.OH_AudioCapturer_OnInterruptEvent = OnInterruptEvent; - OH_AudioStream_Result ret = OH_AudioStreamBuilder_SetCapturerCallback(builder, callbacks, this); + OH_AudioStream_Result ret = OH_AudioStreamBuilder_SetCapturerCallback(this->builder, callbacks, this); OH_LOG_DEBUG(LOG_APP, "设置回调函数:%o", ret); // 构造音频采集器 - ret = OH_AudioStreamBuilder_GenerateCapturer(builder, &audioCapturer); + ret = OH_AudioStreamBuilder_GenerateCapturer(this->builder, &this->audioCapturer); OH_LOG_DEBUG(LOG_APP, "构造音频采集器:%o", ret); // 开始录制 - ret = OH_AudioCapturer_Start(audioCapturer); + ret = OH_AudioCapturer_Start(this->audioCapturer); OH_LOG_DEBUG(LOG_APP, "开始录制:%o", ret); return ret == OH_AudioStream_Result::AUDIOSTREAM_SUCCESS; } bool acgist::AudioCapturer::stop() { // 停止录制 - OH_AudioStream_Result ret = OH_AudioCapturer_Stop(audioCapturer); + OH_AudioStream_Result ret = OH_AudioCapturer_Stop(this->audioCapturer); OH_LOG_DEBUG(LOG_APP, "停止录制:%o", ret); // 释放音频采集器 - ret = OH_AudioCapturer_Release(audioCapturer); - audioCapturer = nullptr; + ret = OH_AudioCapturer_Release(this->audioCapturer); + this->audioCapturer = nullptr; OH_LOG_DEBUG(LOG_APP, "释放音频采集器:%o", ret); return ret == OH_AudioStream_Result::AUDIOSTREAM_SUCCESS; } @@ -73,11 +72,7 @@ static int32_t OnError(OH_AudioCapturer* capturer, void* userData, OH_AudioStrea static int32_t OnReadData(OH_AudioCapturer* capturer, void* userData, void* buffer, int32_t length) { acgist::AudioCapturer* audioCapturer = (acgist::AudioCapturer*) userData; int64_t timeMillis = rtc::TimeMillis(); - for ( - auto iterator = audioCapturer->map.begin(); - iterator != audioCapturer->map.end(); - ++iterator - ) { + for (auto iterator = audioCapturer->map.begin(); iterator != audioCapturer->map.end(); ++iterator) { iterator->second->OnData(buffer, 16, samplingRate, channelCount, sizeof(buffer) / 2, timeMillis); } return 0; diff --git a/taoyao-client-openharmony/taoyao/media/src/main/cpp/media/Capturer.cpp b/taoyao-client-openharmony/taoyao/media/src/main/cpp/media/Capturer.cpp index e69de29..783e3af 100644 --- a/taoyao-client-openharmony/taoyao/media/src/main/cpp/media/Capturer.cpp +++ b/taoyao-client-openharmony/taoyao/media/src/main/cpp/media/Capturer.cpp @@ -0,0 +1 @@ +#include "../include/Capturer.hpp" diff --git a/taoyao-client-openharmony/taoyao/media/src/main/cpp/media/MediaManager.cpp b/taoyao-client-openharmony/taoyao/media/src/main/cpp/media/MediaManager.cpp index a73ba53..0b47711 100644 --- a/taoyao-client-openharmony/taoyao/media/src/main/cpp/media/MediaManager.cpp +++ b/taoyao-client-openharmony/taoyao/media/src/main/cpp/media/MediaManager.cpp @@ -65,6 +65,10 @@ bool acgist::MediaManager::startAudioCapture() { return true; } +bool acgist::MediaManager::startVideoCapture() { + return true; +} + rtc::scoped_refptr acgist::MediaManager::getAudioTrack() { cricket::AudioOptions options; options.highpass_filter = true; diff --git a/taoyao-client-openharmony/taoyao/media/src/main/cpp/media/VideoCapturer.cpp b/taoyao-client-openharmony/taoyao/media/src/main/cpp/media/VideoCapturer.cpp index 6d5efe5..41b060a 100644 --- a/taoyao-client-openharmony/taoyao/media/src/main/cpp/media/VideoCapturer.cpp +++ b/taoyao-client-openharmony/taoyao/media/src/main/cpp/media/VideoCapturer.cpp @@ -1,8 +1,74 @@ +/** + * https://docs.openharmony.cn/pages/v4.1/zh-cn/application-dev/reference/apis-camera-kit/camera_8h.md + * https://docs.openharmony.cn/pages/v4.1/zh-cn/application-dev/reference/apis-camera-kit/video__output_8h.md + * https://docs.openharmony.cn/pages/v4.1/zh-cn/application-dev/reference/apis-camera-kit/camera__manager_8h.md + * https://docs.openharmony.cn/pages/v4.1/zh-cn/application-dev/reference/apis-camera-kit/capture__session_8h.md + */ #include "../include/Capturer.hpp" #include "hilog/log.h" + #include "ohcamera/camera_input.h" #include "ohcamera/camera_manager.h" #include "ohcamera/capture_session.h" +static void onError(Camera_VideoOutput* videoOutput, Camera_ErrorCode errorCode); +static void onFrameEnd(Camera_VideoOutput* videoOutput, int32_t frameCount); +static void onFrameStart(Camera_VideoOutput* videoOutput); +acgist::VideoCapturer::VideoCapturer() { + Camera_ErrorCode ret = OH_Camera_GetCameraManager(&this->cameraManager); + OH_LOG_INFO(LOG_APP, "构造摄像头管理器:%o", ret); + ret = OH_CameraManager_GetSupportedCameras(this->cameraManager, &this->cameraDevice, &this->size); + OH_LOG_INFO(LOG_APP, "摄像头设备列表:%o %d", ret, size); + 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); + // 二次处理:createImageReceiver/getReceivingSurfaceId +// ret = OH_CameraManager_CreateVideoOutput(this->cameraManager, this->cameraOutputCapability->videoProfiles[0], +// this->nativeWindow, &this->cameraVideoOutput); +} + +acgist::VideoCapturer::~VideoCapturer() { + Camera_ErrorCode ret = OH_VideoOutput_Release(this->cameraVideoOutput); + this->cameraVideoOutput = nullptr; + OH_LOG_INFO(LOG_APP, "释放摄像头视频输出:%o", ret); + ret = OH_CameraManager_DeleteSupportedCameraOutputCapability(this->cameraManager, this->cameraOutputCapability); + this->cameraOutputCapability = nullptr; + OH_LOG_INFO(LOG_APP, "释放摄像头输出能力:%o", ret); + ret = OH_CameraManager_DeleteSupportedCameras(this->cameraManager, this->cameraDevice, this->size); + this->cameraDevice = nullptr; + OH_LOG_INFO(LOG_APP, "释放摄像头设备列表:%o", ret); + ret = OH_Camera_DeleteCameraManager(this->cameraManager); + this->cameraManager = nullptr; + OH_LOG_INFO(LOG_APP, "释放摄像头管理器:%o", ret); +} + +bool acgist::VideoCapturer::start() { + Camera_ErrorCode ret = OH_VideoOutput_Start(this->cameraVideoOutput); + OH_LOG_INFO(LOG_APP, "开始视频捕获:%o", ret); + VideoOutput_Callbacks callbacks; + callbacks.onError = onError; + callbacks.onFrameEnd = onFrameEnd; + callbacks.onFrameStart = onFrameStart; + ret = OH_VideoOutput_RegisterCallback(this->cameraVideoOutput, &callbacks); + OH_LOG_INFO(LOG_APP, "视频捕获回调:%o", ret); + return true; +} + +bool acgist::VideoCapturer::stop() { + Camera_ErrorCode ret = OH_VideoOutput_Stop(this->cameraVideoOutput); + OH_LOG_INFO(LOG_APP, "结束视频捕获:%o", ret); + return true; +} + +static void onError(Camera_VideoOutput* videoOutput, Camera_ErrorCode errorCode) { + OH_LOG_WARN(LOG_APP, "视频捕获数据帧失败:%d", errorCode); +} + +static void onFrameEnd(Camera_VideoOutput* videoOutput, int32_t frameCount) { + OH_LOG_DEBUG(LOG_APP, "结束视频捕获数据帧"); +} + +static void onFrameStart(Camera_VideoOutput* videoOutput) { + OH_LOG_DEBUG(LOG_APP, "开始视频捕获数据帧"); +} diff --git a/taoyao-client-openharmony/taoyao/media/src/main/cpp/media/VideoEncoder.cpp b/taoyao-client-openharmony/taoyao/media/src/main/cpp/media/VideoEncoder.cpp new file mode 100644 index 0000000..42ca9b7 --- /dev/null +++ b/taoyao-client-openharmony/taoyao/media/src/main/cpp/media/VideoEncoder.cpp @@ -0,0 +1,164 @@ +#include "../include/Capturer.hpp" + +#include "hilog/log.h" + +#include "rtc_base/time_utils.h" + +#include "api/video/nv12_buffer.h" +#include "api/video/i420_buffer.h" + +#include +#include +#include +#include + +static uint32_t width = 720; +static uint32_t height = 480; +static uint64_t bitrate = 3'000'000L; +static double frameRate = 30.0; + +static void OnError(OH_AVCodec* codec, int32_t errorCode, void* userData); +static void OnStreamChanged(OH_AVCodec* codec, OH_AVFormat* format, void* userData); +static void OnNeedInputBuffer(OH_AVCodec* codec, uint32_t index, OH_AVBuffer* buffer, void* userData); +static void OnNewOutputBuffer(OH_AVCodec* codec, uint32_t index, OH_AVBuffer* buffer, void* userData); + +acgist::VideoEncoder::VideoEncoder() { + 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_AVCodecCallback callback = { &OnError, &OnStreamChanged, &OnNeedInputBuffer, &OnNewOutputBuffer }; + OH_AVErrCode ret = OH_VideoEncoder_RegisterCallback(this->avCodec, callback, this); + OH_LOG_INFO(LOG_APP, "注册编码回调:%o", ret); + // 配置编码参数:https://docs.openharmony.cn/pages/v4.1/zh-cn/application-dev/media/avcodec/video-encoding.md + OH_AVFormat* format = OH_AVFormat_Create(); + // 配置视频帧宽度(必须) + OH_AVFormat_SetIntValue(format, OH_MD_KEY_WIDTH, width); + // 配置视频帧高度(必须) + OH_AVFormat_SetIntValue(format, OH_MD_KEY_HEIGHT, height); + // 配置视频颜色格式(必须) + OH_AVFormat_SetIntValue(format, OH_MD_KEY_PIXEL_FORMAT, AV_PIXEL_FORMAT_YUVI420); + // OH_AVFormat_SetIntValue(format, OH_MD_KEY_PIXEL_FORMAT, AV_PIXEL_FORMAT_NV12); + // 配置视频帧速率 + OH_AVFormat_SetDoubleValue(format, OH_MD_KEY_FRAME_RATE, frameRate); + // 配置视频YUV值范围标志 + OH_AVFormat_SetIntValue(format, OH_MD_KEY_RANGE_FLAG, false); + // 配置视频原色 + OH_AVFormat_SetIntValue(format, OH_MD_KEY_COLOR_PRIMARIES, static_cast(OH_ColorPrimary::COLOR_PRIMARY_BT709)); + // 配置传输特性 + OH_AVFormat_SetIntValue(format, OH_MD_KEY_TRANSFER_CHARACTERISTICS, static_cast(OH_TransferCharacteristic::TRANSFER_CHARACTERISTIC_BT709)); + // 配置最大矩阵系数 + OH_AVFormat_SetIntValue(format, OH_MD_KEY_MATRIX_COEFFICIENTS, static_cast(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(OH_AVCProfile::AVC_PROFILE_BASELINE)); + // 配置编码比特率模式 + OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_ENCODE_BITRATE_MODE, static_cast(OH_VideoEncodeBitrateMode::CBR)); + // 配置比特率 + OH_AVFormat_SetLongValue(format, OH_MD_KEY_BITRATE, bitrate); + // 配置所需的编码质量:只有在恒定质量模式下配置的编码器才支持此配置 + OH_AVFormat_SetIntValue(format, OH_MD_KEY_QUALITY, 0); + ret = OH_VideoEncoder_Configure(this->avCodec, format); + OH_AVFormat_Destroy(format); + OH_LOG_INFO(LOG_APP, "配置编码参数:%o %d %d %f %ld", ret, width, height, frameRate, bitrate); + ret = OH_VideoEncoder_Prepare(this->avCodec); + OH_LOG_INFO(LOG_APP, "视频编码准备就绪:%o", ret); + ret = OH_VideoEncoder_GetSurface(this->avCodec, &this->nativeWindow); + OH_LOG_INFO(LOG_APP, "配置surface:%o", ret); +} + +acgist::VideoEncoder::~VideoEncoder() { + OH_AVErrCode ret = OH_VideoEncoder_Destroy(this->avCodec); + this->avCodec = nullptr; + OH_LOG_INFO(LOG_APP, "释放视频编码器:%o", ret); + // TODO: delete nativeWindow +} + +void acgist::VideoEncoder::restart() { + OH_AVErrCode ret = OH_VideoEncoder_Flush(this->avCodec); + OH_LOG_INFO(LOG_APP, "清空编码队列:%o", ret); + ret = OH_VideoEncoder_Start(this->avCodec); + OH_LOG_INFO(LOG_APP, "开始视频编码:%o", ret); +} + +void acgist::VideoEncoder::reset(OH_AVFormat* format) { + OH_AVErrCode ret = OH_VideoEncoder_Reset(this->avCodec); + OH_LOG_INFO(LOG_APP, "重置视频编码:%o", ret); + ret = OH_VideoEncoder_Configure(this->avCodec, format); + OH_LOG_INFO(LOG_APP, "配置视频编码:%o", ret); +} + +void acgist::VideoEncoder::resetIntConfig(const char* key, int32_t value) { + OH_AVFormat* format = OH_AVFormat_Create(); + OH_AVFormat_SetIntValue(format, key, value); + OH_AVErrCode ret = OH_VideoEncoder_SetParameter(this->avCodec, format); + OH_LOG_INFO(LOG_APP, "动态配置视频编码:%o %s %d", ret, key, value); + OH_AVFormat_Destroy(format); +} + +void acgist::VideoEncoder::resetLongConfig(const char* key, int64_t value) { + OH_AVFormat* format = OH_AVFormat_Create(); + OH_AVFormat_SetLongValue(format, key, value); + OH_AVErrCode ret = OH_VideoEncoder_SetParameter(this->avCodec, format); + OH_LOG_INFO(LOG_APP, "动态配置视频编码:%o %s %ld", ret, key, value); + OH_AVFormat_Destroy(format); +} + +void acgist::VideoEncoder::resetDoubleConfig(const char* key, double value) { + OH_AVFormat* format = OH_AVFormat_Create(); + OH_AVFormat_SetDoubleValue(format, key, value); + OH_AVErrCode ret = OH_VideoEncoder_SetParameter(this->avCodec, format); + OH_LOG_INFO(LOG_APP, "动态配置视频编码:%o %s %f", ret, key, value); + OH_AVFormat_Destroy(format); +} + +bool acgist::VideoEncoder::start() { + OH_AVErrCode ret = OH_VideoEncoder_Start(this->avCodec); + OH_LOG_INFO(LOG_APP, "开始视频编码:%o", ret); + return true; +} + +bool acgist::VideoEncoder::stop() { + OH_AVErrCode ret = OH_VideoEncoder_NotifyEndOfStream(this->avCodec); + OH_LOG_INFO(LOG_APP, "通知视频编码结束:%o", ret); + ret = OH_VideoEncoder_Stop(this->avCodec); + OH_LOG_INFO(LOG_APP, "结束视频编码:%o", ret); + return true; +} + +static void OnError(OH_AVCodec* codec, int32_t errorCode, void* userData) { + OH_LOG_WARN(LOG_APP, "视频编码发送错误:%d", errorCode); +} + +static void OnStreamChanged(OH_AVCodec* codec, OH_AVFormat* format, void* userData) { + OH_LOG_DEBUG(LOG_APP, "视频编码配置变化"); +} + +static void OnNeedInputBuffer(OH_AVCodec* codec, uint32_t index, OH_AVBuffer* buffer, void* userData) { + // 忽略 +} + +static void OnNewOutputBuffer(OH_AVCodec* codec, uint32_t index, OH_AVBuffer* buffer, void* userData) { + acgist::VideoCapturer* videoCapturer = (acgist::VideoCapturer*) userData; + // TODO: 全局是否性能更好 + OH_AVCodecBufferAttr info; + OH_AVErrCode ret = OH_AVBuffer_GetBufferAttr(buffer, &info); + char* data = reinterpret_cast(OH_AVBuffer_GetAddr(buffer)); + // TODO: 解析 + rtc::scoped_refptr videoFrameBuffer = webrtc::I420Buffer::Copy(width, height, (uint8_t*)data, 0, (uint8_t*)data, 0, (uint8_t*)data, 0); + // webrtc::NV12Buffer::Create(width, height); + webrtc::VideoFrame::Builder builder; + webrtc::VideoFrame videoFrame = builder + .set_timestamp_ms(rtc::TimeMillis()) + .set_video_frame_buffer(videoFrameBuffer) + .set_rotation(webrtc::VideoRotation::kVideoRotation_0) + .build(); + for (auto iterator = videoCapturer->map.begin(); iterator != videoCapturer->map.end(); ++iterator) { + iterator->second->OnFrame(videoFrame); + } + // TODO: 释放webrtc + videoFrameBuffer->Release(); + ret = OH_VideoEncoder_FreeOutputBuffer(codec, index); +} diff --git a/taoyao-client-openharmony/taoyao/media/src/main/cpp/types/libtaoyao/index.d.ts b/taoyao-client-openharmony/taoyao/media/src/main/cpp/types/libtaoyao/index.d.ts index 0af48b0..8b13ba2 100644 --- a/taoyao-client-openharmony/taoyao/media/src/main/cpp/types/libtaoyao/index.d.ts +++ b/taoyao-client-openharmony/taoyao/media/src/main/cpp/types/libtaoyao/index.d.ts @@ -1,16 +1,16 @@ -export const mediaConsume : () => number; -export const mediaConsumerClose : () => number; -export const mediaConsumerPause : () => number; -export const mediaConsumerResume: () => number; -export const mediaProducerClose : () => number; -export const mediaProducerPause : () => number; -export const mediaProducerResume: () => number; -export const roomClientList : () => number; -export const roomClose : () => number; -export const roomEnter : () => number; -export const roomExpel : () => number; +export const shutdown : (json: string) => number; +export const roomClose : (json: string) => number; +export const roomEnter : (json: string) => number; +export const roomExpel : (json: string) => number; export const roomInvite: (json: string) => number; -export const roomLeave : () => number; -export const shutdown : () => number; -export const registerSend : (callback: (signal: string, body: string) => void) => number; -export const registerRequest: (callback: (signal: string, body: string) => string) => number; +export const roomLeave : (json: string) => number; +export const roomClientList : (json: string) => number; +export const mediaConsume : (json: string) => number; +export const mediaConsumerClose : (json: string) => number; +export const mediaConsumerPause : (json: string) => number; +export const mediaConsumerResume: (json: string) => number; +export const mediaProducerClose : (json: string) => number; +export const mediaProducerPause : (json: string) => number; +export const mediaProducerResume: (json: string) => number; +export const registerSend : (callback: (signal: string, body: string) => void) => number; +export const registerRequest: (callback: (signal: string, body: string) => Promise) => number; diff --git a/taoyao-client-openharmony/taoyao/media/src/main/ets/taoyao/TaoyaoSignal.ets b/taoyao-client-openharmony/taoyao/media/src/main/ets/taoyao/TaoyaoSignal.ets index aac5275..a6e373e 100644 --- a/taoyao-client-openharmony/taoyao/media/src/main/ets/taoyao/TaoyaoSignal.ets +++ b/taoyao-client-openharmony/taoyao/media/src/main/ets/taoyao/TaoyaoSignal.ets @@ -166,6 +166,7 @@ class TaoyaoSignal { "body" : body }; try { + hilog.debug(0x0000, "TaoyaoSignal", "发送消息:%o", message); this.socket?.send(JSON.stringify(message)); } catch (error) { hilog.error(0x0000, "TaoyaoSignal", "发送消息异常:%o", message, error); @@ -206,6 +207,7 @@ class TaoyaoSignal { }); // 发送消息 try { + hilog.debug(0x0000, "TaoyaoSignal", "发送请求:%o", message); this.socket?.send(JSON.stringify(message)); } catch (error) { hilog.error(0x0000, "TaoyaoSignal", "发送消息异常:%o", message, error); @@ -222,7 +224,7 @@ class TaoyaoSignal { onMessage(message: string) { const json : Record = JSON.parse(message); const header: Record = json.header as Record; - const body : Record = json.body as Record; + const body : Record = json.body as Record; const id : number = header.id as number; const signal: string = header.signal as string; if (this.callbackMapping.has(id)) { @@ -251,8 +253,8 @@ class TaoyaoSignal { this.send(signal, JSON.parse(body)); } - nativeRequest(signal: string, body: string): string { - const response = this.request(signal, JSON.parse(body)); + async nativeRequest(signal: string, body: string): Promise { + const response = await this.request(signal, JSON.parse(body)); return JSON.stringify(response); } diff --git a/taoyao-client-openharmony/taoyao/media/src/main/module.json5 b/taoyao-client-openharmony/taoyao/media/src/main/module.json5 index 508e731..78e4c5d 100644 --- a/taoyao-client-openharmony/taoyao/media/src/main/module.json5 +++ b/taoyao-client-openharmony/taoyao/media/src/main/module.json5 @@ -36,42 +36,36 @@ "requestPermissions": [ { "name": "ohos.permission.CAMERA", - "reason": "视频", "usedScene": { "when": "always" } }, { "name": "ohos.permission.INTERNET", - "reason": "网络", "usedScene": { "when": "always" } }, { "name": "ohos.permission.MICROPHONE", - "reason": "音频", "usedScene": { "when": "always" } }, { "name": "ohos.permission.READ_MEDIA", - "reason": "录制文件", "usedScene": { "when": "always" } }, { "name": "ohos.permission.WRITE_MEDIA", - "reason": "录制文件", "usedScene": { "when": "always" } }, { "name": "ohos.permission.MEDIA_LOCATION", - "reason": "地理位置", "usedScene": { "when": "always" }