diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index a0dc8d52cf9cfec1d2b6bea2113c9eb6f5bf2092..69f13757b999e674cecf8e051a527e138a6a9997 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -25,6 +25,7 @@ -keep class * implements android.os.IInterface {*;} -keep interface com.huawei.cloudphone.service.CasInteractiveStateCallback {*;} +-keep interface com.huawei.cloudphone.service.CasCallback {*;} -keepclasseswithmembernames class * { # 保持native方法不被混淆 native ; } diff --git a/app/src/main/java/com/huawei/cloudapp/ui/fragment/home/PhoneListFragment.java b/app/src/main/java/com/huawei/cloudapp/ui/fragment/home/PhoneListFragment.java index 1a55b90daf91c4562a2d064a424464ba17848e7c..6efc83ae4e40d7872d9ba7efb8eb0480f39b11a6 100644 --- a/app/src/main/java/com/huawei/cloudapp/ui/fragment/home/PhoneListFragment.java +++ b/app/src/main/java/com/huawei/cloudapp/ui/fragment/home/PhoneListFragment.java @@ -740,6 +740,9 @@ public class PhoneListFragment extends CloudPhoneFragment implements IHandleData public void run() { //提示为空 showPhoneList(new ArrayList<>(),true, false); + if (mRefreshLayout != null) { + mRefreshLayout.finishRefresh(true).finishLoadMore(true); + } } }); } else if (list.size() == 0) { @@ -751,9 +754,6 @@ public class PhoneListFragment extends CloudPhoneFragment implements IHandleData params.text = getResources().getString(R.string.no_more_response); params.style = new CustomToastStyle(R.layout.toast_info); Toaster.show(params); - if (mRefreshLayout != null) { - mRefreshLayout.finishRefresh(true).finishLoadMore(true); - } } }); } else if (((List) list).get(0) instanceof User) { diff --git a/cloudphone/proguard-rules.pro b/cloudphone/proguard-rules.pro index 7f31c16ae6e0dff1cb4e0a16484ccda4fe6b1c8f..6a3e46a8696a78318b756f32b8075b815e1727db 100644 --- a/cloudphone/proguard-rules.pro +++ b/cloudphone/proguard-rules.pro @@ -32,5 +32,6 @@ -keep interface com.huawei.cloudphone.api.ICloudPhone {*;} -keep public enum com.huawei.cloudphone.api.CloudPhoneParas$* {*;} -keep interface com.huawei.cloudphone.service.CasInteractiveStateCallback {*;} +-keep interface com.huawei.cloudphone.service.CasCallback {*;} -keep class com.huawei.cloudphone.utils.CasDevRandomSeed {*;} \ No newline at end of file diff --git a/cloudphone/src/main/cpp/CMakeLists.txt b/cloudphone/src/main/cpp/CMakeLists.txt index b831d160beb2dc4e510b2cf520a06d39a73fed68..0adea55e74d0e49a1bcf0a6bd0c4fc9c925e17d3 100644 --- a/cloudphone/src/main/cpp/CMakeLists.txt +++ b/cloudphone/src/main/cpp/CMakeLists.txt @@ -67,7 +67,7 @@ add_subdirectory(cas_service) add_subdirectory(cas_socket) add_subdirectory(hwsecure) add_subdirectory(cas_decoder) - +add_subdirectory(cas_controller) ##添加libssl静态库 add_library(ssl STATIC IMPORTED) @@ -111,4 +111,5 @@ target_link_libraries(cloudapp libopus VideoDecoder hwsecure + cas_controller ) \ No newline at end of file diff --git a/cloudphone/src/main/cpp/CasController.cpp b/cloudphone/src/main/cpp/CasController.cpp index 7734d60798b008bfc6f20ca508ed401d82fba6b2..83c15c0a6b2e7e15948ba59b89cf530b68fd75c4 100644 --- a/cloudphone/src/main/cpp/CasController.cpp +++ b/cloudphone/src/main/cpp/CasController.cpp @@ -26,6 +26,8 @@ #include "CasExtVideoDataPipe.h" #include "opus.h" #include "CasVideoUtil.h" +#include "cas_controller/CasTransmission.h" +#include "cas_controller/CasHandlerManager.h" using namespace std; @@ -39,267 +41,61 @@ const int TIMES = 8; const int FRAME_RATE_MIN = 10; const int FRAME_RATE_MAX = 60; const std::string CLIENT_TYPE = "1"; -const std::string HRTP_LOG_PATH = "/sdcard/Android/media/com.huawei.cloudapp.b004/"; const uint64_t DURATION_USEC = 1000000ULL; -int32_t OnRecvVideoStreamData(uint8_t* data, uint32_t length); -int32_t OnRecvAudioStreamData(uint8_t* data, uint32_t length); -int32_t OnRecvAudioDecodeCallback(int32_t streamId, AudioJbDecode* audioJbDecode); -int32_t OnRecvCmdData(uint8_t* data, uint32_t length); -void OnGotTransLog(const char* str, uint32_t length); -void OnNeedKeyFrameCallback(); -OpusDecoder *g_opusDecoder = nullptr; -shared_ptr hrtpLogger = nullptr; -int g_plcCount = 0; - -CasController *CasController::g_instance = nullptr; -CasController *CasController::GetInstance() -{ - if (g_instance == nullptr) { - g_instance = new (std::nothrow) CasController(); - if (g_instance == nullptr) { - ERR("Failed to new CasController."); - return nullptr; - } - } - return g_instance; -} - -bool CasController::DestroyInstance() -{ - if (g_instance != nullptr) { - delete g_instance; - g_instance = nullptr; - return true; - } - INFO("Instance already destroyed."); - return true; -} - CasController::CasController() { - m_videoDecodeThread = nullptr; - m_videoPacketStream = nullptr; - m_audioPacketStream = nullptr; - m_orientationStream = nullptr; - m_virtualDeviceStream = nullptr; - m_controlStream = nullptr; - m_channelStream = nullptr; - m_imeDataStream = nullptr; - m_cmdController = nullptr; - m_streamParseThread = nullptr; - m_heartbeatThread = nullptr; - m_heartbeatController = nullptr; - m_casClientSocket = nullptr; - m_streamBuildSender = nullptr; - m_streamParser = nullptr; - m_state = INIT; - m_touch = nullptr; - cmdCallBack = nullptr; - m_orientation = 0; - m_frameType = FrameType::H264; - m_rotationDegrees = 0; - m_mtrans = nullptr; - m_isMTransValid = false; + INFO("Constuctor."); + m_isconnect = true; } CasController::~CasController() { - m_videoDecodeThread = nullptr; - m_videoPacketStream = nullptr; - m_audioPacketStream = nullptr; - m_orientationStream = nullptr; - m_virtualDeviceStream = nullptr; - m_controlStream = nullptr; - m_channelStream = nullptr; - m_imeDataStream = nullptr; - m_cmdController = nullptr; - m_streamParseThread = nullptr; - m_heartbeatThread = nullptr; - m_controlThread = nullptr; - m_heartbeatController = nullptr; - m_casClientSocket = nullptr; - m_streamBuildSender = nullptr; - m_streamParser = nullptr; - m_state = INIT; - m_touch = nullptr; - cmdCallBack = nullptr; - m_orientation = 0; - m_mtrans = nullptr; - m_isMTransValid = false; -} - -void CasController::SetJniConf(string key, string value) -{ - std::lock_guard lockGuard(m_jniConfLock); - this->m_jniConf[key] = value; -} - -JNIState CasController::GetState() -{ - return this->m_state; -} - -void CasController::SetState(JNIState state) -{ - this->m_state = state; + INFO("Destuctor."); + ReleaseResource(); } bool CasController::Start(ANativeWindow *nativeWindow, bool isHome) { - m_orientation = 0; - m_nativeWindow = nativeWindow; - bool res = false; - if (isHome && this->GetState() != STOPPED && m_casClientSocket->GetStatus() != SOCKET_STATUS_RUNNING) { - res = Reconnect(); - if (res) { - CreateDecWorker(m_nativeWindow, m_needVideoDecode); - StartDecWorker(!m_retainVideoDecode); - return true; - } else { - NotifyCommand(CAS_CONNECT_LOST, CasMsgCode::GetMsg(CAS_CONNECT_LOST)); - return false; - } - } std::lock_guard lockGuard(this->m_lock); - if (isHome) { - return ProcessEnterForeground(m_nativeWindow); - } - - if (this->GetState() == STOPPED) { - this->SetState(INIT); - } - - m_conf.parseConf(m_jniConf); - m_ticket = m_conf.ticket; - m_sessionId = m_conf.sessionId; - m_ip = TransIp(m_conf.ip.c_str()); - m_port = (unsigned short)m_conf.port; - m_encryptedData = m_conf.encryptedData; - m_verifyData = m_conf.verifyData; - m_authTs = m_conf.authTs; - m_aesIv = m_conf.aesIv; - m_clientType = CLIENT_TYPE; - m_maxDisconnectDuration = CalcMaxDisconnectDuration(m_conf.backgroundTimeout); - - if (m_mediaConfig.find(KEY_FRAME_TYPE) != m_mediaConfig.end()) { - m_frameType = m_mediaConfig[KEY_FRAME_TYPE] == "h264" ? FrameType::H264 : FrameType::H265; - } - - res = InitDataStream(); - if (!res) { - CloseDataStream(); - return false; - } - - res = CreateWorkers(); - if (!res) { - DestroyWorkers(); - CloseDataStream(); - return false; - } - - res = BuildConnection(); - if (!res) { - ERR("Failed to build connection"); - DestroyWorkers(); - CloseDataStream(); - return false; - } - - StartWorkers(); - - if (!SendStartCmd()) { - ERR("Failed to send start command"); - return false; - } - return true; -} - -bool CasController::IsValidMediaConfig(map mediaConfig) -{ - if (mediaConfig.empty()) { - ERR("Media config is empty."); - return false; - } - - if (mediaConfig.find(KEY_BITRATE) != mediaConfig.end()) { - uint32_t bitrate = static_cast(atoi(mediaConfig[KEY_BITRATE].c_str())); - if (bitrate < BITRATE_MIN || bitrate > BITRATE_MAX) { - ERR("Bitrate is invalid, value is %u.", bitrate); - return false; - } - } - - if (mediaConfig.find(KEY_FRAME_RATE) != mediaConfig.end()) { - uint32_t frameRate = static_cast(atoi(mediaConfig[KEY_FRAME_RATE].c_str())); - if (frameRate < FRAME_RATE_MIN || frameRate > FRAME_RATE_MAX || frameRate % 10 != 0) { - ERR("Frame rate is invalid, value is %u.", frameRate); - return false; - } - } - - // 校验虚拟宽高,虚拟宽高需满足同时设置或同时未设置,且大于等于240,小于等于4096,与8对齐 - bool containsStreamWidth = mediaConfig.find(KEY_STREAM_WIDTH) != mediaConfig.end(); - bool containsStreamHeight = mediaConfig.find(KEY_STREAM_HEIGHT) != mediaConfig.end(); - if (containsStreamWidth && containsStreamHeight) { - uint32_t streamWidth = static_cast(atoi(mediaConfig[KEY_STREAM_WIDTH].c_str())); - uint32_t streamHeight = static_cast(atoi(mediaConfig[KEY_STREAM_HEIGHT].c_str())); - if (streamWidth > streamHeight || streamWidth < WIDTH_MIN || streamWidth > WIDTH_MAX || - streamWidth % TIMES != 0 || streamHeight < HEIGHT_MIN || streamHeight > HEIGHT_MAX || - streamHeight % TIMES != 0) { - ERR("Stream width or Stream height is invalid, Stream width %u Stream height %u", streamWidth, streamHeight); - return false; - } - } else if (containsStreamWidth || containsStreamHeight) { - ERR("Stream width or Stream height is not included"); + prepareParameters(); + m_nativeWindow = nativeWindow; + m_casMessageTypes.clear(); + m_casMessageTypes.push_back(CasMsgType::Video); + m_casMessageTypes.push_back(CasMsgType::HeartBeat); + m_casMessageTypes.push_back(CasMsgType::CmdControl); + m_casMessageTypes.push_back(CasMsgType::Orientation); + m_casMessageTypes.push_back(CasMsgType::Audio); + m_casMessageTypes.push_back(CasMsgType::VirtualCamera); + m_casMessageTypes.push_back(CasMsgType::VirtualMicrophone); + m_casMessageTypes.push_back(CasMsgType::VirtualSensor); + m_casMessageTypes.push_back(CasMsgType::VirtualLocation); + + if (!ProcessStart()) { + SetState(STOPPED); return false; + } else { + SetState(START_SUCCESS); + return true; } - return true; -} - -string CasController::CalcMaxDisconnectDuration(string backgroundTimeout) -{ - int maxDisconnectDuration = 60; - int timeout = atoi(backgroundTimeout.c_str()); - if (timeout > 120) { - maxDisconnectDuration = timeout - 60; - } - return to_string(maxDisconnectDuration); } bool CasController::Stop(bool isHome) { std::lock_guard lockGuard(this->m_lock); - if (this->GetState() == STOPPED) { - INFO("Current state is stopped."); - return true; - } - if (isHome) { - ProcessEnterBackground(); - return true; + if (this->GetState() == STOPPED || this->GetState() == INIT) { + INFO("Already stop."); + return false; } - map parameters = { { KEY_COMMAND, CMD_STOP_APP } }; bool res = SendCommand(parameters); if (!res) { ERR("Failed to send stop command."); } - this->SetState(STOPPED); - - DestroyWorkers(); - StopDecWorker(!m_retainVideoDecode); - CloseDataStream(); - -#if MTRANS_ENABLED - if (m_mtrans != nullptr) { - delete m_mtrans; - m_mtrans = nullptr; - } -#endif - - m_isNotifyFirstFrame = false; + m_casHandlerManager->stop(); + m_casTransmission->stop(); + ReleaseResource(); m_rotationDegrees = 0; m_orientation = 0; m_mediaConfig.clear(); @@ -307,301 +103,97 @@ bool CasController::Stop(bool isHome) return true; } -bool CasController::Release() +bool CasController::Pause() { std::lock_guard lockGuard(this->m_lock); if (this->GetState() == STOPPED) { - INFO("Release failed because phone already stop."); + INFO("Current state is stopped."); return false; } - - DestroyWorkers(); - StopDecWorker(m_retainVideoDecode); - ClearDataStream(); + ProcessEnterBackground(); return true; } -bool CasController::Reconnect() +bool CasController::Resume(ANativeWindow *nativeWindow) { - bool res = Release(); - if (!res) { - ERR("Reconnect fail because release resource failed."); - return false; - } - std::lock_guard lockGuard(this->m_lock); if (this->GetState() == STOPPED) { - INFO("Reconnect failed because phone already stop."); - return false; - } - - res = CreateWorkers(); - if (!res) { - DestroyWorkers(); - return false; - } - - NotifyCommand(CAS_RECONNECTING, CasMsgCode::GetMsg(CAS_RECONNECTING)); - int connectRes = m_casClientSocket->Connect(); - if (connectRes != 0) { - ERR("Failed to build Reconnect socket state %d.", m_casClientSocket->GetStatus()); - this->SetState(CONNECTION_FAILURE); - return false; - } - - if (m_sessionId.empty()) { - ERR("SessionId is empty."); - NotifyCommand(CAS_RECONNECT_PARAMETER_INVALID, CasMsgCode::GetMsg(CAS_RECONNECT_PARAMETER_INVALID)); + INFO("Current state is stopped."); return false; } - this->SetState(CONNECTED); - NotifyCommand(CAS_RECONNECT_SUCCESS, CasMsgCode::GetMsg(CAS_RECONNECT_SUCCESS)); - - std::string reconnectCmd = CMD_RECONNECT; - map parameters = { { KEY_COMMAND, reconnectCmd }, { KEY_SESSION_ID, m_sessionId } }; - - res = SendCommand(parameters); - if (!res) { - ERR("Failed to send reconnect command"); - return false; + m_nativeWindow = nativeWindow; + if (GetState() != STOPPED && !GetConnectStatus()) { + if (!ProcessReconnect()) { + NotifyCommand(CAS_CONNECT_LOST, CasMsgCode::GetMsg(CAS_CONNECT_LOST)); + return false; + } + SetState(START_SUCCESS); + m_casHandlerManager->setVideoParameters(m_nativeWindow, m_frameType, m_rotationDegrees); + return m_casHandlerManager->startVideoHandler() == 0; + } else { + return ProcessEnterForeground(nativeWindow); } - - StartWorkers(); - StartDecWorker(m_retainVideoDecode); - return true; } -bool CasController::CreateWorkers() +bool CasController::Reconnect() { - m_casClientSocket = new (std::nothrow) CasTcpClientSocket(m_ip, m_port); - if (m_casClientSocket == nullptr) { - ERR("Failed to new client socket."); - return false; - } - - m_streamBuildSender = new (std::nothrow) CasStreamBuildSender(m_casClientSocket); - if (m_streamBuildSender == nullptr) { - ERR("Failed to new stream build sender."); - return false; - } - - m_streamParser = CasStreamRecvParser::GetInstance(); - if (m_streamParser == nullptr) { - ERR("Failed to get stream parser."); - return false; - } - - m_streamParseThread = new (std::nothrow) CasStreamParseThread(m_casClientSocket, m_streamParser); - if (m_streamParseThread == nullptr) { - ERR("Failed to new stream parse thread."); - return false; - } - - m_heartbeatController = new (std::nothrow) CasHeartbeatController(m_streamBuildSender); - if (m_heartbeatController == nullptr) { - ERR("Failed to new heartbeat controller."); - return false; - } - - m_heartbeatThread = new (std::nothrow) CasHeartbeatThread(m_heartbeatController, m_casClientSocket); - if (m_heartbeatThread == nullptr) { - ERR("Failed to new heartbeat thread."); - return false; - } - - m_cmdController = new (std::nothrow) CasCmdController(m_streamBuildSender); - if (m_cmdController == nullptr) { - ERR("Failed to new cmd controller."); - return false; - } - m_cmdController->RegisterStateChangeListener(this); - - m_controlThread = new (std::nothrow) CasCmdControlThread(m_cmdController); - if (m_controlThread == nullptr) { - ERR("Failed to new cmd controller."); - return false; - } - m_controlThread->SetControlPktHandle(m_controlStream); - - m_streamParser->SetServiceHandle(CasMsgType::HeartBeat, m_heartbeatController); - m_streamParser->SetServiceHandle(CasMsgType::CmdControl, m_controlStream); - m_streamParser->SetServiceHandle(CasMsgType::Orientation, m_orientationStream); - m_streamParser->SetServiceHandle(CasMsgType::Video, m_videoPacketStream); - m_streamParser->SetServiceHandle(CasMsgType::Audio, m_audioPacketStream); - m_streamParser->SetServiceHandle(CasMsgType::Channel, m_channelStream); - m_streamParser->SetServiceHandle(CasMsgType::VirtualDevice, m_virtualDeviceStream); - m_streamParser->SetServiceHandle(CasMsgType::ImeData, m_imeDataStream); - - m_touch = new (std::nothrow) CasTouch(m_casClientSocket); - if (m_touch == nullptr) { - ERR("Failed to new touch."); + std::lock_guard lockGuad(m_lock); + if (this->GetState() == STOPPED) { + INFO("Reconnect failed because phone already stop."); return false; } - -#if MTRANS_ENABLED - if (m_mtrans == nullptr) { - TransConfigParam param; - param.minVideoSendBitrate = 500; - param.maxVideoSendBitrate = 10000; - - if (!m_logInit) { - m_logInit = true; - // Create a file rotating logger with 50 MB size max and 3 rotated files - auto max_size = 1048576 * 50; - auto max_files = 3; - hrtpLogger = spdlog::rotating_logger_mt("hrtp_logger", HRTP_LOG_PATH + "hrtp_log.txt", max_size, max_files); - } - - m_mtrans = new (std::nothrow) NetTrans(); - m_mtrans->EnableLog(HRTP_LOG_PATH + "hrtp_log.txt", TRANS_LOG_LEVEL_INFO); - m_mtrans->Init(PEER_CLIENT, m_conf.ip.c_str(), m_conf.port, param, - OnRecvVideoStreamData, - OnRecvAudioStreamData, - nullptr, - OnNeedKeyFrameCallback, - OnRecvCmdData, - OnRecvAudioDecodeCallback, - OnGotTransLog); - } -#endif - - int err = 0; - g_opusDecoder = opus_decoder_create(48000, 2, &err); - - return true; + return ProcessReconnect(); } -bool CasController::StartWorkers() +bool CasController::StartScreenshot() { -#if MTRANS_ENABLED - if (m_mtrans != nullptr) { - if (m_mtrans->Start() < 0) { - m_mtrans->Stop(); - m_streamBuildSender->SetNetTrans(nullptr); - } else { - m_streamBuildSender->SetNetTrans(m_mtrans); - } + std::lock_guard lockGuard(m_lock); + m_snapshotFlag = true; + prepareParameters(); + + m_casMessageTypes.clear(); + m_casMessageTypes.push_back(CasMsgType::HeartBeat); + m_casMessageTypes.push_back(CasMsgType::CmdControl); + m_casMessageTypes.push_back(CasMsgType::Orientation); + m_casMessageTypes.push_back(CasMsgType::ScreenShot); + if (!ProcessStart()) { + SetState(STOPPED); + return false; + } else { + SetState(START_SUCCESS); + return true; } -#endif - m_streamParseThread->Start(); - m_heartbeatThread->Start(); - m_controlThread->Start(); - INFO("Succeed to start workers"); - return true; } -bool CasController::DestroyWorkers() +bool CasController::StopScreenshot() { - if (m_streamParseThread != nullptr) { - m_streamParseThread->Stop(); - delete m_streamParseThread; - m_streamParseThread = nullptr; - } - - if (m_streamParser != nullptr) { - CasStreamRecvParser::DestroyInstance(); - m_streamParser = nullptr; - } - - if (m_touch != nullptr) { - delete m_touch; - m_touch = nullptr; - } - - if (m_heartbeatThread != nullptr) { - if (m_heartbeatController != nullptr) { - m_heartbeatController->StopHandle(); - } - m_heartbeatThread->Exit(); - delete m_heartbeatThread; - m_heartbeatThread = nullptr; - } - - if (m_heartbeatController != nullptr) { - delete m_heartbeatController; - m_heartbeatController = nullptr; - } - - if (m_controlThread != nullptr) { - m_controlThread->Exit(); - delete m_controlThread; - m_controlThread = nullptr; - } - - if (m_cmdController != nullptr) { - delete m_cmdController; - m_cmdController = nullptr; - } - - if (m_streamBuildSender != nullptr) { - delete m_streamBuildSender; - m_streamBuildSender = nullptr; - } - - if (m_casClientSocket != nullptr) { - delete m_casClientSocket; - m_casClientSocket = nullptr; - } - -#if MTRANS_ENABLED - if (m_mtrans != nullptr) { - m_mtrans->Stop(); - } -#endif - m_isMTransValid = false; - - INFO("Succeed to destroy workers"); - return true; + m_snapshotFlag = false; + return Stop(false); } -bool CasController::BuildConnection() +void CasController::SetJniConf(string key, string value) { - NotifyCommand(CAS_CONNECTING, CasMsgCode::GetMsg(CAS_CONNECTING)); - int connectRes = -2; - int retry = 0; - while ((connectRes == -1 || connectRes == -2) && retry < 3) { - retry++; - INFO("Connect times: %d", retry); - connectRes = m_casClientSocket->Connect(); - if (connectRes >= 0) { - break; - } else { - usleep(500000); - } - } - - if (connectRes == -1) { - this->SetState(CONNECTION_FAILURE); - NotifyCommand(CAS_SERVER_UNREACHABLE, CasMsgCode::GetMsg(CAS_SERVER_UNREACHABLE)); - return false; - } else if (connectRes == -2) { - this->SetState(CONNECTION_FAILURE); - NotifyCommand(CAS_RESOURCE_IN_USING, CasMsgCode::GetMsg(CAS_RESOURCE_IN_USING)); - return false; - } - NotifyCommand(CAS_CONNECT_SUCCESS, CasMsgCode::GetMsg(CAS_CONNECT_SUCCESS)); - this->SetState(CONNECTED); - return true; + std::lock_guard lockGuard(m_jniConfLock); + this->m_jniConf[key] = value; } bool CasController::SetMediaConfig(map mediaConfig) { + std::lock_guard lockGuard(m_lock); if (!IsValidMediaConfig(mediaConfig)) { ERR("Media config is invalid"); return false; } - - if (this->GetState() == INIT || this->GetState() == STOPPED) { + if (GetState() == INIT || GetState() == STOPPED) { INFO("Init media config"); this->m_mediaConfig = mediaConfig; return true; } - std::string setMediaConfigCmd = CMD_SET_MEDIA_CONFIG; string mediaConfigStr = CasAppCtrlCmdUtils::MakeCommand(mediaConfig, SUB_COMMAND_SEPARATOR); map parameters = { - { KEY_COMMAND, setMediaConfigCmd }, - { KEY_MEDIA_CONFIG, mediaConfigStr }, + { KEY_COMMAND, setMediaConfigCmd }, + { KEY_MEDIA_CONFIG, mediaConfigStr }, }; bool res = SendCommand(parameters); if (!res) { @@ -611,374 +203,163 @@ bool CasController::SetMediaConfig(map mediaConfig) return true; } -bool CasController::SendCommand(map parameters) -{ - if ((m_casClientSocket == nullptr) || (m_casClientSocket->GetStatus() != SOCKET_STATUS_RUNNING)) { - ERR("Failed to send command because socket status not running."); - return false; - } - bool sendCmdRes = m_cmdController->SendCtrlCmd(parameters); - if (!sendCmdRes) { - ERR("Failed to send command."); - return false; - } - return true; -} - -bool CasController::InitDataStream() -{ - m_audioPacketStream = new (std::nothrow) CasDataPipe(); - if (m_audioPacketStream == nullptr) { - ERR("Failed to new audio packet stream."); - return false; - } - - m_videoPacketStream = new (std::nothrow) CasExtVideoDataPipe(); - if (m_videoPacketStream == nullptr) { - ERR("Failed to new video packet stream."); - return false; - } - - m_orientationStream = new (std::nothrow) CasDataPipe(); - if (m_orientationStream == nullptr) { - ERR("Failed to new orientation packet stream."); - return false; - } - - m_controlStream = new (std::nothrow) CasDataPipe(); - if (m_controlStream == nullptr) { - ERR("Failed to new control packet stream."); - return false; - } - - m_channelStream = new (std::nothrow) CasDataPipe(); - if (m_channelStream == nullptr) { - ERR("Failed to new channel packet stream."); - return false; - } - - m_virtualDeviceStream = new (std::nothrow) CasDataPipe(); - if (m_virtualDeviceStream == nullptr) { - ERR("Failed to new virtual device packet stream."); - return false; - } - - m_imeDataStream = new (std::nothrow) CasDataPipe(); - if (m_imeDataStream == nullptr) { - ERR("Failed to new ime data packet stream."); - return false; - } - - return true; -} - -bool CasController::ClearDataStream() -{ - if (m_audioPacketStream != nullptr) { - m_audioPacketStream->Clear(); - } - - if (m_videoPacketStream != nullptr) { - m_videoPacketStream->Clear(); - } - - if (m_controlStream != nullptr) { - m_controlStream->Clear(); - } - - if (m_orientationStream != nullptr) { - m_orientationStream->Clear(); - } - - if (m_channelStream != nullptr) { - m_channelStream->Clear(); - } - - if (m_virtualDeviceStream != nullptr) { - m_virtualDeviceStream->Clear(); - } - - if (m_imeDataStream != nullptr) { - m_imeDataStream->Clear(); - } - - INFO("Succeed to clear data stream "); - return true; -} - -bool CasController::CloseDataStream() -{ - if (m_audioPacketStream != nullptr) { - delete m_audioPacketStream; - m_audioPacketStream = nullptr; - } - - if (m_videoPacketStream != nullptr) { - delete m_videoPacketStream; - m_videoPacketStream = nullptr; - } - - if (m_controlStream != nullptr) { - delete m_controlStream; - m_controlStream = nullptr; - } - - if (m_orientationStream != nullptr) { - delete m_orientationStream; - m_orientationStream = nullptr; - } - - if (m_channelStream != nullptr) { - delete m_channelStream; - m_channelStream = nullptr; - } - - if (m_imeDataStream != nullptr) { - delete m_imeDataStream; - m_imeDataStream = nullptr; - } - - INFO("Succeed to close data stream "); - return true; -} - -uint64_t CasController::GetLag() -{ - if (m_heartbeatThread != nullptr) { - return m_heartbeatThread->GetLag(); - } - return 0; -} - bool CasController::SendTouchEvent(int id, int action, int x, int y, int pressure, long time, int orientation, int height, int width) { - int new_time = (int) time; - std::lock_guard lockGuard(this->m_lock); - if (this->GetState() == STOPPED) { - INFO("Failed to send touch event because phone already stop."); - return false; - } - if (m_casClientSocket == nullptr || m_casClientSocket->GetStatus() != SOCKET_STATUS_RUNNING) { - ERR("Failed to send touch event because socket status not running."); - return false; - } - - if (m_touch == nullptr) { - return false; - } - return m_touch->SendTouchEvent(id, action, x, y, pressure, new_time, orientation, height, width); -} - -bool CasController::SendKeyEvent(uint16_t keycode, uint16_t action) -{ - std::lock_guard lockGuard(this->m_lock); - if (this->GetState() == STOPPED) { - INFO("Failed to send key event because phone already stop."); - return false; - } - if (m_casClientSocket == nullptr || m_casClientSocket->GetStatus() != SOCKET_STATUS_RUNNING) { - ERR("Failed to send key event because socket status not running."); - return false; - } - - if (m_touch == nullptr) { - return false; - } - return m_touch->SendKeyEvent(keycode, action); -} - -bool CasController::SendMotionEvent(uint16_t masterAxis, int32_t masterValue, uint16_t secondaryAxis, - int32_t secondaryValue) -{ - std::lock_guard lockGuard(this->m_lock); - if (this->GetState() == STOPPED) { - INFO("Failed to send motion event because phone already stop."); - return false; - } - if (m_casClientSocket == nullptr || m_casClientSocket->GetStatus() != SOCKET_STATUS_RUNNING) { - ERR("Failed to send motion event because socket status not running."); - return false; - } - -#if MTRANS_ENABLED - if (m_mtrans != nullptr && m_isMTransValid) { - size_t pkgLen = sizeof(MotionEventParam); - stream_msg_head_t msgHead; - msgHead.checksum = CAS_MSG_CHECKSUM_MOTIONEVENTINPUT; - msgHead.SetPayloadSize(pkgLen); - msgHead.magicword = CAS_STREAM_DELIMITER_MAGICWORD; - msgHead.type = MotionEventInput; - - MotionEventParam motionEventParam{}; - motionEventParam.masterAxis = masterAxis; - motionEventParam.secondaryAxis = secondaryAxis; - motionEventParam.masterValue = masterValue; - motionEventParam.secondaryValue = secondaryValue; - - size_t dataLen = sizeof(stream_msg_head_t) + pkgLen; - - char *outBuffer = (char *)malloc(dataLen); - if (outBuffer == nullptr) { - ERR("Failed to malloc out buffer."); - return 0; - } - if (EOK != memcpy_s(outBuffer, dataLen, &msgHead, sizeof(stream_msg_head_t))) { - ERR("Copy msg head fail."); - free(outBuffer); - return 0; - } - if (EOK != memcpy_s(outBuffer + sizeof(stream_msg_head_t), pkgLen, &motionEventParam, pkgLen)) { - ERR("Copy msg data fail."); - free(outBuffer); - return 0; - } - - int ret = m_mtrans->SendMotionEventData(reinterpret_cast(outBuffer), dataLen); - if (ret != static_cast(dataLen)) { - ERR("Error: failed to send motion event, aimed to Send:%d, sent:%d", (int)sizeof(CasMotionEventMsg), ret); - return false; - } - return true; + std::lock_guard lockGuard(m_lock); + if (this->GetState() != START_SUCCESS) { + INFO("Failed to send touch event because phone not start."); + return false; } -#endif + return m_touch->SendTouchEvent(id, action, x, y, pressure, (int)time, orientation, height, width); +} - if (m_touch == nullptr) { +bool CasController::SendKeyEvent(uint16_t keycode, uint16_t action) +{ + std::lock_guard lockGuard(m_lock); + if (this->GetState() != START_SUCCESS) { + INFO("Failed to send key event because phone already stop."); return false; } + return m_touch->SendKeyEvent(keycode, action); +} +bool CasController::SendMotionEvent(uint16_t masterAxis, int32_t masterValue, uint16_t secondaryAxis, + int32_t secondaryValue) +{ + std::lock_guard lockGuard(m_lock); + if (this->GetState() != START_SUCCESS) { + INFO("Failed to send motion event because phone not start."); + return false; + } return m_touch->SendMotionEvent(masterAxis, masterValue, secondaryAxis, secondaryValue); } int CasController::JniSendData(CasMsgType type, uint8_t *data, int length) { - std::lock_guard lockGuard(this->m_lock); -#if MTRANS_ENABLED - if (m_mtrans != nullptr && m_isMTransValid) { - switch (type) { - case (VirtualCamera): - return length == m_mtrans->SendVideoData(data, length, "h264"); - case (VirtualMicrophone): - return length == m_mtrans->SendAudioData(data, length); - default: - break; - } + std::lock_guard lockGuard(m_lock); + if (this->GetState() != START_SUCCESS) { + return false; } -#endif - - if (m_streamBuildSender == nullptr) { - return -1; + if (m_streamBuildSender == nullptr || !m_casTransmission->isReady()) { + return false; } return length == m_streamBuildSender->SendDataToServer(type, data, length); } -int CasController::JniRecvData(CasMsgType type, uint8_t *data, int length) +void CasController::SetLogPath(std::string &path) { - void *pPkt = nullptr; - bool isGetOrientation = false; - switch (type) { - case CasMsgType::Audio: - if (m_audioPacketStream != nullptr) { - pPkt = m_audioPacketStream->GetNextPkt(); - } - break; - case CasMsgType::Orientation: - if (m_orientationStream != nullptr) { - pPkt = m_orientationStream->GetNextPkt(); - isGetOrientation = true; - } - break; - case CasMsgType::Channel: - if (m_channelStream != nullptr) { - pPkt = m_channelStream->GetNextPkt(); - } - break; - case CasMsgType::VirtualDevice: - if (m_virtualDeviceStream != nullptr) { - pPkt = m_virtualDeviceStream->GetNextPkt(); - } - break; - case CasMsgType::ImeData: - if (m_imeDataStream != nullptr) { - pPkt = m_imeDataStream->GetNextPkt(); - } - break; - default: - ERR("Invalid type %d, length %d.", type, length); - return 0; - } + m_logPath = path; +} - if (pPkt == nullptr) { - DBG("Packet is null."); - return 0; +JNIState CasController::GetState() +{ + return this->m_state; +} + +void CasController::SetState(JNIState state) +{ + this->m_state = state; +} + +bool CasController::IsValidMediaConfig(map mediaConfig) +{ + if (mediaConfig.empty()) { + ERR("Media config is empty."); + return false; } - stream_msg_head_t *streamMsgHead = (stream_msg_head_t *)pPkt; - unsigned int dataLen = streamMsgHead->GetPayloadSize(); - char *pPayload = (char *)pPkt + sizeof(stream_msg_head_t); - - if ((unsigned int)length < dataLen) { - ERR("Input buffer is not large enough, type %d free %d payload %u", streamMsgHead->type, length, dataLen); - if (pPkt != nullptr) { - free(pPkt); - pPkt = nullptr; + + if (mediaConfig.count(KEY_BITRATE)) { + int bitrate = atoi(mediaConfig[KEY_BITRATE].c_str()); + if (bitrate < BITRATE_MIN || bitrate > BITRATE_MAX) { + ERR("Bitrate is invalid, value is %u.", bitrate); + return false; } - return 0; } - errno_t ret = memcpy_s(data, dataLen, pPayload, dataLen); - if (EOK != ret) { - ERR("Failed to copy ret = %d, dataLen = %u.", ret, dataLen); - if (pPkt != nullptr) { - free(pPkt); - pPkt = nullptr; + if (mediaConfig.count(KEY_FRAME_RATE)) { + int frameRate = atoi(mediaConfig[KEY_FRAME_RATE].c_str()); + if (frameRate < FRAME_RATE_MIN || frameRate > FRAME_RATE_MAX || frameRate % 10 != 0) { + ERR("Frame rate is invalid, value is %u.", frameRate); + return false; } - return 0; } - if (isGetOrientation) { - IsNeedRotation((int) (*pPayload)); + // 校验虚拟宽高,虚拟宽高需满足同时设置或同时未设置,且大于等于240,小于等于4096,与8对齐 + bool containsStreamWidth = mediaConfig.find(KEY_STREAM_WIDTH) != mediaConfig.end(); + bool containsStreamHeight = mediaConfig.find(KEY_STREAM_HEIGHT) != mediaConfig.end(); + + if (containsStreamWidth && containsStreamWidth) { + int streamWidth = atoi(mediaConfig[KEY_STREAM_WIDTH].c_str()); + int streamHeight = atoi(mediaConfig[KEY_STREAM_HEIGHT].c_str()); + if (streamWidth > streamHeight || streamWidth < WIDTH_MIN || streamWidth > WIDTH_MAX || + streamWidth % TIMES != 0 || streamHeight < HEIGHT_MIN || streamHeight > HEIGHT_MAX || + streamHeight % TIMES != 0) { + ERR("Stream width or Stream height is invalid, Stream width %u Stream height %u", streamWidth, streamHeight); + return false; + } + } else if (containsStreamWidth || containsStreamHeight) { + ERR("Stream width or Stream height is not included"); + return false; } + return true; +} - if (pPkt != nullptr) { - free(pPkt); - pPkt = nullptr; +string CasController::CalcMaxDisconnectDuration(string backgroundTimeout) +{ + int maxDisconnectDuration = 60; + int timeout = atoi(backgroundTimeout.c_str()); + if (timeout > 120) { + maxDisconnectDuration = timeout - 60; } + return to_string(maxDisconnectDuration); +} - return dataLen; +bool CasController::BuildConnection() +{ + NotifyCommand(CAS_CONNECTING, CasMsgCode::GetMsg(CAS_CONNECTING)); + int connectRes = m_casTransmission->connect(); + if (connectRes == -1) { + SetState(CONNECTION_FAILURE); + NotifyCommand(CAS_SERVER_UNREACHABLE, CasMsgCode::GetMsg(CAS_SERVER_UNREACHABLE)); + return false; + } else if (connectRes == -2) { + SetState(CONNECTION_FAILURE); + NotifyCommand(CAS_RESOURCE_IN_USING, CasMsgCode::GetMsg(CAS_RESOURCE_IN_USING)); + return false; + } + NotifyCommand(CAS_CONNECT_SUCCESS, CasMsgCode::GetMsg(CAS_CONNECT_SUCCESS)); + SetConnectStatus(true); + SetState(CONNECTED); + return true; } -void CasController::IsNeedRotation(int orientation) +bool CasController::SendCommand(map parameters) { - INFO("ORIENTATION is %d, old ORIENTATION is %d.", orientation, m_orientation); - - if (orientation == 8) { - m_rotationDegrees = 270; - } else if (orientation == 24) { - m_rotationDegrees = 90; - } else if (orientation == 16) { - m_rotationDegrees = 180; - } else { - m_rotationDegrees = 0; + if (m_cmdController == nullptr) { + ERR("Failed to send command because socket status not running."); + return false; + } + bool sendCmdRes = m_cmdController->SendCtrlCmd(parameters); + if (!sendCmdRes) { + ERR("Failed to send command."); + return false; } + return true; +} - ResetDecoder(m_isNotifyFirstFrame); - ForceIFrame(); - m_orientation = orientation; +uint64_t CasController::GetLag() +{ + return m_lag; } bool CasController::GetConnectStatus() { -#if defined(RECONNECT) || (SOCKET_RECONNECT) - if (m_casClientSocket != nullptr) { - int status = m_casClientSocket->GetStatus(); - if (status == SOCKET_STATUS_RUNNING) { - return true; - } - } - return false; -#else - // always return true to avoid reconnect dialog - return true; -#endif + return m_isconnect; +} + +void CasController::SetConnectStatus(bool status) +{ + m_isconnect = status; } void CasController::ProcessEnterBackground() @@ -992,14 +373,11 @@ void CasController::ProcessEnterBackground() map parameters = { { KEY_COMMAND, pauseCmd }, { KEY_SESSION_ID, m_sessionId } }; SendCommand(parameters); - StopDecWorker(!m_retainVideoDecode); - ClearDataStream(); + m_casHandlerManager->stopVideoHandler(); } bool CasController::ProcessEnterForeground(ANativeWindow *nativeWindow) { - ClearDataStream(); - if (m_sessionId.empty()) { ERR("SessionId is empty."); return false; @@ -1013,49 +391,17 @@ bool CasController::ProcessEnterForeground(ANativeWindow *nativeWindow) ERR("Failed to send resume command"); return false; } + SetState(START_SUCCESS); + m_casHandlerManager->setVideoParameters(m_nativeWindow, m_frameType, m_rotationDegrees); + m_casHandlerManager->startVideoHandler(); return true; } -bool CasController::CreateDecWorker(ANativeWindow *nativeWindow, bool needVideoDecode) -{ - std::lock_guard lockGuard(this->m_decoderLock); - if (needVideoDecode) { - m_videoDecodeThread = new (std::nothrow) CasVideoHDecodeThread(nativeWindow, m_frameType, m_rotationDegrees); - if (m_videoDecodeThread == nullptr) { - ERR("Failed to new video decode thread."); - return false; - } - m_videoDecodeThread->SetDecodePktHandle(m_videoPacketStream); - } - return true; -} - -void CasController::StartDecWorker(bool retainVideoDecode) -{ - std::lock_guard lockGuard(this->m_decoderLock); - if (retainVideoDecode) { - if (m_videoDecodeThread != nullptr) { - m_videoDecodeThread->Restart(); - } - } else { - m_videoDecodeThread->Start(); - } -} - -void CasController::StopDecWorker(bool retainVideoDecode) +void CasController::ResetDecoder() { - std::lock_guard lockGuard(this->m_decoderLock); - if (m_videoDecodeThread == nullptr) { - return; - } - - if (retainVideoDecode) { - m_videoDecodeThread->Stop(); - } else { - m_videoDecodeThread->Exit(); - delete m_videoDecodeThread; - m_videoDecodeThread = nullptr; - } + m_casHandlerManager->stopVideoHandler(); + m_casHandlerManager->setVideoParameters(m_nativeWindow, m_frameType, m_rotationDegrees); + m_casHandlerManager->startVideoHandler(); } bool CasController::ForceIFrame() @@ -1065,12 +411,11 @@ bool CasController::ForceIFrame() INFO("Force IFrame failed because phone already stop."); return false; } + return doForceIFrame(); +} - if (m_casClientSocket == nullptr || m_casClientSocket->GetStatus() != SOCKET_STATUS_RUNNING) { - ERR("Force IFrame failed because socket status not running."); - return false; - } - +bool CasController::doForceIFrame() +{ map parameters = { { KEY_COMMAND, CMD_REQ_IFRAME }, { KEY_SESSION_ID, m_sessionId } }; bool res = SendCommand(parameters); if (!res) { @@ -1083,17 +428,9 @@ bool CasController::ForceIFrame() void CasController::NotifyCommand(int type, string msg) { std::lock_guard lockGuard(this->m_callbackLock); - if (cmdCallBack != nullptr) { - cmdCallBack(type, std::move(msg)); - } -} - -void CasController::NotifyFirstVideoFrame() -{ - std::lock_guard lockGuard(this->m_callbackLock); - if (!m_isNotifyFirstFrame) { - m_isNotifyFirstFrame = true; - cmdCallBack(CAS_FIRST_FRAME, CasMsgCode::GetMsg(CAS_FIRST_FRAME)); + if (m_casCmdCallbck != nullptr) { + INFO("msg = %s", msg.c_str()); + m_casCmdCallbck(type, msg, m_callbackOpaque); } } @@ -1108,20 +445,32 @@ void CasController::OnCmdRecv(int code, string msg) ERR("Failed to send start command"); } } else { + INFO("code %d msg = %s", code, msg.c_str()); NotifyCommand(code, msg); } } -void CasController::ResetDecoder(bool isClearStream) +void CasController::onMessageRecv(stream_msg_head_t *header, uint8_t *body, int length) { - StopDecWorker(false); - if (isClearStream) { - if (m_videoPacketStream != nullptr) { - m_videoPacketStream->Clear(); + if (header->type == CasMsgType::Orientation && !m_snapshotFlag) { + int orientation = (int)body[0]; + INFO("ORIENTATION is %d, old ORIENTTION %d", orientation, m_orientation); + if (orientation == 8) { + m_rotationDegrees = 270; + } else if (orientation == 24) { + m_rotationDegrees = 90; + } else if (orientation == 16) { + m_rotationDegrees = 180; + } else { + m_rotationDegrees = 0; } + ResetDecoder(); + doForceIFrame(); + m_orientation = orientation; + } + if (m_casMessageCallback != nullptr) { + m_casMessageCallback(header, body, length, m_callbackOpaque); } - CreateDecWorker(m_nativeWindow, m_needVideoDecode); - StartDecWorker(false); } bool CasController::SendStartCmd() @@ -1145,214 +494,177 @@ bool CasController::SendStartCmd() { KEY_REGION_ID, m_conf.regionId }, { KEY_MEDIA_CONFIG, mediaConfigStr }, { KEY_MAX_DISCONNECT_DURATION, m_maxDisconnectDuration } }; - + if (m_snapshotFlag) { + parameters["operate_type"] = "screenshot"; + } return SendCommand(parameters); } -void CasController::RecvdVideoData(uint8_t *data, int length) +std::string CasController::GetSimpleRecvStats() { - if (m_videoPacketStream != nullptr) { - uint8_t *videoData = new uint8_t[length]; - memcpy(videoData, data, length); - m_videoPacketStream->Handle(videoData); - CalculateFPS(); - } - m_isMTransValid = true; -} + std::string statsString; + std::stringstream stream; -void CasController::RecvdAudioData(uint8_t *data, int length) -{ - if (m_audioPacketStream != nullptr) { - uint8_t *audioData = new uint8_t[length]; - memcpy(audioData, data, length); - m_audioPacketStream->Handle(audioData); + uint64_t lag = GetLag() / 1000; + if (lag >= 600) { + stream << "600+ms"; + } else { + stream << lag << "ms"; } -} -bool CasController::IsMtransValid() -{ - return m_isMTransValid; + stream << " " << GetCurrentFPS() << "FPS"; + statsString = stream.str(); + return statsString; } -int32_t OnRecvVideoStreamData(uint8_t* data, uint32_t length) +std::string CasController::GetVideoRecvStats() { - CasController::GetInstance()->RecvdVideoData(data, length); - return 0; -} + std::string statsString; + std::stringstream stream; -int32_t OnRecvAudioStreamData(uint8_t* data, uint32_t length) -{ - int headerLen = 36; - - stream_msg_head_t msgHead; - msgHead.size = length; - msgHead.checksum = CAS_MSG_CHECKSUM_AUDIO; - msgHead.magicword = CAS_STREAM_DELIMITER_MAGICWORD; - msgHead.type = Audio; - msgHead.SetPayloadSize(length); - - size_t dataLen = sizeof(stream_msg_head_t) + length; - char *outBuffer = (char *)malloc(dataLen); - if (outBuffer == nullptr) { - ERR("Failed to malloc out buffer."); - return 0; + StreamRecvStats videoRecvStats; + if (m_casTransmission->getRecvStats(Video, &videoRecvStats) == 0) { + stream << "下行视频丢包 : " << videoRecvStats.lostRate << std::endl; + stream << "视频接收码率 : " << videoRecvStats.recvBitrate << std::endl; } - if (EOK != memcpy_s(outBuffer, dataLen, &msgHead, sizeof(stream_msg_head_t))) { - ERR("Copy msg head fail."); - free(outBuffer); - return 0; + StreamRecvStats audioRecvStats; + if (m_casTransmission->getRecvStats(Audio, &audioRecvStats) == 0) { + stream << "音频接受码率 : " << audioRecvStats.recvBitrate << std::endl; } - if (EOK != memcpy_s(outBuffer + sizeof(stream_msg_head_t), dataLen - sizeof(stream_msg_head_t), data, length)) { - ERR("Copy msg data fail."); - free(outBuffer); - return 0; + StreamSendStats cmdSendStats; + if (m_casTransmission->getRecvStats(CmdControl, &cmdSendStats) == 0) { + stream << "指令发送码率(需操作) : " << cmdSendStats.encBitrate << std::endl; } + stream << "帧率 : " << GetCurrentFPS() << "fps" << std::endl; + stream << "策略丢帧 : " << CasVideoUtil::GetInstance()->GetCurrentDropFPS() << "fps" << std::endl; - CasController::GetInstance()->RecvdAudioData(reinterpret_cast(outBuffer), dataLen); - return 0; + statsString = stream.str(); + return statsString; } -int32_t OnRecvAudioDecodeCallback(int32_t streamId, AudioJbDecode* audioJbDecode) +bool CasController::prepareParameters() { - if (audioJbDecode->inputLength > 0) { - g_plcCount = 0; - int opusSize = 240; - int pcmLen = opus_decode(g_opusDecoder, audioJbDecode->payload + 8, opusSize, audioJbDecode->outputData, 480, 0); - audioJbDecode->frameType = 1; - audioJbDecode->outputLength = pcmLen * 2; - } else { - int pcmLen = opus_decode(g_opusDecoder, audioJbDecode->payload, 0, audioJbDecode->outputData, 480, 1); - audioJbDecode->frameType = 1; - audioJbDecode->outputLength = pcmLen * 2; - if (g_plcCount >= 4) { - errno_t et = memset_s(audioJbDecode->outputData, audioJbDecode->outputLength * 2, 0, audioJbDecode->outputLength * 2); - if (et != EOK) { - ERR("plc memset 0 error, err : %d", et); - } - } - g_plcCount++; - if (g_plcCount > 100) { - g_plcCount = 4; - } - } - return 0; -} + m_conf.parseConf(m_jniConf); + m_ticket = m_conf.ticket; + m_sessionId = m_conf.sessionId; + m_ip = TransIp(m_conf.ip.c_str()); + m_port = (unsigned short)m_conf.port; + m_encryptedData = m_conf.encryptedData; + m_verifyData = m_conf.verifyData; + m_authTs = m_conf.authTs; + m_aesIv = m_conf.aesIv; + m_clientType = CLIENT_TYPE; + m_maxDisconnectDuration = CalcMaxDisconnectDuration(m_conf.backgroundTimeout); -void OnGotTransLog(const char* str, uint32_t length) -{ - if (hrtpLogger == nullptr) { - return; + if (m_mediaConfig.find(KEY_FRAME_TYPE) != m_mediaConfig.end()) { + m_frameType = m_mediaConfig[KEY_FRAME_TYPE] == "h264" ? FrameType::H264 : FrameType::H265; } - hrtpLogger->info(str); + return true; } -void OnNeedKeyFrameCallback() +bool CasController::ProcessStart() { - INFO("Need key frame callback.."); - CasController::GetInstance()->NotifyCommand(CAS_REQUEST_CAMERA_KEY_FRAME, "camera need key frame"); -} + m_casTransmission = std::make_shared(!m_snapshotFlag); + if (m_casTransmission == nullptr) { + ERR("Couldn't create CasTransmission."); + return false; + } + m_handlerResultProcessor = std::make_shared(this); + if (m_handlerResultProcessor == nullptr) { + ERR("Couldn't create CasHandlerResultProcessor."); + goto FAILED; + } + m_casHandlerManager = std::make_shared(m_handlerResultProcessor); + if (m_casHandlerManager == nullptr) { + ERR("Couldn't create CasHandlerManager."); + goto FAILED; + } + m_streamBuildSender = std::make_shared(m_casTransmission); + if (m_streamBuildSender == nullptr) { + ERR("Couldn't create CasStreamBuildSender."); + goto FAILED; + } + m_cmdController = std::make_shared(m_streamBuildSender); + if (m_cmdController == nullptr) { + ERR("Couldn't create CasCmdController."); + goto FAILED; + } + m_touch = std::make_shared(m_casTransmission); + if (m_touch == nullptr) { + ERR("Couldn't create CasTouch."); + goto FAILED; + } + for (auto &it : m_casMessageTypes) { + m_casHandlerManager->createHandler(it); + } -int32_t OnRecvCmdData(uint8_t* data, uint32_t length) -{ - INFO("OnRecvCmdData"); - CasController::GetInstance()->HandleCmdData(reinterpret_cast(data), length); - return 0; -} + m_casTransmission->init(m_ip, m_port, m_logPath); + m_casTransmission->setListener(m_casHandlerManager); -void CasController::HandleCmdData(uint8_t *data, int length) -{ - streamMsgHead *msgHead = (streamMsgHead *)data; - INFO("Cmd callback size = %d, type = %d", length, msgHead->type); - if (msgHead->type <= Invalid || msgHead->type >= End) { - ERR("msgHead type is invalid : %d.", msgHead->type); - return; + m_casHandlerManager->init(); + if (!BuildConnection()) { + ERR("Connect failed."); + goto FAILED; } - CasPktHandle *serviceHandle = m_streamParser->GetServiceHandle(msgHead->type); - if (serviceHandle) { - void *pTmp = AllocBuffer(length); - if (pTmp != nullptr) { - if (EOK != memcpy_s(pTmp, length, data, length)) { - ERR("Memory copy data fail."); - return; - } - serviceHandle->Handle((void *)pTmp); - } + if (m_casTransmission->start() != 0) { + ERR("Failed to build connection"); + goto FAILED; } -} + m_casHandlerManager->setVideoParameters(m_nativeWindow, m_frameType, m_rotationDegrees); + m_casHandlerManager->setStreamBuilder(m_streamBuildSender); + m_casHandlerManager->start(); + if (!SendStartCmd()) { + ERR("Send start cmd failed."); + goto FAILED; + } + return true; -std::string CasController::GetSimpleRecvStats() { - std::string statsString; - std::stringstream stream; +FAILED: + ReleaseResource(); + return false; +} - uint64_t lag = GetLag() / 1000; - if (lag >= 600) { - stream << "600+ms"; - } else { - stream << lag << "ms"; +bool CasController::ProcessReconnect() +{ + m_casHandlerManager->stop(false); + NotifyCommand(CAS_RECONNECTING, CasMsgCode::GetMsg(CAS_RECONNECTING)); + int connectRes = m_casTransmission->reconnect(); + if (connectRes != 0) { + this->SetState(CONNECTION_FAILURE); + return false; } -#if MTRANS_ENABLED - if (m_mtrans != nullptr && m_isMTransValid) { - StreamRecvStats stats{}; - if (0 != m_mtrans->GetVideoRecvStats(&stats)) { - WARN("GetSimpleRecvStats failed"); - } else { - stream << " " << stats.recvBitrate << "kbps"; - } + if (m_sessionId.empty()) { + ERR("SessionId is empty."); + NotifyCommand(CAS_RECONNECT_PARAMETER_INVALID, CasMsgCode::GetMsg(CAS_RECONNECT_PARAMETER_INVALID)); + return false; } -#endif - - stream << " " << GetCurrentFPS() << "FPS"; - statsString = stream.str(); - return statsString; -} + this->SetState(CONNECTED); + NotifyCommand(CAS_RECONNECT_SUCCESS, CasMsgCode::GetMsg(CAS_RECONNECT_SUCCESS)); -std::string CasController::GetVideoRecvStats() { - std::string statsString; - std::stringstream stream; -#if MTRANS_ENABLED - if (m_mtrans != nullptr && m_isMTransValid) { - - uint64_t lag = GetLag() / 1000; - stream << "网络时延 : "; - if (lag != 0) { - if (lag >= 600) { - stream << "600+ms"; - } else { - stream << lag << "ms"; - } - } - stream << std::endl; + std::string reconnectCmd = CMD_RECONNECT; + map parameters = { { KEY_COMMAND, reconnectCmd }, { KEY_SESSION_ID, m_sessionId } }; - StreamRecvStats stats{}; - if (0 != m_mtrans->GetVideoRecvStats(&stats)) { - WARN("GetVideoRecvStats failed"); - } else { - stream << "下行视频丢包 : " << stats.lostRate << std::endl; - stream << "视频接收码率 : " << stats.recvBitrate << "kbps" << std::endl; - } - StreamRecvStats audioStats{}; - if (0 != m_mtrans->GetAudioRecvStats(&audioStats)) { - WARN("GetAudioRecvStats failed"); - } else { - stream << "音频接收码率 : " << audioStats.recvBitrate << "kbps" << std::endl; - } - StreamSendStats cmdSendStats{}; - if (0 != m_mtrans->GetCmdSendStats(&cmdSendStats)) { - WARN("GetCmdSendStats failed"); - } else { - stream << "指令发送码率(需操作) : " << cmdSendStats.encBitrate << "kbps" << std::endl; - } + bool res = SendCommand(parameters); + if (!res) { + ERR("Failed to send reconnect command"); + return false; } -#endif - stream << "帧率 : " << GetCurrentFPS() << "fps" << std::endl; - stream << "策略丢帧 : " << CasVideoUtil::GetInstance()->GetCurrentDropFPS() << "fps" << std::endl; + m_casHandlerManager->start(false); + SetConnectStatus(true); + SetState(START_SUCCESS); + return true; +} - statsString = stream.str(); - return statsString; +void CasController::ReleaseResource() +{ + if (m_casHandlerManager != nullptr) { + m_casHandlerManager->stop(); + } } -void CasController::CalculateFPS() { +void CasController::CalculateFPS() +{ uint64_t currentTime = CasVideoUtil::GetInstance()->GetNow(); m_videoDataCount++; @@ -1367,10 +679,11 @@ void CasController::CalculateFPS() { } } -int CasController::GetCurrentFPS() const { +int CasController::GetCurrentFPS() const +{ uint64_t currentTime = CasVideoUtil::GetInstance()->GetNow(); if (currentTime - m_lastTimeRefreshFPS > 2 * DURATION_USEC) { return 0; } return m_currentFPS; -} \ No newline at end of file +} diff --git a/cloudphone/src/main/cpp/CasController.h b/cloudphone/src/main/cpp/CasController.h index 86def0032e52c774a3a4f92f7f2543d4eff9af06..95d1e22d86cc52a0accd77ced980fd96a2e019f3 100644 --- a/cloudphone/src/main/cpp/CasController.h +++ b/cloudphone/src/main/cpp/CasController.h @@ -12,12 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef CLOUDAPPSDK_CASCONTROLLRT_H -#define CLOUDAPPSDK_CASCONTROLLRT_H +#ifndef CLOUDAPPSDK_CASCONTROLLERT_H +#define CLOUDAPPSDK_CASCONTROLLERT_H #include #include #include +#include +#include #include "CasDataPipe.h" #include "CasTouch.h" #include "CasConf.h" @@ -32,14 +34,18 @@ #include "CasVideoHDecodeThread.h" #include "CasStreamRecvParser.h" #include "CasStreamBuildSender.h" +#include "cas_controller/CasHandlerResultProcessor.h" #include "libs/mtrans/include/net_trans.h" -class CasController : public CasControllerListener { -public: - static CasController *GetInstance(); +class CasTransmission; +class CasHandlerManager; +class CasHandlerResultProcessor; - static bool DestroyInstance(); +typedef void *(*CasCmdCallback)(int, std::string &, void*); +typedef void *(*CasMessageCallback)(stream_msg_head_t*, uint8_t*, int, void*); +class CasController { +public: CasController(); ~CasController(); @@ -48,8 +54,16 @@ public: bool Stop(bool isHome); + bool Pause(); + + bool Resume(ANativeWindow *nativeWindow); + bool Reconnect(); + bool StartScreenshot(); + + bool StopScreenshot(); + bool SetMediaConfig(std::map mediaConfig); void SetJniConf(std::string key, std::string value); @@ -60,120 +74,72 @@ public: bool SendMotionEvent(uint16_t masterAxis, int32_t masterValue, uint16_t secondaryAxis, int32_t secondaryValue); - void SetCmdCallBackMethod(void *(*method)(int type, std::string msg)) + void SetCmdCallBackMethod(CasCmdCallback method1, CasMessageCallback method2, void *opaque) { - cmdCallBack = method; - } - - CasVideoHDecodeThread* GetVideoDecodeThread() - { - return m_videoDecodeThread; + m_casCmdCallbck = method1; + m_casMessageCallback = method2; + m_callbackOpaque = opaque; } int JniSendData(CasMsgType type, uint8_t *data, int length); - int JniRecvData(CasMsgType type, uint8_t *data, int length); + void SetLogPath(std::string &path); uint64_t GetLag(); + void SetLag(uint64_t lag) { + m_lag = lag; + } + JNIState GetState(); void SetState(JNIState state); bool GetConnectStatus(); - void NotifyFirstVideoFrame(); - void NotifyCommand(int type, std::string msg); - void RecvdVideoData(uint8_t *data, int length); - - void RecvdAudioData(uint8_t *data, int length); - - void HandleCmdData(uint8_t *data, int length); - std::string GetVideoRecvStats(); std::string GetSimpleRecvStats(); - bool IsMtransValid(); private: bool Release(); + void OnCmdRecv(int code, std::string msg); - void IsNeedRotation(int orientation); - - void ResetDecoder(bool isClearStream); + void onMessageRecv(stream_msg_head_t *header, uint8_t *body, int length); + friend class CasHandlerManager; + friend class CasHandlerResultProcessor; +private: + bool prepareParameters(); + bool ProcessStart(); + bool ProcessReconnect(); + void ReleaseResource(); bool SendStartCmd(); - - bool CreateWorkers(); - - bool StartWorkers(); - - bool DestroyWorkers(); - - bool CreateDecWorker(ANativeWindow *nativeWindow, bool needVideoDecode); - - void StartDecWorker(bool retainVideoDecode); - - void StopDecWorker(bool retainVideoDecode); - - bool InitDataStream(); - - bool ClearDataStream(); - - bool CloseDataStream(); - bool BuildConnection(); - bool SendCommand(std::map parameters); - void ProcessEnterBackground(); - bool ProcessEnterForeground(ANativeWindow *nativeWindow); - + void ResetDecoder(); bool ForceIFrame(); - - void OnCmdRecv(int code, std::string msg); - bool IsValidMediaConfig(std::map mediaConfig); - std::string CalcMaxDisconnectDuration(std::string backgroundTimeout); - void CalculateFPS(); int GetCurrentFPS() const; + void SetConnectStatus(bool status); + bool doForceIFrame(); - void *(*cmdCallBack)(int type, std::string msg) = nullptr; - - static CasController *g_instance; - CasDataPipe *m_videoPacketStream = nullptr; - CasDataPipe *m_audioPacketStream = nullptr; - CasDataPipe *m_orientationStream = nullptr; - CasDataPipe *m_controlStream = nullptr; - CasDataPipe *m_channelStream = nullptr; - CasDataPipe *m_virtualDeviceStream = nullptr; - CasDataPipe *m_imeDataStream = nullptr; - - CasCmdController *m_cmdController = nullptr; - CasHeartbeatController *m_heartbeatController = nullptr; - CasStreamParseThread *m_streamParseThread = nullptr; - CasCmdControlThread *m_controlThread = nullptr; - CasHeartbeatThread *m_heartbeatThread = nullptr; - CasSocket *m_casClientSocket = nullptr; - CasStreamBuildSender *m_streamBuildSender = nullptr; - CasStreamRecvParser *m_streamParser = nullptr; - CasVideoHDecodeThread *m_videoDecodeThread = nullptr; - NetTrans *m_mtrans = nullptr; + CasCmdCallback m_casCmdCallbck = nullptr; + CasMessageCallback m_casMessageCallback = nullptr; std::map m_jniConf; enum JNIState m_state = INIT; - CasTouch *m_touch = nullptr; CasConf m_conf; std::mutex m_jniConfLock; std::mutex m_lock; std::mutex m_callbackLock; - std::mutex m_decoderLock; std::string m_sessionId; std::string m_ticket; std::string m_encryptedData; @@ -186,17 +152,24 @@ private: ANativeWindow *m_nativeWindow; unsigned int m_ip = 0; unsigned short m_port = 0; - const bool m_retainVideoDecode = true; - const bool m_needVideoDecode = true; - bool m_isNotifyFirstFrame = false; std::map m_mediaConfig; int m_orientation = 0; int m_rotationDegrees = 0; - bool m_isMTransValid = false; int m_videoDataCount = 0; int m_currentFPS = 0; uint64_t m_lastTimeRefreshFPS = 0; - std::atomic_bool m_logInit {false}; + void *m_callbackOpaque = nullptr; + bool m_snapshotFlag = false; + std::shared_ptr m_casTransmission = nullptr; + std::shared_ptr m_touch = nullptr; + std::shared_ptr m_casHandlerManager = nullptr; + std::shared_ptr m_cmdController = nullptr; + std::shared_ptr m_streamBuildSender = nullptr; + std::shared_ptr m_handlerResultProcessor = nullptr; + std::vector m_casMessageTypes; + bool m_isconnect; + std::string m_logPath{}; + uint64_t m_lag; }; -#endif // CLOUDAPPSDK_CASCONTROLLRT_H \ No newline at end of file +#endif // CLOUDAPPSDK_CASCONTROLLERT_H \ No newline at end of file diff --git a/cloudphone/src/main/cpp/CasJniBridge.cpp b/cloudphone/src/main/cpp/CasJniBridge.cpp index b4b470e2783ca5bfff157a910cfcaf84a649e448..a029150a7dac4b8c432b770b089d4555932ede31 100644 --- a/cloudphone/src/main/cpp/CasJniBridge.cpp +++ b/cloudphone/src/main/cpp/CasJniBridge.cpp @@ -28,12 +28,10 @@ using namespace std; ANativeWindow *gANativeWindow; -CasController *gJniApiCtrl = CasController::GetInstance(); - // 用于jni回调java static JavaVM *g_JVM = nullptr; -static jobject jniCallback = nullptr; -void *invokeCmdCallBack(int type, string msg); +void *invokeCmdCallBack(int type, string &msg, void*); +void *invokeMessageCallBack(stream_msg_head_t *header, uint8_t *body, int length, void *opaque); static std::string jstring2string(JNIEnv *env, jstring jStr) { @@ -52,15 +50,39 @@ static std::string jstring2string(JNIEnv *env, jstring jStr) return ret; } -JNIEXPORT jboolean JNICALL JNI(start)(JNIEnv *env, jclass, jobject surface, jboolean isHome) +JNIEXPORT jlong JNICALL JNI(init)(JNIEnv *env, jobject obj, jstring logPath) +{ + CasController *casController = new (std::nothrow) CasController(); + if (casController != nullptr) { + std::string path = jstring2string(env, logPath); + casController->SetLogPath(path); + } + return reinterpret_cast(casController); +} + +JNIEXPORT void JNICALL JNI(deinit)(JNIEnv *env, jobject clazz, jlong nativeObject) { + CasController *casController = reinterpret_cast(nativeObject); + if (casController != nullptr) { + delete casController; + } +} + +JNIEXPORT jboolean JNICALL JNI(start)(JNIEnv *env, jobject clazz, jlong nativeObject, + jobject surface, jboolean isHome) +{ + CasController *casController = reinterpret_cast(nativeObject); + if (casController == nullptr) { + ERR("native CasController object is nullptr."); + return false; + } if (surface == nullptr) { ERR("Native window is null"); return JNI_FALSE; } gANativeWindow = ANativeWindow_fromSurface(env, surface); - if (gJniApiCtrl->Start(gANativeWindow, isHome)) { + if (casController->Start(gANativeWindow, isHome)) { return JNI_TRUE; } return JNI_FALSE; @@ -68,9 +90,14 @@ JNIEXPORT jboolean JNICALL JNI(start)(JNIEnv *env, jclass, jobject surface, jboo #if defined(RECONNECT) -JNIEXPORT jboolean JNICALL JNI(reconnect)(JNIEnv *env, jclass) +JNIEXPORT jboolean JNICALL JNI(reconnect)(JNIEnv *env, jobject clazz, jlong nativeObject) { - if (gJniApiCtrl->Reconnect()) { + CasController *casController = reinterpret_cast(nativeObject); + if (casController == nullptr) { + ERR("native CasController object is nullptr."); + return false; + } + if (casController->Reconnect()) { return JNI_TRUE; } return JNI_FALSE; @@ -78,17 +105,69 @@ JNIEXPORT jboolean JNICALL JNI(reconnect)(JNIEnv *env, jclass) #endif -JNIEXPORT void JNICALL JNI(stop)(JNIEnv *, jclass, jboolean isHome) +JNIEXPORT void JNICALL JNI(stop)(JNIEnv *, jobject clazz, jlong nativeObject, jboolean isHome) { - gJniApiCtrl->Stop(isHome); - if (!isHome && gANativeWindow != nullptr) { + CasController *casController = reinterpret_cast(nativeObject); + if (casController == nullptr) { + ERR("native CasController object is nullptr."); + return; + } + casController->Stop(isHome); + if (gANativeWindow != nullptr) { ANativeWindow_release(gANativeWindow); gANativeWindow = nullptr; } } -JNIEXPORT jboolean JNICALL JNI(setMediaConfig)(JNIEnv *env, jclass, jobject mediaConfig) +JNIEXPORT jboolean JNICALL JNI(pause)(JNIEnv *, jobject clazz, jlong nativeObject) +{ + CasController *casController = reinterpret_cast(nativeObject); + if (casController == nullptr) { + ERR("native CasController object is nullptr."); + return false; + } + return casController->Pause(); +} + +JNIEXPORT jboolean JNICALL JNI(resume)(JNIEnv *env, jobject clazz, jlong nativeObject, jobject surface) +{ + CasController *casController = reinterpret_cast(nativeObject); + if (casController == nullptr) { + ERR("native CasController object is nullptr."); + return false; + } + gANativeWindow = ANativeWindow_fromSurface(env, surface); + return casController->Resume(gANativeWindow); +} + +JNIEXPORT jboolean JNICALL JNI(startScreenshot)(JNIEnv *env, jobject clazz, jlong nativeObject) +{ + CasController *casController = reinterpret_cast(nativeObject); + if (casController == nullptr) { + ERR("native CasController object is nullptr."); + return false; + } + return casController->StartScreenshot(); +} + +JNIEXPORT jboolean JNICALL JNI(stopScreenshot)(JNIEnv *env, jobject clazz, jlong nativeObject) { + CasController *casController = reinterpret_cast(nativeObject); + if (casController == nullptr) { + ERR("native CasController object is nullptr."); + return false; + } + return casController->StopScreenshot(); +} + +JNIEXPORT jboolean JNICALL JNI(setMediaConfig)(JNIEnv *env, jobject clazz, + jlong nativeObject, jobject mediaConfig) +{ + CasController *casController = reinterpret_cast(nativeObject); + if (casController == nullptr) { + ERR("native CasController object is nullptr."); + return false; + } jclass hashMapClass = env->FindClass("java/util/HashMap"); jmethodID entrySetMID = env->GetMethodID(hashMapClass, "entrySet", "()Ljava/util/Set;"); @@ -125,110 +204,157 @@ JNIEXPORT jboolean JNICALL JNI(setMediaConfig)(JNIEnv *env, jclass, jobject medi mediaConfigMap.insert(pair(mediaConfigKeyStr, mediaConfigValueStr)); } - if (gJniApiCtrl->SetMediaConfig(mediaConfigMap)) { + if (casController->SetMediaConfig(mediaConfigMap)) { return JNI_TRUE; } return JNI_FALSE; } -JNIEXPORT void JNICALL JNI(setJniConf)(JNIEnv *env, jclass, jstring jKey, jstring jValue) +JNIEXPORT void JNICALL JNI(setJniConf)(JNIEnv *env, jobject clazz, jlong nativeObject, jstring jKey, jstring jValue) { + CasController *casController = reinterpret_cast(nativeObject); + if (casController == nullptr) { + ERR("native CasController object is nullptr."); + return; + } std::string key = jstring2string(env, jKey); std::string value = jstring2string(env, jValue); - gJniApiCtrl->SetJniConf(key, value); + casController->SetJniConf(key, value); } -JNIEXPORT jboolean JNICALL JNI(getConnectStatus)(JNIEnv *env, jclass) +JNIEXPORT jboolean JNICALL JNI(getConnectStatus)(JNIEnv *env, jobject clazz, jlong nativeObject) { - if (gJniApiCtrl->GetConnectStatus()) { + CasController *casController = reinterpret_cast(nativeObject); + if (casController == nullptr) { + ERR("native CasController object is nullptr."); + return false; + } + if (casController->GetConnectStatus()) { return JNI_TRUE; } return JNI_FALSE; } -int gRotation; -int _gRotation; - -void doRotateSync() -{ - _gRotation = gRotation; -} - -extern "C" JNIEXPORT jboolean JNICALL JNICALL JNI(setRotation)(JNIEnv *env, jclass, jint rotation) +extern "C" JNIEXPORT jboolean JNICALL JNICALL JNI(setRotation)(JNIEnv *env, jobject clazz, + jlong nativeObject, jint rotation) { - doRotateSync(); - gRotation = rotation; + CasController *casController = reinterpret_cast(nativeObject); + if (casController == nullptr) { + ERR("native CasController object is nullptr."); + return false; + } return JNI_TRUE; } -extern "C" JNIEXPORT jint JNICALL JNI(recvData)(JNIEnv *env, jclass, jbyte type, jbyteArray jData, jint length) +extern "C" JNIEXPORT jint JNICALL JNI(sendData)(JNIEnv *env, jobject clazz, jlong nativeObject, + jbyte type, jbyteArray jData, jint length) { + CasController *casController = reinterpret_cast(nativeObject); + if (casController == nullptr) { + ERR("native CasController object is nullptr."); + return -1; + } uint8_t *data = (uint8_t *)env->GetByteArrayElements(jData, nullptr); - int ret = gJniApiCtrl->JniRecvData((CasMsgType)type, data, length); - env->ReleaseByteArrayElements(jData, (jbyte *)data, JNI_ABORT); - return ret; -} - -extern "C" JNIEXPORT jint JNICALL JNI(sendData)(JNIEnv *env, jclass clazz, jbyte type, jbyteArray jData, jint length) { - uint8_t *data = (uint8_t *)env->GetByteArrayElements(jData, nullptr); - int ret = gJniApiCtrl->JniSendData((CasMsgType)type, data, length); + int ret = casController->JniSendData((CasMsgType)type, data, length); env->ReleaseByteArrayElements(jData, (jbyte *)data, JNI_ABORT); return ret; } -extern "C" JNIEXPORT jboolean JNICALL JNI(sendTouchEvent)(JNIEnv *env, jclass, jint id, jint action, jint x, jint y, +extern "C" JNIEXPORT jboolean JNICALL JNI(sendTouchEvent)(JNIEnv *env, jobject clazz, + jlong nativeObject, jint id, jint action, jint x, jint y, jint pressure, jlong time, jint orientation, jint height, jint width) { - if (gJniApiCtrl->SendTouchEvent(id, action, x, y, pressure, time, orientation, height, width)) { + CasController *casController = reinterpret_cast(nativeObject); + if (casController == nullptr) { + ERR("native CasController object is nullptr."); + return false; + } + if (casController->SendTouchEvent(id, action, x, y, pressure, time, orientation, height, width)) { return JNI_TRUE; } return JNI_FALSE; } -extern "C" JNIEXPORT jboolean JNICALL JNI(sendKeyEvent)(JNIEnv *env, jclass, jint keycode, jint action) +extern "C" JNIEXPORT jboolean JNICALL JNI(sendKeyEvent)(JNIEnv *env, jobject clazz, + jlong nativeObject, jint keycode, jint action) { - if (gJniApiCtrl->SendKeyEvent(keycode, action)) { + CasController *casController = reinterpret_cast(nativeObject); + if (casController == nullptr) { + ERR("native CasController object is nullptr."); + return false; + } + if (casController->SendKeyEvent(keycode, action)) { return JNI_TRUE; } return JNI_FALSE; } -extern "C" JNIEXPORT jboolean JNICALL JNI(sendMotionEvent)(JNIEnv *env, jclass, jint masterAxis, - jint masterValue, jint secondaryAxis, jint secondaryValue) +extern "C" JNIEXPORT jboolean JNICALL JNI(sendMotionEvent)(JNIEnv *env, jobject clazz, + jlong nativeObject, jint masterAxis, jint masterValue, jint secondaryAxis, jint secondaryValue) { - if (gJniApiCtrl->SendMotionEvent(masterAxis, masterValue, secondaryAxis, secondaryValue)) { + CasController *casController = reinterpret_cast(nativeObject); + if (casController == nullptr) { + ERR("native CasController object is nullptr."); + return false; + } + if (casController->SendMotionEvent(masterAxis, masterValue, secondaryAxis, secondaryValue)) { return JNI_TRUE; } return JNI_FALSE; } -extern "C" JNIEXPORT jint JNICALL JNI(getJniStatus)(JNIEnv *env, jclass) +extern "C" JNIEXPORT jint JNICALL JNI(getJniStatus)(JNIEnv *env, jobject clazz, jlong nativeObject) { - int statusCode = gJniApiCtrl->GetState(); + CasController *casController = reinterpret_cast(nativeObject); + if (casController == nullptr) { + ERR("native CasController object is nullptr."); + return -1; + } + int statusCode = casController->GetState(); return statusCode; } -extern "C" JNIEXPORT jint JNICALL JNI(getLag)(JNIEnv *env, jclass) +extern "C" JNIEXPORT jint JNICALL JNI(getLag)(JNIEnv *env, jobject clazz, jlong nativeObject) { - int lag = static_cast(gJniApiCtrl->GetLag() / 1000); + CasController *casController = reinterpret_cast(nativeObject); + if (casController == nullptr) { + ERR("native CasController object is nullptr."); + return 0; + } + int lag = static_cast(casController->GetLag() / 1000); return lag; } -extern "C" JNIEXPORT jstring JNICALL JNI(getVideoStreamStats)(JNIEnv *env, jclass) +extern "C" JNIEXPORT jstring JNICALL JNI(getVideoStreamStats)(JNIEnv *env, jobject clazz, jlong nativeObject) { - string statsString = gJniApiCtrl->GetVideoRecvStats(); + CasController *casController = reinterpret_cast(nativeObject); + if (casController == nullptr) { + ERR("native CasController object is nullptr."); + return env->NewStringUTF("null"); + } + string statsString = casController->GetVideoRecvStats(); return env->NewStringUTF(statsString.c_str()); } -extern "C" JNIEXPORT jstring JNICALL JNI(getSimpleStreamStats)(JNIEnv *env, jclass) +extern "C" JNIEXPORT jstring JNICALL JNI(getSimpleStreamStats)(JNIEnv *env, jobject clazz, jlong nativeObject) { - string statsString = gJniApiCtrl->GetSimpleRecvStats(); + CasController *casController = reinterpret_cast(nativeObject); + if (casController == nullptr) { + ERR("native CasController object is nullptr."); + return env->NewStringUTF("null"); + } + string statsString = casController->GetSimpleRecvStats(); return env->NewStringUTF(statsString.c_str()); } -extern "C" JNIEXPORT void JNICALL JNI(setAssetsData)(JNIEnv *env, jclass, jstring jFilename, jbyteArray jData, - jint length) +extern "C" JNIEXPORT void JNICALL JNI(setAssetsData)(JNIEnv *env, jobject clazz, jlong nativeObject, + jstring jFilename, jbyteArray jData, jint length) { + CasController *casController = reinterpret_cast(nativeObject); + if (casController == nullptr) { + ERR("native CasController object is nullptr."); + return; + } const char *filename = env->GetStringUTFChars(jFilename, 0); jbyte *data = env->GetByteArrayElements(jData, nullptr); @@ -237,19 +363,23 @@ extern "C" JNIEXPORT void JNICALL JNI(setAssetsData)(JNIEnv *env, jclass, jstrin env->ReleaseByteArrayElements(jData, data, 0); } -extern "C" JNIEXPORT void JNICALL JNI(registerCasJNICallback)(JNIEnv *env, jclass, jobject callback) +extern "C" JNIEXPORT void JNICALL JNI(registerCasJNICallback)(JNIEnv *env, jobject clazz, + jlong nativeObject, jobject callback) { - env->GetJavaVM(&g_JVM); - jniCallback = env->NewGlobalRef(callback); - gJniApiCtrl->SetCmdCallBackMethod(invokeCmdCallBack); + CasController *casController = reinterpret_cast(nativeObject); + if (casController == nullptr) { + ERR("native CasController object is nullptr."); + return; + } + jobject jniCallbackObject = env->NewGlobalRef(callback); + casController->SetCmdCallBackMethod(invokeCmdCallBack, invokeMessageCallBack, (void*)jniCallbackObject); } -void *invokeCmdCallBack(int type, string msg) +void *invokeCmdCallBack(int type, string &msg, void *opaque) { JNIEnv *env = nullptr; int mNeedDetach = 0; int getEnvStat = g_JVM->GetEnv((void **)&env, JNI_VERSION_1_4); - INFO("Get env stat %d type %d.", getEnvStat, type); if (getEnvStat == JNI_EDETACHED) { if (g_JVM->AttachCurrentThread(&env, nullptr) != 0) { ERR("Attach current thread failed."); @@ -257,24 +387,59 @@ void *invokeCmdCallBack(int type, string msg) } mNeedDetach = JNI_TRUE; } - jclass javaClass = env->GetObjectClass(jniCallback); + jobject jniCallbackObject = static_cast(opaque); + jclass javaClass = env->GetObjectClass(jniCallbackObject); if (javaClass == 0) { ERR("Unable to find class"); g_JVM->DetachCurrentThread(); return nullptr; } - jmethodID javaCallbackId = env->GetMethodID(javaClass, "onCmdReceive", "(ILjava/lang/String;)V"); if (javaCallbackId == nullptr) { ERR("Unable to find method."); return nullptr; } - env->CallVoidMethod(jniCallback, javaCallbackId, type, env->NewStringUTF(msg.c_str())); - INFO("Invoke cmd callback end."); + jstring jmessage = env->NewStringUTF(msg.c_str()); + env->CallVoidMethod(jniCallbackObject, javaCallbackId, type, jmessage); + env->DeleteLocalRef(javaClass); + if (mNeedDetach) { + g_JVM->DetachCurrentThread(); + } + return nullptr; +} + +void *invokeMessageCallBack(stream_msg_head_t *header, uint8_t *body, int length, void *opaque) +{ + JNIEnv *env = nullptr; + int mNeedDetach = 0; + int getEnvStat = g_JVM->GetEnv((void **)&env, JNI_VERSION_1_4); + if (getEnvStat == JNI_EDETACHED) { + if (g_JVM->AttachCurrentThread(&env, nullptr) != 0) { + ERR("Attach current thread failed."); + return nullptr; + } + mNeedDetach = JNI_TRUE; + } + jobject jniCallbackObject = static_cast(opaque); + jclass javaClass = env->GetObjectClass(jniCallbackObject); + if (javaClass == 0) { + ERR("Unable to find class"); + g_JVM->DetachCurrentThread(); + return nullptr; + } + jmethodID jmethodId = env->GetMethodID(javaClass, "onMessageReceive", "(I[BI)V"); + if (jmethodId == nullptr) { + ERR("Unable to find method."); + return nullptr; + } + jbyteArray bytesArray = env->NewByteArray(length); + env->SetByteArrayRegion(bytesArray, 0, length, (jbyte*)body); + env->CallVoidMethod(jniCallbackObject, jmethodId, header->type, bytesArray, length); + env->DeleteLocalRef(javaClass); + env->DeleteLocalRef(bytesArray); if (mNeedDetach) { g_JVM->DetachCurrentThread(); } - env = nullptr; return nullptr; } diff --git a/cloudphone/src/main/cpp/CasJniBridge.h b/cloudphone/src/main/cpp/CasJniBridge.h index 1c385b7a0e5756f4d67a60dbd741e76033a0ad70..e5307e049df0cb14181b8b212586b9e8830f0cc5 100644 --- a/cloudphone/src/main/cpp/CasJniBridge.h +++ b/cloudphone/src/main/cpp/CasJniBridge.h @@ -20,40 +20,58 @@ #define JNI(func) Java_com_huawei_cloudphone_jniwrapper_JNIWrapper_##func extern "C" { -JNIEXPORT void JNICALL JNI(setJniConf)(JNIEnv *env, jclass, jstring key, jstring value); -JNIEXPORT jboolean JNICALL JNI(start)(JNIEnv *env, jclass, jobject surface, jboolean isHome); +JNIEXPORT long JNICALL JNI(init)(JNIEnv *env, jobject obj, jstring logPath); -JNIEXPORT jboolean JNICALL JNI(reconnect)(JNIEnv *env, jclass); +JNIEXPORT void JNICALL JNI(deinit)(JNIEnv *env, jobject clazz, jlong nativeObject); -JNIEXPORT void JNICALL JNI(stop)(JNIEnv *env, jclass, jboolean isHome); +JNIEXPORT void JNICALL JNI(setJniConf)(JNIEnv *env, jobject clazz, jlong nativeObject, jstring key, jstring value); -JNIEXPORT jobject JNICALL JNI(getBitmap)(JNIEnv *env, jclass); +JNIEXPORT jboolean JNICALL JNI(start)(JNIEnv *env, jobject clazz, jlong nativeObject, jobject surface, jboolean isHome); -JNIEXPORT int JNICALL JNI(getJniStatus)(JNIEnv *env, jclass); +JNIEXPORT jboolean JNICALL JNI(reconnect)(JNIEnv *env, jobject clazz, jlong nativeObject); -JNIEXPORT int JNICALL JNI(getLag)(JNIEnv *env, jclass); +JNIEXPORT void JNICALL JNI(stop)(JNIEnv *env, jobject clazz, jlong nativeObject, jboolean isHome); -JNIEXPORT jstring JNICALL JNI(getVideoStreamStats)(JNIEnv *env, jclass); +JNIEXPORT jboolean JNICALL JNI(pause)(JNIEnv *env, jobject clazz, jlong nativeObject); -JNIEXPORT jstring JNICALL JNI(getSimpleStreamStats)(JNIEnv *env, jclass); +JNIEXPORT jboolean JNICALL JNI(resume)(JNIEnv *env, jobject clazz, jlong nativeObject, jobject surface); -JNIEXPORT jboolean JNICALL JNI(setMediaConfig)(JNIEnv *env, jclass, jobject mediaConfig); +JNIEXPORT jboolean JNICALL JNI(startScreenshot)(JNIEnv *env, jobject clazz, jlong nativeObject); -JNIEXPORT jint JNICALL JNI(recvData)(JNIEnv *env, jclass, jbyte type, jbyteArray data, int length); +JNIEXPORT jboolean JNICALL JNI(stopScreenshot)(JNIEnv *env, jobject clazz, jlong nativeObject); -JNIEXPORT jint JNICALL JNI(sendData)(JNIEnv *env, jclass clazz, jbyte type, jbyteArray data, jint length); +JNIEXPORT jobject JNICALL JNI(getBitmap)(JNIEnv *env, jobject clazz, jlong nativeObject); -JNIEXPORT jboolean JNICALL JNI(sendTouchEvent)(JNIEnv *env, jclass, int id, int action, int x, int y, int pressure, jlong time, - jint orientation, jint height, jint width); +JNIEXPORT int JNICALL JNI(getJniStatus)(JNIEnv *env, jobject clazz, jlong nativeObject); -JNIEXPORT jboolean JNICALL JNI(sendKeyEvent)(JNIEnv *env, jclass, jint keycode, jint action); +JNIEXPORT int JNICALL JNI(getLag)(JNIEnv *env, jobject clazz, jlong nativeObject); -JNIEXPORT jboolean JNICALL JNI(sendMotionEvent)(JNIEnv *env, jclass, jint masterAxis, jint masterValue, - jint secondaryAxis, jint secondaryValue); +JNIEXPORT jstring JNICALL JNI(getVideoStreamStats)(JNIEnv *env, jobject clazz, jlong nativeObject); -JNIEXPORT jboolean JNICALL JNI(setRotation)(JNIEnv *, jclass, int h); +JNIEXPORT jstring JNICALL JNI(getSimpleStreamStats)(JNIEnv *env, jobject clazz, jlong nativeObject); + +JNIEXPORT jboolean JNICALL JNI(setMediaConfig)(JNIEnv *env, jobject clazz, + jlong nativeObject, jobject mediaConfig); + +JNIEXPORT jint JNICALL JNI(recvData)(JNIEnv *env, jobject clazz, jlong nativeObject, + jbyte type, jbyteArray data, int length); + +JNIEXPORT jint JNICALL JNI(sendData)(JNIEnv *env, jobject clazz, jlong nativeObject, + jbyte type, jbyteArray data, jint length); + +JNIEXPORT jboolean JNICALL JNI(sendTouchEvent)(JNIEnv *env, jobject clazz, jlong nativeObject, + int id, int action, int x, int y, int pressure, jlong time, jint orientation, jint height, jint width); + +JNIEXPORT jboolean JNICALL JNI(sendKeyEvent)(JNIEnv *env, jobject clazz, + jlong nativeObject, jint keycode, jint action); + +JNIEXPORT jboolean JNICALL JNI(sendMotionEvent)(JNIEnv *env, jobject, jlong nativeObject, + jint masterAxis, jint masterValue, jint secondaryAxis, jint secondaryValue); + +JNIEXPORT jboolean JNICALL JNI(setRotation)(JNIEnv *, jobject clazz, jlong nativeObject, int h); + +JNIEXPORT jboolean JNICALL JNI(getConnectStatus)(JNIEnv *env, jobject clazz, jlong nativeObject); -JNIEXPORT jboolean JNICALL JNI(getConnectStatus)(JNIEnv *env, jclass); } #endif // CLOUDAPPSDK_JNIRENDER_H \ No newline at end of file diff --git a/cloudphone/src/main/cpp/cas_common/CasMsg.h b/cloudphone/src/main/cpp/cas_common/CasMsg.h index f591ff846bf06edc253bc95cfe1d0f24a3088df5..1fcb7f667e126daff44764a0ec5a579bc0b08f7b 100644 --- a/cloudphone/src/main/cpp/cas_common/CasMsg.h +++ b/cloudphone/src/main/cpp/cas_common/CasMsg.h @@ -30,6 +30,7 @@ enum CasMsgType : uint8_t { CmdControl = 7, HeartBeat = 8, Orientation = 9, + ScreenShot = 10, Recorder = 11, ImeData = 14, KeyEventInput = 15, diff --git a/cloudphone/src/main/cpp/cas_controller/CMakeLists.txt b/cloudphone/src/main/cpp/cas_controller/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..ab9a526830fc733ee7d76b0010ffbea4217a189a --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CMakeLists.txt @@ -0,0 +1,10 @@ +aux_source_directory(. CAS_CONTROLLER_SRC_LIST) +add_library(cas_controller STATIC ${CAS_CONTROLLER_SRC_LIST}) +target_link_libraries( # Specifies the target library. + cas_controller + cas_common + cas_socket + cas_service + hwsecure + libmtrans + ) \ No newline at end of file diff --git a/cloudphone/src/main/cpp/cas_controller/CasAudioHandler.cpp b/cloudphone/src/main/cpp/cas_controller/CasAudioHandler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b7f154ad26df6158e4f1cfeee964794d8c8b2cd5 --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasAudioHandler.cpp @@ -0,0 +1,134 @@ +// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "CasAudioHandler.h" +#include "../cas_common/CasLog.h" +#include "../cas_common/CasMsg.h" +#include "../cas_common/CasBuffer.h" +#include "ICasTransmission.h" +#include "opus.h" +#include "CasLog.h" + +const int SAMPLE_RATE = 48000; +const int CHANNEL = 2; +const int OPUS_FRAME_SIZE = 1920; + +CasAudioHandler::CasAudioHandler(std::shared_ptr &result) + :CasMessageHandler(result) +{ + INFO("Construct."); + m_opusDecoder = nullptr; + m_audioTaskRun = false; +} + +CasAudioHandler::~CasAudioHandler() +{ + INFO("Destruct."); + m_audioPktStream = nullptr; +} + +int CasAudioHandler::init() +{ + INFO("Init."); + return 0; +} + +int CasAudioHandler::start() +{ + INFO("Start."); + std::lock_guard lock(m_lock); + m_audioPktStream = std::make_shared(true); + if (m_audioPktStream == nullptr) { + ERR("Could't create m_audioPktStream."); + return -1; + } + int err; + m_opusDecoder = opus_decoder_create(SAMPLE_RATE, CHANNEL, &err); + if (m_opusDecoder == nullptr) { + ERR("Create opus decoder failed."); + return -1; + } + m_audioPktStream->Clear(); + m_audioTaskRun = true; + m_thread = std::thread(&CasAudioHandler::processThread, this); + return 0; +} + +int CasAudioHandler::stop() +{ + INFO("Stop."); + std::lock_guard lock(m_lock); + if (m_audioTaskRun) { + m_audioTaskRun = false; + m_audioPktStream->Exit(); + m_thread.join(); + if (m_opusDecoder) { + opus_decoder_destroy(m_opusDecoder); + m_opusDecoder = nullptr; + } + m_audioPktStream = nullptr; + } + return 0; +} + +void CasAudioHandler::handleMessage(uint8_t *message, uint32_t length, int source) +{ + std::lock_guard lock(m_lock); + if (m_audioPktStream == nullptr) { + FreeBuffer(message); + return; + } + m_audioPktStream->Handle(message); + m_type = source; +} + +void CasAudioHandler::processThread() +{ + while (m_audioTaskRun) { + const int timeout = 600; // 600毫秒 + void *message = m_audioPktStream->GetNextPktWaitFor(timeout); + if (message == nullptr) { + continue; + } + stream_msg_head_t *header = (stream_msg_head_t *) message; + uint8_t *body = (uint8_t *) message + sizeof(stream_msg_head_t); + if (m_type == SOURCE_TCP && m_opusDecoder != nullptr) { + stream_msg_head_t msgHead; + msgHead.type = CasMsgType::Audio; + msgHead.checksum = CAS_MSG_CHECKSUM_AUDIO; + msgHead.magicword = CAS_STREAM_DELIMITER_MAGICWORD; + uint32_t bufferLen = sizeof(stream_msg_head_t) + OPUS_FRAME_SIZE; + uint8_t *pcmBuffer = new(std::nothrow) uint8_t[bufferLen]; + if (pcmBuffer != nullptr) { + const int opusSize = 240; + int size = opus_decode(m_opusDecoder, + body, + opusSize, + (opus_int16 *) (pcmBuffer + sizeof(stream_msg_head_t)), + 480, + 0); + msgHead.SetPayloadSize(size * 4); + memcpy_s(pcmBuffer, sizeof(stream_msg_head_t), &msgHead, sizeof(stream_msg_head_t)); + m_messageResult->onMessageRecv((stream_msg_head_t *) pcmBuffer, + pcmBuffer + sizeof(stream_msg_head_t)); + delete[] pcmBuffer; + } + } else if (m_type == SOURCE_MTRANS) { + if (m_messageResult != nullptr) { + m_messageResult->onMessageRecv(header, body); + } + } + FreeBuffer(message); + } +} \ No newline at end of file diff --git a/cloudphone/src/main/cpp/cas_controller/CasAudioHandler.h b/cloudphone/src/main/cpp/cas_controller/CasAudioHandler.h new file mode 100644 index 0000000000000000000000000000000000000000..b6c264bc413177635c3ec06100fdc0d08e0f8d4e --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasAudioHandler.h @@ -0,0 +1,47 @@ +// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CLOUDAPPSDK_CASAUDIOHANDLER_H +#define CLOUDAPPSDK_CASAUDIOHANDLER_H + +#include +#include +#include +#include +#include "CasMessageHandler.h" +#include "CasDataPipe.h" + +struct OpusDecoder; + +class CasAudioHandler : public CasMessageHandler{ +public: + CasAudioHandler(std::shared_ptr &result); + ~CasAudioHandler(); + + int init() override; + int start() override; + int stop() override; + void handleMessage(uint8_t *message, uint32_t length, int dataSource) override; + void processThread(); + +public: + std::thread m_thread; + std::mutex m_lock; + OpusDecoder *m_opusDecoder; + std::shared_ptr m_audioPktStream; + bool m_audioTaskRun; + int m_type; +}; + +#endif // CLOUDAPPSDK_CASAUDIOHANDLER_H diff --git a/cloudphone/src/main/cpp/cas_controller/CasCmdHandler.cpp b/cloudphone/src/main/cpp/cas_controller/CasCmdHandler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bb7140e8995e376be89563f9c7642dffba5087a9 --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasCmdHandler.cpp @@ -0,0 +1,83 @@ +// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include "CasCmdHandler.h" +#include "../cas_common/CasMsg.h" +#include "../cas_service/CasAppCtrlCmdUtils.h" +#include "../cas_common/CasBuffer.h" + +CasCmdHandler::CasCmdHandler(std::shared_ptr &result) + :CasMessageHandler(result) +{ + INFO("Constructor."); +} + +CasCmdHandler::~CasCmdHandler() +{ + INFO("Destructor."); +} + +int CasCmdHandler::init() +{ + INFO("Init."); + return 0; +} + +int CasCmdHandler::start() +{ + INFO("Cmd handler start."); + return 0; +} + +int CasCmdHandler::stop() +{ + INFO("Cmd handler stop."); + return 0; +} + +void CasCmdHandler::handleMessage(uint8_t *message, uint32_t length, int source) +{ + if (message == nullptr) { + return; + } + streamMsgHead *streamHead = (streamMsgHead *)message; + const char *receivedMsgString = (char *)((uint8_t *)message + sizeof(streamMsgHead)); + std::map parameters = + CasAppCtrlCmdUtils::ParseCommand(receivedMsgString, streamHead->GetPayloadSize()); + + int command = -1; + int result = -1; + int code = -1; + std::string msg = ""; + + if (parameters.find(KEY_COMMAND) != parameters.end()) { + command = atoi(parameters[KEY_COMMAND].c_str()); + } + if (parameters.find(KEY_RESULT) != parameters.end()) { + result = atoi(parameters[KEY_RESULT].c_str()); + } + if (parameters.find(KEY_CODE) != parameters.end()) { + code = atoi(parameters[KEY_CODE].c_str()); + } + if (parameters.find(KEY_MSG) != parameters.end()) { + msg = parameters[KEY_MSG]; + } + if (m_messageResult != nullptr) { + m_messageResult->onCmdRecv(code, msg); + } + FreeBuffer(message); +} \ No newline at end of file diff --git a/cloudphone/src/main/cpp/cas_controller/CasCmdHandler.h b/cloudphone/src/main/cpp/cas_controller/CasCmdHandler.h new file mode 100644 index 0000000000000000000000000000000000000000..1c409892e77e24d5b0cf21aa46544b6a2cecf0bb --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasCmdHandler.h @@ -0,0 +1,34 @@ +// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CLOUDAPPSDK_CASCMDHANDLER_H +#define CLOUDAPPSDK_CASCMDHANDLER_H + + +#include "../CasController.h" +#include "CasMessageHandler.h" +#include "../cas_common/CasMsg.h" + +class CasCmdHandler : public CasMessageHandler { +public: + CasCmdHandler(std::shared_ptr &result); + ~CasCmdHandler(); + + int init() override; + int start() override; + int stop() override; + void handleMessage(uint8_t *message, uint32_t length, int dataSource) override; +}; + +#endif // CLOUDAPPSDK_CASCMDHANDLER_H diff --git a/cloudphone/src/main/cpp/cas_controller/CasCommonHandler.cpp b/cloudphone/src/main/cpp/cas_controller/CasCommonHandler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a54b5a3e563d8b5d43b40bee46c394d09ad14325 --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasCommonHandler.cpp @@ -0,0 +1,52 @@ +// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "CasCommonHandler.h" +#include "../cas_common/CasMsg.h" +#include "../cas_common/CasBuffer.h" + +CasCommonHandler::CasCommonHandler(std::shared_ptr &result) + :CasMessageHandler(result) +{ +} + +CasCommonHandler::~CasCommonHandler() +{ +} + +int CasCommonHandler::init() +{ + return 0; +} + +int CasCommonHandler::start() +{ + return 0; +} + +int CasCommonHandler::stop() +{ + return 0; +} + +void CasCommonHandler::handleMessage(uint8_t *message, uint32_t length, int source) +{ + stream_msg_head_t *header = (stream_msg_head_t *) message; + uint8_t *body = message + sizeof(stream_msg_head_t); + + if (m_messageResult != nullptr) { + m_messageResult->onMessageRecv(header, body); + } + FreeBuffer(message); +} \ No newline at end of file diff --git a/cloudphone/src/main/cpp/cas_controller/CasCommonHandler.h b/cloudphone/src/main/cpp/cas_controller/CasCommonHandler.h new file mode 100644 index 0000000000000000000000000000000000000000..174ecb47de4eb03d8c22198b89070f35ea386f5a --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasCommonHandler.h @@ -0,0 +1,33 @@ +// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CLOUDAPPSDK_CASCOMMONHANDLER_H +#define CLOUDAPPSDK_CASCOMMONHANDLER_H + +#include +#include "../cas_common/CasMsg.h" +#include "CasMessageHandler.h" + +class CasCommonHandler : public CasMessageHandler { +public: + CasCommonHandler(std::shared_ptr &result); + ~CasCommonHandler(); + + int init() override; + int start() override; + int stop() override; + void handleMessage(uint8_t *message, uint32_t length, int dataSource) override; +}; + +#endif // CLOUDAPPSDK_CASCOMMONHANDLER_H diff --git a/cloudphone/src/main/cpp/cas_controller/CasHandlerManager.cpp b/cloudphone/src/main/cpp/cas_controller/CasHandlerManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ebd1be26f4f3546cd7b025859f7cab932ffa8f6d --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasHandlerManager.cpp @@ -0,0 +1,195 @@ +// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "CasHandlerManager.h" +#include "../CasController.h" +#include "CasVideoHandler.h" +#include "CasHandlerManager.h" +#include "CasCmdHandler.h" +#include "CasCommonHandler.h" +#include "CasHeartbeatHandler.h" +#include "../cas_common/CasLog.h" +#include "CasBuffer.h" +#include "CasAudioHandler.h" + +CasHandlerManager::CasHandlerManager(std::shared_ptr result) +{ + INFO("Constructor."); + m_hanlderResult = result; +} + +CasHandlerManager::~CasHandlerManager() +{ + INFO("Destructor."); + m_hanlderResult = nullptr; + for (auto &it : m_handlerMap) { + auto &handler = it.second; + handler = nullptr; + } + m_handlerMap.clear(); +} + +int CasHandlerManager::init() +{ + INFO("Init."); + for (auto &it : m_handlerMap) { + auto &handler = it.second; + if (handler->init() != 0) { + return -1; + } + } + return 0; +} + +int CasHandlerManager::start(bool isStartVideo) +{ + INFO("Start."); + for (auto &it : m_handlerMap) { + auto &handler = it.second; + if (!isStartVideo && it.first == Video) { + continue; + } + if (handler->start() != 0) { + return -1; + } + } + return 0; +} + +int CasHandlerManager::stop(bool isStopVideo) +{ + INFO("Stop."); + for (auto &it : m_handlerMap) { + if (!isStopVideo && it.first == Video) { + continue; + } + auto &handler = it.second; + handler->stop(); + } + return 0; +} + +int CasHandlerManager::createHandler(CasMsgType type) +{ + std::shared_ptr handler = nullptr; + + switch (type) { + case CasMsgType::Video: + handler = std::make_shared(m_hanlderResult); + break; + case CasMsgType::HeartBeat: + handler = std::make_shared(m_hanlderResult); + break; + case CasMsgType::CmdControl: + handler = std::make_shared(m_hanlderResult); + break; + case CasMsgType::Channel: + case CasMsgType::Orientation: + case CasMsgType::ImeData: + case CasMsgType::VirtualCamera: + case CasMsgType::VirtualMicrophone: + case CasMsgType::VirtualSensor: + case CasMsgType::VirtualLocation: + case CasMsgType::ScreenShot: + handler = std::make_shared(m_hanlderResult); + break; + case CasMsgType::Audio: + handler = std::make_shared(m_hanlderResult); + break; + default: + return -1; + } + if (handler != nullptr) { + addHandler(type, handler); + } + return 0; +} + +void CasHandlerManager::addHandler(CasMsgType type, std::shared_ptr &handler) +{ + m_handlerMap[type] = handler; +} + +void CasHandlerManager::setVideoParameters(ANativeWindow *nativeWindow, + FrameType frameType, int rotation) +{ + m_nativeWindow = nativeWindow; + m_rotation = rotation; + m_frameType = frameType; + if (m_handlerMap.count(CasMsgType::Video) != 0) { + std::shared_ptr handler = + std::static_pointer_cast(m_handlerMap[CasMsgType::Video]); + handler->setParameters(m_nativeWindow, m_frameType, m_rotation); + } +} + +void CasHandlerManager::setStreamBuilder(std::shared_ptr &streamBuildSender) +{ + m_streamBuildSender = streamBuildSender; + if (m_handlerMap.count(CasMsgType::HeartBeat) != 0) { + std::shared_ptr handler = + std::static_pointer_cast(m_handlerMap[CasMsgType::HeartBeat]); + handler->setParameters(m_streamBuildSender); + } +} + +int CasHandlerManager::startVideoHandler() +{ + if (m_handlerMap.count(CasMsgType::Video) == 0) { + return -1; + } + std::shared_ptr handler = + std::static_pointer_cast(m_handlerMap[CasMsgType::Video]); + handler->setParameters(m_nativeWindow, m_frameType, m_rotation); + handler->start(); + return 0; +} + +int CasHandlerManager::stopVideoHandler() +{ + if (m_handlerMap.count(CasMsgType::Video) == 0) { + return -1; + } + std::shared_ptr handler = + std::static_pointer_cast(m_handlerMap[CasMsgType::Video]); + handler->stop(); + return 0; +} + +std::shared_ptr CasHandlerManager::getHandler(int type) +{ + if (m_handlerMap.count(CasMsgType::Video) == 0) { + return nullptr; + } + std::shared_ptr handler = + std::static_pointer_cast(m_handlerMap[type]); + return handler; +} + +void CasHandlerManager::onNewPacket(int type, uint8_t *buffer, uint32_t length, int source) +{ + if (m_handlerMap.count(type)) { + auto &handler = m_handlerMap[type]; + handler->handleMessage(buffer, length, source); + } else { + FreeBuffer(buffer); + } +} + +void CasHandlerManager::onNewCmd(int cmd, std::string detail) +{ + if (m_hanlderResult != nullptr) { + m_hanlderResult->onCmdRecv(cmd, detail); + } +} diff --git a/cloudphone/src/main/cpp/cas_controller/CasHandlerManager.h b/cloudphone/src/main/cpp/cas_controller/CasHandlerManager.h new file mode 100644 index 0000000000000000000000000000000000000000..e0cdea9320ed1b79f55b7a328b95da5f06daa050 --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasHandlerManager.h @@ -0,0 +1,57 @@ +// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CLOUDAPPSDK__CASHANDLERMANAGER_H +#define CLOUDAPPSDK__CASHANDLERMANAGER_H + +#include +#include +#include +#include "CasTransmission.h" +#include "CasMessageHandler.h" +#include "../CasController.h" + +class CasHandlerManager : public CasTransmissionListener { +public: + CasHandlerManager(std::shared_ptr result); + ~CasHandlerManager(); + + int init(); + int start(bool isStartVideo = true); + int stop(bool isStopVideo = true); + int createHandler(CasMsgType type); + int startVideoHandler(); + int stopVideoHandler(); + + void setVideoParameters(ANativeWindow *nativeWindow, FrameType frameType, int rotationDegree); + void setStreamBuilder(std::shared_ptr &streamBuildSender); + std::shared_ptr getHandler(int type); + +private: + void onNewPacket(int type, uint8_t *buffer, uint32_t length, int dataSource) override; + void onNewCmd(int cmd, std::string detail) override; + +private: + void addHandler(CasMsgType type, std::shared_ptr &handler); + +private: + std::map> m_handlerMap; + std::shared_ptr m_streamBuildSender; + std::shared_ptr m_hanlderResult; + ANativeWindow *m_nativeWindow; + int m_rotation; + FrameType m_frameType; +}; + +#endif // CLOUDAPPSDK_CASHANDLERMANAGER_H diff --git a/cloudphone/src/main/cpp/cas_controller/CasHandlerResultProcessor.cpp b/cloudphone/src/main/cpp/cas_controller/CasHandlerResultProcessor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fb02b00aaf64cabe4d56bfb17fe6a887df3304f7 --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasHandlerResultProcessor.cpp @@ -0,0 +1,62 @@ +// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "CasHandlerResultProcessor.h" +#include "../CasController.h" + +CasHandlerResultProcessor::CasHandlerResultProcessor(CasController *controller) +{ + INFO("Constructor."); + m_casController = controller; +} + +CasHandlerResultProcessor::~CasHandlerResultProcessor() +{ + INFO("Destructor."); +} + +void CasHandlerResultProcessor::onCmdRecv(int code, std::string &msg) +{ + if (m_casController != nullptr) { + m_casController->OnCmdRecv(code, msg); + } +} + +void CasHandlerResultProcessor::onMessageRecv(stream_msg_head_t *header, uint8_t *body) +{ + if (m_casController != nullptr) { + m_casController->onMessageRecv(header, body, header->GetPayloadSize()); + } +} + +void CasHandlerResultProcessor::onConnectionStateChange(bool status) +{ + if (m_casController != nullptr) { + m_casController->SetConnectStatus(status); + } +} + +void CasHandlerResultProcessor::onVideoPacketRecv() +{ + if (m_casController != nullptr) { + m_casController->CalculateFPS(); + } +} + +void CasHandlerResultProcessor::updateLag(uint64_t lag) +{ + if (m_casController != nullptr) { + m_casController->SetLag(lag); + } +} \ No newline at end of file diff --git a/cloudphone/src/main/cpp/cas_controller/CasHandlerResultProcessor.h b/cloudphone/src/main/cpp/cas_controller/CasHandlerResultProcessor.h new file mode 100644 index 0000000000000000000000000000000000000000..450c0c8281ff2ad0677ddf98ecc7e999aad78405 --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasHandlerResultProcessor.h @@ -0,0 +1,38 @@ +// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CLOUDAPPSDK_CASHANDLERRESULTPROCESSOR_H +#define CLOUDAPPSDK_CASHANDLERRESULTPROCESSOR_H + +#include +#include +#include "../cas_common/CasMsg.h" +#include "CasMessageHandler.h" + +class CasController; +class CasHandlerResultProcessor : public CasHandlerResult { +public: + CasHandlerResultProcessor(CasController *casController); + virtual ~CasHandlerResultProcessor(); + void onCmdRecv(int code, std::string &msg) override; + void onMessageRecv(stream_msg_head_t *header, uint8_t *body) override; + void onConnectionStateChange(bool status) override; + void onVideoPacketRecv() override; + void updateLag(uint64_t lag) override; + +private: + CasController *m_casController; +}; + +#endif // CLOUDAPPSDK_CASHANDLERRESULTPROCESSOR_H diff --git a/cloudphone/src/main/cpp/cas_controller/CasHeartbeatHandler.cpp b/cloudphone/src/main/cpp/cas_controller/CasHeartbeatHandler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..41336595ed565ddc6c66cf6773b67267ec968929 --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasHeartbeatHandler.cpp @@ -0,0 +1,181 @@ +// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include "CasHeartbeatHandler.h" +#include "CasLog.h" +#include "../cas_service/CasAppCtrlCmdUtils.h" +#include "CasBuffer.h" + +namespace { + const uint64_t MAX_LAGS_NUM = 5; + const uint64_t MAX_LAG = 10 * 1000000; +} + +CasHeartbeatHandler::CasHeartbeatHandler(std::shared_ptr &result) + :CasMessageHandler(result) +{ + INFO("Costruct"); + m_conditionWait = false; + m_heartbeatTaskRun = false; +} + +CasHeartbeatHandler::~CasHeartbeatHandler() +{ + INFO("Destruct"); + m_heartbeatPktStream = nullptr; + m_streamBuildSender = nullptr; +} + +int CasHeartbeatHandler::init() +{ + INFO("Init"); + return 0; +} + +int CasHeartbeatHandler::start() +{ + std::lock_guard lock(m_lock); + m_heartbeatPktStream = std::make_shared(true); + if (m_heartbeatPktStream == nullptr) { + ERR("Could't create m_heartbeatPktStream."); + return -1; + } + m_conditionWait = false; + m_heartbeatTaskRun = true; + m_heartbeatThread = std::thread(&CasHeartbeatHandler::heartbeatThread, this); + return 0; +} + +int CasHeartbeatHandler::stop() +{ + INFO("Stop"); + std::lock_guard lock(m_lock); + if (m_heartbeatTaskRun) { + m_heartbeatTaskRun = false; + m_heartbeatPktStream->Exit(); + interruptSleep(); + m_heartbeatThread.join(); + m_heartbeatPktStream = nullptr; + } + return 0; +} + +void CasHeartbeatHandler::handleMessage(uint8_t *message, uint32_t length, int dataSource) +{ + std::lock_guard lock(m_lock); + if (m_heartbeatPktStream == nullptr) { + FreeBuffer(message); + return; + } + m_heartbeatPktStream->Handle(message); +} + +void CasHeartbeatHandler::setParameters(std::shared_ptr &streamBuildSender) +{ + std::lock_guard lock(m_lock); + m_streamBuildSender = streamBuildSender; +} + +bool CasHeartbeatHandler::sendHeartbeat() +{ + m_heartbeatPktStream->Clear(); + std::map parameters = { { KEY_COMMAND, CMD_HEARTBEAT_REQUEST } }; + std::string msg = CasAppCtrlCmdUtils::MakeCommand(parameters); + int messageLength = msg.size() + 1; + int ret = m_streamBuildSender->SendDataToServer(CasMsgType::HeartBeat, msg.c_str(), messageLength); + if (ret != messageLength) { + ERR("Send heartbeat failed."); + return false; + } + const int timeout = 600; // 600毫秒 + void *onePkt = m_heartbeatPktStream->GetNextPktWaitFor(timeout); + if (onePkt == nullptr) { + ERR("Wait heartbeat response failed."); + return false; + } + FreeBuffer(onePkt); + return true; +} + +void CasHeartbeatHandler::heartbeatThread() +{ + struct timeval sendtime; + struct timeval recvtime; + int heatbeatFailedCount = 0; + const int maxFailedTimes = 5; + uint64_t lag; + bool isConnect = true; + + while (m_heartbeatTaskRun) { + gettimeofday(&sendtime, nullptr); + bool ret = sendHeartbeat(); + if (!ret) { + heatbeatFailedCount++; + if (heatbeatFailedCount > maxFailedTimes && isConnect) { + isConnect = false; + heatbeatFailedCount = 0; + m_messageResult->onConnectionStateChange(false); + ERR("client is disconnect."); + } + } else { + gettimeofday(&recvtime, nullptr); + lag = (uint64_t)recvtime.tv_usec + (uint64_t)recvtime.tv_sec * 1000000 - + ((uint64_t)sendtime.tv_usec + (uint64_t)sendtime.tv_sec * 1000000); + heatbeatFailedCount = 0; + if (!isConnect) { + isConnect = true; + m_messageResult->onConnectionStateChange(true); + } + updateLag(lag); + } + sleepFor(200); // 200毫秒 + } + INFO("HeartbeatThread exit."); +} + +void CasHeartbeatHandler::updateLag(uint64_t lag) +{ + if (m_lagDeque.size() >= MAX_LAGS_NUM) { + m_lagDeque.pop_front(); + } + m_lagDeque.push_back(lag); + + uint64_t maxLag = 0; + for (uint64_t i = 0; i < m_lagDeque.size(); ++i) { + if (maxLag < m_lagDeque.at(i)) { + maxLag = m_lagDeque.at(i); + } + } + m_lag = maxLag; + if (m_messageResult != nullptr) { + m_messageResult->updateLag(m_lag); + } +} + +void CasHeartbeatHandler::sleepFor(const int timeout) +{ + std::unique_lock lock(m_mutex); + m_cv.wait_for(lock, std::chrono::milliseconds(timeout), + [this]() { return m_conditionWait; }); +} + +void CasHeartbeatHandler::interruptSleep() +{ + { + std::lock_guard lock(m_mutex); + m_conditionWait = true; + } + m_cv.notify_all(); +} \ No newline at end of file diff --git a/cloudphone/src/main/cpp/cas_controller/CasHeartbeatHandler.h b/cloudphone/src/main/cpp/cas_controller/CasHeartbeatHandler.h new file mode 100644 index 0000000000000000000000000000000000000000..e33d96badc82ea52c181a8fa31c015a7529e8c07 --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasHeartbeatHandler.h @@ -0,0 +1,62 @@ +// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CLOUDAPPSDK_CASHEARTBEATHANDLER_H +#define CLOUDAPPSDK_CASHEARTBEATHANDLER_H + +#include +#include +#include +#include +#include +#include "CasMessageHandler.h" +#include "CasTransmission.h" +#include "CasStreamBuildSender.h" +#include "../cas_service/CasDataPipe.h" + +class CasHeartbeatHandler : public CasMessageHandler{ +public: + CasHeartbeatHandler(std::shared_ptr &result); + ~CasHeartbeatHandler(); + + int init() override; + int start() override; + int stop() override; + void handleMessage(uint8_t *message, uint32_t length, int dataSource) override; + void setParameters(std::shared_ptr &streamBuildSender); + uint64_t getLag() { + return m_lag; + } + +private: + void heartbeatThread(); + bool sendHeartbeat(); + void sleepFor(int ms); + void interruptSleep(); + void updateLag(uint64_t lag); + +private: + std::mutex m_lock; + uint64_t m_lag = 0; + bool m_heartbeatTaskRun; + std::thread m_heartbeatThread; + std::shared_ptr m_streamBuildSender; + std::deque m_lagDeque; + std::mutex m_mutex; + std::condition_variable m_cv; + bool m_conditionWait; + std::shared_ptr m_heartbeatPktStream; +}; + +#endif // CLOUDAPPSDK_CASHEARTBEATHANDLER_H diff --git a/cloudphone/src/main/cpp/cas_controller/CasMessageHandler.h b/cloudphone/src/main/cpp/cas_controller/CasMessageHandler.h new file mode 100644 index 0000000000000000000000000000000000000000..efe99d80d76067aee6a14009190b7a1d277fba41 --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasMessageHandler.h @@ -0,0 +1,50 @@ +// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CLOUDAPPSDK_CASMESSAGEHANDLER_H +#define CLOUDAPPSDK_CASMESSAGEHANDLER_H + +#include +#include +#include "CasMsg.h" + +class CasHandlerResult { +public: + CasHandlerResult() = default; + virtual ~CasHandlerResult() {} + virtual void onCmdRecv(int code, std::string &msg) = 0; + virtual void onMessageRecv(stream_msg_head_t *header, uint8_t *body) = 0; + virtual void onConnectionStateChange(bool state) = 0; + virtual void onVideoPacketRecv() = 0; + virtual void updateLag(uint64_t lag) = 0; +}; + +class CasMessageHandler { +public: + CasMessageHandler(std::shared_ptr &result) { + m_messageResult = result; + } + virtual ~CasMessageHandler() { + m_messageResult = nullptr; + } + virtual int init() = 0; + virtual int start() = 0; + virtual int stop() = 0; + virtual void handleMessage(uint8_t *message, uint32_t length, int source) = 0; + +protected: + std::shared_ptr m_messageResult; +}; + +#endif // CLOUDAPPSDK_CASMESSAGEHANDLER_H diff --git a/cloudphone/src/main/cpp/cas_controller/CasMtansTransmission.cpp b/cloudphone/src/main/cpp/cas_controller/CasMtansTransmission.cpp new file mode 100644 index 0000000000000000000000000000000000000000..75cb3e922dafd984f29d0431ffae06e7884ffac5 --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasMtansTransmission.cpp @@ -0,0 +1,316 @@ +// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include "CasMtansTransmission.h" +#include "CasLog.h" +#include "opus.h" +#include "CasMsg.h" +#include "CasTransmission.h" +#include "net_trans.h" +#include "spdlog/sinks/rotating_file_sink.h" +#include "../CasCommon.h" + +int32_t OnRecvVideoStreamData(uint8_t *data, uint32_t length); +int32_t OnRecvAudioStreamData(uint8_t *data, uint32_t length); +int32_t OnRecvAudioDecodeCallback(int32_t streamId, AudioJbDecode* audioJbDecode); +int32_t OnRecvCmdData(uint8_t* data, uint32_t length); +void OnGotTransLog(const char* str, uint32_t length); +void OnNeedKeyFrameCallback(); + +static std::weak_ptr g_listener; +static int g_plcCount = 0; +static OpusDecoder *g_opusDecoder = nullptr; +static std::shared_ptr g_hrtpLogger = nullptr; +static bool g_MtransIsvalid = false; + +CasMtansTransmission::CasMtansTransmission() +{ + INFO("CasMtansTransmission construct."); + m_mtrans = nullptr; + g_MtransIsvalid = false; + g_listener.reset(); +} + +CasMtansTransmission::~CasMtansTransmission() +{ + INFO("CasMtansTransmission destruct."); + m_mtrans = nullptr; + g_listener.reset(); +} + +int CasMtansTransmission::init(uint32_t ip, uint16_t port, std::string &logPath) +{ + m_mtrans = new (std::nothrow) NetTrans(); + if (m_mtrans == nullptr) { + ERR("Create mtrans instance failed."); + return -1; + } + + struct in_addr addr; + addr.s_addr = htonl(ip); + m_ip = inet_ntoa(addr); + if (g_hrtpLogger == nullptr) { + // Create a file rotating logger with 50 MB size max and 3 rotated files + auto max_size = 1048576 * 50; + auto max_files = 3; + g_hrtpLogger = spdlog::rotating_logger_mt("hrtp_logger", logPath + "hrtp_log.txt", max_size, max_files); + } + + if (!logPath.empty()) { + m_mtrans->EnableLog(logPath + "hrtp_log.txt", TRANS_LOG_LEVEL_INFO); + } + + TransConfigParam param; + param.minVideoSendBitrate = 500; + param.maxVideoSendBitrate = 10000; + INFO("CasMtansTransmission init ip = %s port = %d.", m_ip.c_str(), port); + + g_MtransIsvalid = false; + return m_mtrans->Init(PEER_CLIENT, m_ip.c_str(), port, param, + OnRecvVideoStreamData, + OnRecvAudioStreamData, + nullptr, + OnNeedKeyFrameCallback, + OnRecvCmdData, + OnRecvAudioDecodeCallback, + OnGotTransLog); + m_isStart = false; +} + +int CasMtansTransmission::deinit() +{ + INFO("CasMtansTransmission deinit."); + if (m_mtrans != nullptr) { + delete m_mtrans; + m_mtrans = nullptr; + } + return 0; +} + +int CasMtansTransmission::connect() +{ + INFO("Connect"); + return 0; +} + +int CasMtansTransmission::reconnect() +{ + INFO("Reonnect"); + stop(); + return start(); +} + +int CasMtansTransmission::send(uint8_t *buffer, uint32_t length) +{ + if (m_mtrans == nullptr) { + ERR("m_mtrans is null."); + return -1; + } + stream_msg_head_t *msgHead = (stream_msg_head_t *)buffer; + switch ((int)msgHead->type) { + case VirtualMicrophone: + m_mtrans->SendAudioData(buffer + sizeof(stream_msg_head_t), + length - sizeof(stream_msg_head_t)); + break; + case VirtualCamera: + m_mtrans->SendVideoData(buffer + sizeof(stream_msg_head_t), + length - sizeof(stream_msg_head_t), "h264"); + break; + case VirtualLocation: + m_mtrans->SendLocationData(buffer, length); + break; + case HeartBeat: + m_mtrans->SendCmdData(buffer, length); + break; + case TouchInput: + m_mtrans->SendTouchEventData(buffer, length); + break; + case KeyEventInput: + m_mtrans->SendKeyEventData(buffer, length); + break; + case MotionEventInput: + INFO("MotionEventInput"); + m_mtrans->SendMotionEventData(buffer, length); + break; + case VirtualSensor: + m_mtrans->SendSensorData(buffer, length); + break; + default: + break; + } + return length; +} + +int CasMtansTransmission::start() +{ + INFO("Start."); + if (m_isStart) { + return 0; + } + if (m_mtrans == nullptr) { + ERR("m_mtrans is null."); + return -1; + } + int ret = m_mtrans->Start(); + if (ret < 0) { + ERR("Mtrans start failed."); + } else { + INFO("Mtrans start success."); + } + int err = 0; + if (g_opusDecoder == nullptr) { + g_opusDecoder = opus_decoder_create(48000, 2, &err); + } + g_MtransIsvalid = false; + m_isStart = true; + return 0; +} + +int CasMtansTransmission::stop() +{ + if (!m_isStart) { + return 0; + } + INFO("Stop."); + if (m_mtrans == nullptr) { + ERR("Mtrans instance is null."); + return false; + } + g_MtransIsvalid = false; + m_isStart = false; + return m_mtrans->Stop();; +} + +void CasMtansTransmission::setListener(std::weak_ptr &listener) +{ + g_listener = listener; +} + +bool CasMtansTransmission::isReady() +{ + return g_MtransIsvalid; +} + +int32_t OnRecvVideoStreamData(uint8_t* data, uint32_t length) +{ + std::shared_ptr listener = g_listener.lock(); + if (listener == nullptr) { + return -1; + } + uint8_t *videoData = new(std::nothrow) uint8_t[length]; + if (videoData != nullptr) { + memcpy_s(videoData, length, data, length); + listener->onNewPacket(CasMsgType::Video, videoData, length, SOURCE_MTRANS); + } + g_MtransIsvalid = true; + return 0; +} + +int32_t OnRecvAudioStreamData(uint8_t* data, uint32_t length) +{ + stream_msg_head_t msgHead; + msgHead.type = CasMsgType::Audio; + msgHead.checksum = CAS_MSG_CHECKSUM_AUDIO; + msgHead.magicword = CAS_STREAM_DELIMITER_MAGICWORD; + msgHead.SetPayloadSize(length); + + int dataLen = sizeof(stream_msg_head_t) + length; + uint8_t *buffer = (uint8_t*)malloc(dataLen); + memcpy_s(buffer, dataLen, &msgHead, sizeof(stream_msg_head_t)); + memcpy_s(buffer + sizeof(stream_msg_head_t), dataLen - sizeof(stream_msg_head_t), data, length); + std::shared_ptr listener = g_listener.lock(); + if (listener != nullptr) { + listener->onNewPacket(CasMsgType::Audio, buffer, dataLen, SOURCE_MTRANS); + } + return 0; +} + +int32_t OnRecvAudioDecodeCallback(int32_t streamId, AudioJbDecode* audioJbDecode) +{ + if (audioJbDecode->inputLength > 0) { + g_plcCount = 0; + int opusSize = 240; + int pcmLen = opus_decode(g_opusDecoder, + audioJbDecode->payload + 8, + opusSize, + audioJbDecode->outputData, + 480, + 0); + audioJbDecode->frameType = 1; + audioJbDecode->outputLength = pcmLen * 2; + } else { + int pcmLen = opus_decode(g_opusDecoder, + audioJbDecode->payload, + 0, + audioJbDecode->outputData, + 480, + 1); + audioJbDecode->frameType = 1; + audioJbDecode->outputLength = pcmLen * 2; + if (g_plcCount >= 4) { + memset_s(audioJbDecode->outputData, + audioJbDecode->outputLength * 2, + 0, + audioJbDecode->outputLength * 2); + } + g_plcCount++; + if (g_plcCount > 100) { + g_plcCount = 4; + } + } + return 0; +} + +void OnGotTransLog(const char* str, uint32_t length) +{ + if (g_hrtpLogger != nullptr) { + g_hrtpLogger->info(str); + } +} + +void OnNeedKeyFrameCallback() +{ + std::shared_ptr listener = g_listener.lock(); + if (listener != nullptr) { + listener->onNewCmd(CAS_REQUEST_CAMERA_KEY_FRAME, "camera need keyframe"); + } +} + +int32_t OnRecvCmdData(uint8_t* data, uint32_t length) +{ + std::shared_ptr listener = g_listener.lock(); + if (listener != nullptr) { + stream_msg_head_t *header = (stream_msg_head_t *)data; + listener->onNewPacket(header->type, data, length, SOURCE_MTRANS); + } + return 0; +} + +int CasMtansTransmission::getRecvStats(int type, void *value) +{ + if (m_mtrans == nullptr) { + return -1; + } + switch (type) { + case Video: + return m_mtrans->GetVideoRecvStats((StreamRecvStats*)value); + case Audio: + return m_mtrans->GetAudioRecvStats((StreamRecvStats*)value); + case CmdControl: + return m_mtrans->GetCmdSendStats((StreamSendStats*)value); + default: + return -1; + } +} \ No newline at end of file diff --git a/cloudphone/src/main/cpp/cas_controller/CasMtansTransmission.h b/cloudphone/src/main/cpp/cas_controller/CasMtansTransmission.h new file mode 100644 index 0000000000000000000000000000000000000000..dc9d80542b3baaab06d9cc74254fc4622a3e949f --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasMtansTransmission.h @@ -0,0 +1,46 @@ +// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CLOUDAPPSDK_CASMTRANS_H +#define CLOUDAPPSDK_CASMTRANS_H + +#include +#include +#include "ICasTransmission.h" + +class CasTransmissionListener; +class NetTrans; + +class CasMtansTransmission : public ICasTransmission { +public: + CasMtansTransmission(); + ~CasMtansTransmission(); + int init(uint32_t ip, uint16_t port, std::string &logPath) override; + int deinit() override; + int connect() override; + int start() override; + int stop() override; + int reconnect() override; + int send(uint8_t *buffer, uint32_t length) override; + void setListener(std::weak_ptr &listener) override; + bool isReady() override; + int getRecvStats(int type, void *value) override; + +private: + std::string m_ip; + NetTrans *m_mtrans; + bool m_isStart; +}; + +#endif // CLOUDAPPSDK_CASMTRANS_H diff --git a/cloudphone/src/main/cpp/cas_controller/CasTcpTransmission.cpp b/cloudphone/src/main/cpp/cas_controller/CasTcpTransmission.cpp new file mode 100644 index 0000000000000000000000000000000000000000..865465d4616a68af1bcdd653d7e5e4e0ee7f2d0c --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasTcpTransmission.cpp @@ -0,0 +1,190 @@ +// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "CasTcpTransmission.h" +#include "../cas_socket/CasTcpSocket.h" +#include "../cas_common/CasLog.h" +#include "../cas_common/CasBuffer.h" + +CasTcpTransmission::CasTcpTransmission() +{ + INFO("Construct."); + m_ip = 0; + m_port = 0; + m_isTaskRun = false; + m_casClientSocket = nullptr; + m_listener.reset(); + m_isReady = false; +} + +CasTcpTransmission::~CasTcpTransmission() +{ + INFO("Destruct."); + m_casClientSocket = nullptr; + m_listener.reset(); +} + +int CasTcpTransmission::init(uint32_t ip, uint16_t port, std::string &logPath) +{ + INFO("Init, ip=%d, port=%d", ip, port); + m_ip = ip; + m_port = port; + return 0; +} + +int CasTcpTransmission::deinit() +{ + INFO("Deinit."); + return 0; +} + +int CasTcpTransmission::connect() +{ + INFO("Connect."); + m_casClientSocket = std::make_shared(m_ip, m_port); + if (m_casClientSocket == nullptr) { + ERR("Create CasTcpClientSocket instance failed."); + return -1; + } + int result = 0; + const int retry = 3; + for (int i = 0; i < retry; i++) { + result = m_casClientSocket->Connect(); + if (result >= 0) { + break; + } else { + usleep(500000); + ERR("Connect to servcer failed, %d times.", i); + } + } + if (result < 0) { + ERR("Connect to server failed."); + m_casClientSocket = nullptr; + return result; + } + m_isReady = true; + INFO("Connect server success."); + return 0; +} + +int CasTcpTransmission::start() +{ + INFO("Start."); + m_isTaskRun = true; + m_task = std::thread(&CasTcpTransmission::recvTask, this); + return 0; +} + +int CasTcpTransmission::stop() +{ + INFO("Stop."); + if (m_isTaskRun) { + m_isTaskRun = false; + m_task.join(); + m_casClientSocket.reset(); + m_isReady = false; + } + return 0; +} + +int CasTcpTransmission::reconnect() +{ + INFO("Reconnect."); + stop(); + if (connect() != 0) { + ERR("Reconnect failed."); + return -1; + } + return start();; +} + +int CasTcpTransmission::send(uint8_t *buffer, uint32_t length) +{ + if (m_casClientSocket == nullptr) { + ERR("client socket is nullptr."); + return -1; + } + if (!m_isReady) { + ERR("socket not ready"); + return -1; + } + return m_casClientSocket->Send(buffer, length); +} + +void CasTcpTransmission::setListener(std::weak_ptr &listener) +{ + m_listener = listener; +} + +bool CasTcpTransmission::isReady() +{ + return m_isReady; +} + +// private method +int CasTcpTransmission::readN(uint8_t *buffer, uint32_t length) +{ + uint32_t readLen = 0; + while (m_isTaskRun && readLen < length) { + int ret = m_casClientSocket->Recv(buffer + readLen, length - readLen); + if (ret > 0) { + readLen += ret; + m_isReady = true; + } else { + if ((ret < 0) && ((0 == errno) || (EAGAIN == errno) || + (EWOULDBLOCK == errno) || (EINTR == errno) || (ETIMEDOUT == errno))) { + return 0; + } else { + m_isReady = false; + return -1; + } + } + } + return readLen; +} + +void CasTcpTransmission::recvTask() +{ + stream_msg_head_t msgHead; + const uint32_t headerLength = sizeof(stream_msg_head_t); + while (m_isTaskRun) { + int ret = readN((uint8_t*)&msgHead, headerLength); + if (ret != headerLength) { + usleep(100000); + continue; + } + uint32_t bodyLength = msgHead.GetPayloadSize(); + uint8_t *message = (uint8_t*)AllocBuffer(bodyLength + headerLength); + ret = readN(message + headerLength, bodyLength); + if (ret != bodyLength) { + FreeBuffer(message); + continue; + } + (void)memcpy_s(message, headerLength, &msgHead, headerLength); + dispatchMessage(msgHead, message, bodyLength + headerLength); + } +} + +void CasTcpTransmission::dispatchMessage(streamMsgHead &header, uint8_t *message, uint32_t length) +{ + std::shared_ptr listener = m_listener.lock(); + if (listener != nullptr) { + listener->onNewPacket(header.type, message, length, SOURCE_TCP); + } +} + +int CasTcpTransmission::getRecvStats(int , void *) +{ + return 0; +} \ No newline at end of file diff --git a/cloudphone/src/main/cpp/cas_controller/CasTcpTransmission.h b/cloudphone/src/main/cpp/cas_controller/CasTcpTransmission.h new file mode 100644 index 0000000000000000000000000000000000000000..91bdb1078b63949d27f3131e5ba4608ac2cd3a10 --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasTcpTransmission.h @@ -0,0 +1,56 @@ +// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CLOUDAPPSDK_CASTCP_H +#define CLOUDAPPSDK_CASTCP_H + +#include +#include +#include +#include "ICasTransmission.h" +#include "CasMsg.h" + +class CasTcpClientSocket; + +class CasTcpTransmission : public ICasTransmission { +public: + CasTcpTransmission(); + ~CasTcpTransmission(); + int init(uint32_t ip, uint16_t port, std::string &logPath) override; + int deinit() override; + int connect() override; + int start() override; + int stop() override; + int reconnect() override; + int send(uint8_t *buffer, uint32_t length) override; + void setListener(std::weak_ptr &listener) override; + bool isReady() override; + int getRecvStats(int type, void *value) override; + +private: + int readN(uint8_t *buffer, uint32_t length); + void recvTask(); + void dispatchMessage(streamMsgHead &header, uint8_t *body, uint32_t length); + +private: + uint32_t m_ip; + uint16_t m_port; + bool m_isTaskRun; + std::thread m_task; + std::shared_ptr m_casClientSocket; + std::weak_ptr m_listener; + bool m_isReady; +}; + +#endif // CLOUDAPPSDK_CASTCP_H diff --git a/cloudphone/src/main/cpp/cas_controller/CasTransmission.cpp b/cloudphone/src/main/cpp/cas_controller/CasTransmission.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4b6fe0c9a445719c7d96792fb2bf6d59a3591723 --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasTransmission.cpp @@ -0,0 +1,211 @@ +// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "CasTransmission.h" +#include "../cas_socket/CasTcpSocket.h" +#include "../cas_common/CasLog.h" +#include "../cas_common/CasBuffer.h" +#include "CasTcpTransmission.h" +#include "CasMtansTransmission.h" +#include "ICasTransmission.h" + +CasTransmission::CasTransmission(bool isStream) +{ + INFO("CasTransmission construct."); + m_stream = isStream; + m_casTcp = nullptr; +#if MTRANS_ENABLED + m_casMtrans = nullptr; +#endif +} + +CasTransmission::~CasTransmission() +{ + INFO("CasTransmission destruct."); +} + +int CasTransmission::init(uint32_t ip, uint16_t port, std::string &logPath) +{ + INFO("Init"); + std::lock_guard lock(m_mutex); + char strIp[INET_ADDRSTRLEN] = {0}; + inet_ntop(AF_INET, &ip, strIp, INET_ADDRSTRLEN); + INFO("ip=%s, port=%d", strIp, port); + m_casTcp = std::make_shared(); + if (m_casTcp == nullptr) { + ERR("Could't create CasTcpTransmission."); + return -1; + } + m_casTcp->init(ip, port, logPath); +#if MTRANS_ENABLED + if (m_stream) { + m_casMtrans = std::make_shared(); + if (m_casMtrans == nullptr) { + m_casTcp = nullptr; + return -1; + } + m_casMtrans->init(ip, port, logPath); + } +#endif + return 0; +} + +int CasTransmission::deinit() +{ + INFO("Deinit."); + std::lock_guard lock(m_mutex); + m_casTcp = nullptr; +#if MTRANS_ENABLED + m_casMtrans = nullptr; +#endif + return 0; +} + +int CasTransmission::connect() +{ + INFO("Connect."); + std::lock_guard lock(m_mutex); + if (m_casTcp == nullptr) { + ERR("m_casTcp is null."); + return -1; + } + if (m_casTcp->connect() < 0) { + return -1; + } +#if MTRANS_ENABLED + if (m_casMtrans != nullptr) { + if (m_casMtrans->connect() != 0) { + return -1; + } + } +#endif + return 0; +} + +int CasTransmission::start() +{ + INFO("Start."); + std::lock_guard lock(m_mutex); + if (m_casTcp == nullptr) { + ERR("m_casTcp is null."); + return -1; + } + if (m_casTcp->start() < 0) { + return -1; + } + +#if MTRANS_ENABLED + if (m_casMtrans != nullptr) { + if (m_casMtrans->start() != 0) { + return -1; + } + } +#endif + return 0; +} + +int CasTransmission::stop() +{ + INFO("Stop."); + std::lock_guard lock(m_mutex); + if (m_casTcp == nullptr) { + ERR("m_casTcp is null."); + return -1; + } + int ret = m_casTcp->stop(); +#if MTRANS_ENABLED + if (m_casMtrans != nullptr) { + m_casMtrans->stop(); + } +#endif + return ret; +} + +int CasTransmission::reconnect() +{ + INFO("Reconnect."); + std::lock_guard lock(m_mutex); + if (m_casTcp == nullptr) { + ERR("m_casTcp is null."); + return -1; + } + if (m_casTcp->reconnect() != 0) { + return -1; + } +#if MTRANS_ENABLED + if (m_casMtrans != nullptr) { + m_casMtrans->reconnect(); + } +#endif + return 0; +} + +int CasTransmission::send(uint8_t *buffer, uint32_t length) +{ + std::lock_guard lock(m_mutex); + if (m_casTcp == nullptr) { + ERR("m_casTcp is null."); + return -1; + } +#if MTRANS_ENABLED + stream_msg_head_t *msgHead = (stream_msg_head_t *)buffer; + if (m_casMtrans != nullptr && m_casMtrans->isReady()) { + if (msgHead->type == VirtualMicrophone + || msgHead->type == VirtualCamera + || msgHead->type == TouchInput + || msgHead->type == KeyEventInput + || msgHead->type == MotionEventInput) { + return m_casMtrans->send(buffer, length); + } + } + if (m_casMtrans != nullptr && msgHead->type == CasMsgType::HeartBeat) { + m_casMtrans->send(buffer, length); + } +#endif + return m_casTcp->send(buffer, length); +} + +void CasTransmission::setListener(std::weak_ptr listener) +{ + INFO("SetListener."); + std::lock_guard lock(m_mutex); + if (m_casTcp != nullptr) { + m_casTcp->setListener(listener); + } +#if MTRANS_ENABLED + if (m_casMtrans != nullptr) { + m_casMtrans->setListener(listener); + } +#endif +} + +int CasTransmission::getRecvStats(int type, void *stats) +{ + std::lock_guard lock(m_mutex); +#if MTRANS_ENABLED + if (m_casMtrans != nullptr) { + return m_casMtrans->getRecvStats(type, stats); + } +#endif + return -1; +} + +bool CasTransmission::isReady() +{ + std::lock_guard lock(m_mutex); + if (m_casTcp != nullptr) { + return m_casTcp->isReady(); + } + return false; +} \ No newline at end of file diff --git a/cloudphone/src/main/cpp/cas_controller/CasTransmission.h b/cloudphone/src/main/cpp/cas_controller/CasTransmission.h new file mode 100644 index 0000000000000000000000000000000000000000..78b67cf530bb02936a6cb24d4a5c99cdcf0ce6f0 --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasTransmission.h @@ -0,0 +1,54 @@ +// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CLOUDAPPSDK_CASCLIENT_H +#define CLOUDAPPSDK_CASCLIENT_H + +#include +#include +#include +#include +#include "ICasTransmission.h" + +class CasMtansTransmission; +class CasTcpTransmission; +struct StreamRecvStats; + +class CasTransmission { +public: + CasTransmission(bool isStream = true); + ~CasTransmission(); + + int init(uint32_t ip, uint16_t port, std::string &path); + int deinit(); + int connect(); + int start(); + int stop(); + int reconnect(); + int send(uint8_t *buffer, uint32_t length); + void setListener(std::weak_ptr listener); + int getRecvStats(int type, void *stats); + bool isReady(); + +private: + bool m_stream; + std::mutex m_mutex; + std::shared_ptr m_casTcp; + +#if MTRANS_ENABLED + std::shared_ptr m_casMtrans; +#endif +}; + +#endif // CLOUDAPPSDK_CASCLIENT_H diff --git a/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.cpp b/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a51a980033022d1b352d2e41159c74953c4b2016 --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.cpp @@ -0,0 +1,115 @@ +// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include "CasVideoHandler.h" +#include "../cas_decoder/CasVideoEngineCommon.h" +#include "../cas_common/CasMsg.h" +#include "../cas_common/CasBuffer.h" +#include "CasMsgCode.h" +#include "../CasCommon.h" + +CasVideoHandler::CasVideoHandler(std::shared_ptr &result) + :CasMessageHandler(result) +{ + INFO("CasVideoHandler constructor."); + m_videoPktStream = nullptr; + m_nativeWindow = nullptr; + m_videoThread = nullptr; + m_frameType = FrameType::H264; + m_rotation = 0; +} + +CasVideoHandler::~CasVideoHandler() +{ + INFO("CasVideoHandler destructor."); + stop(); + if (m_videoPktStream) { + delete m_videoPktStream; + } +} + +int CasVideoHandler::init() +{ + INFO("init"); + m_videoPktStream = new (std::nothrow) CasDataPipe(false); + if (m_videoPktStream == nullptr) { + ERR("Create video CasDataPipe failed."); + return -1; + } + return 0; +} + +int CasVideoHandler::start() +{ + INFO("Start."); + std::lock_guard lockGuard(m_lock); + m_videoThread = new (std::nothrow) CasVideoHDecodeThread(m_nativeWindow, m_frameType, m_rotation, this); + if (m_videoThread == nullptr) { + ERR("Couldn't create CasVideoHDecodeThread."); + return -1; + } + m_videoThread->SetDecodePktHandle(m_videoPktStream); + if (m_videoThread->Start() != 0) { + delete m_videoThread; + m_videoThread = nullptr; + return -1; + } + return 0; +} + +int CasVideoHandler::stop() +{ + INFO("Stop."); + std::lock_guard lockGuard(m_lock); + if (m_videoThread == nullptr) { + WARN("Already stop."); + return -1; + } + m_videoThread->Stop(); + m_videoThread->Exit(); + delete m_videoThread; + m_videoThread = nullptr; + m_videoPktStream->Clear(); + return 0; +} + +void CasVideoHandler::handleMessage(uint8_t *message, uint32_t length, int source) +{ + std::lock_guard lockGuard(m_lock); + if (m_videoPktStream == nullptr) { + FreeBuffer(message); + ERR("VideoPktStream is null."); + return; + } + m_videoPktStream->Handle(message); + m_messageResult->onVideoPacketRecv(); +} + +void CasVideoHandler::setParameters(ANativeWindow *nativeWindow, FrameType frameType, int rotation) +{ + INFO("setParameters."); + std::lock_guard lockGuard(m_lock); + m_nativeWindow = nativeWindow; + m_frameType = frameType; + m_rotation = rotation; +} + +void CasVideoHandler::OnFirstFrame() +{ + if (m_messageResult != nullptr) { + std::string message = CasMsgCode::GetMsg(CAS_FIRST_FRAME); + m_messageResult->onCmdRecv(CAS_FIRST_FRAME, message); + } +} \ No newline at end of file diff --git a/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.h b/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.h new file mode 100644 index 0000000000000000000000000000000000000000..3a423b9e1e128a2dd1587664de99c2d712f6a8b3 --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.h @@ -0,0 +1,51 @@ +// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CLOUDAPPSDK_CASVIDEOHANDLER_H +#define CLOUDAPPSDK_CASVIDEOHANDLER_H + +#include +#include +#include +#include +#include "CasMessageHandler.h" +#include "../cas_decoder/CasVideoEngineCommon.h" +#include "../cas_service/CasDataPipe.h" +#include "../cas_decoder/CasVideoEngine.h" +#include "../cas_service/CasVideoHDecodeThread.h" + +class CasVideoHandler :public CasMessageHandler, public CasFirstVideoFrameListener{ +public: + CasVideoHandler(std::shared_ptr &result); + ~CasVideoHandler(); + + int init() override; + int start() override; + int stop() override; + void handleMessage(uint8_t *message, uint32_t length, int source) override; + void setParameters(ANativeWindow *nativeWindow, FrameType frameType, int rotation); + +public: + void OnFirstFrame() override; + +private: + CasVideoHDecodeThread *m_videoThread; + CasDataPipe *m_videoPktStream; + std::mutex m_lock; + ANativeWindow *m_nativeWindow; + FrameType m_frameType; + int m_rotation; +}; + +#endif // CLOUDAPPSDK_CASVIDEOHANDLER_H diff --git a/cloudphone/src/main/cpp/cas_controller/ICasTransmission.h b/cloudphone/src/main/cpp/cas_controller/ICasTransmission.h new file mode 100644 index 0000000000000000000000000000000000000000..2a1978e24b0f0388d5d166a21258c1d5fb27b3cb --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/ICasTransmission.h @@ -0,0 +1,50 @@ +// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CLOUDAPPSDK_ICASTRANSISSION_H +#define CLOUDAPPSDK_ICASTRANSISSION_H + +#include "stdint.h" + +enum DataSource { + SOURCE_MTRANS = 0, + SOURCE_TCP +}; + +class CasTransmissionListener { +public: + CasTransmissionListener() {}; + virtual ~CasTransmissionListener() {}; + virtual void onNewPacket(int type, uint8_t *buffer, uint32_t length, int dataSource) = 0; + virtual void onNewCmd(int cmd, std::string detail) = 0; +}; + +class ICasTransmission { +public: + ICasTransmission() {} + virtual ~ICasTransmission(){} + + virtual int init(uint32_t ip, uint16_t port, std::string &logPath) = 0; + virtual int deinit() = 0; + virtual int connect() = 0; + virtual int start() = 0; + virtual int stop() = 0; + virtual int reconnect() = 0; + virtual int send(uint8_t *buffer, uint32_t length) = 0; + virtual void setListener(std::weak_ptr &listener) = 0; + virtual bool isReady() = 0; + virtual int getRecvStats(int type, void *value) = 0; +}; + +#endif // CLOUDAPPSDK_ICASTRANSISSION_H diff --git a/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.cpp b/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.cpp index 9167fa7795dd7a6b698c24c270e1addfdd65e98c..69ddc8713165b8f91935a0c6c17afffa02e44dbd 100644 --- a/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.cpp +++ b/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.cpp @@ -24,11 +24,8 @@ namespace { const int WAITING_TIME_US = 10000; } -CasDecodeController *CasDecodeController::g_instance = nullptr; - #if __ANDROID_API__ >= 24 AChoreographer *choreographer; -CasDecodeController *g_controller = nullptr; uint32_t CasDecodeController::ReduceFps(uint64_t frameBuffer) { @@ -74,13 +71,13 @@ void CasDecodeController::ClearDiscardFrameCache() m_HoldFrames = 0; } -void FrameCallback(long frameTime, void* data) +void FrameCallback(long frameTime, void* parameters) { - if (!g_controller->IsStatus(EngineStat::ENGINE_RUNNING)) { + CasDecodeController *controller = (CasDecodeController *)parameters; + if (!controller->IsStatus(EngineStat::ENGINE_RUNNING)) { return; } - bool isDecodeFirstFrame = false; int iRet = 0; uint64_t startUs = 0; uint64_t nowUs = 0; @@ -90,16 +87,16 @@ void FrameCallback(long frameTime, void* data) startUs = casVideoUtil->GetNow(); } - CasDecoder* decoder = g_controller->GetCasDecoder(); - CasFirstVideoFrameListener* firstVideoFrameListener = g_controller->GetCasFirstVideoFrameListener(); - CasVideoDecodeStatListener* videoDecodeStatListener = g_controller->GetCasVideoDecodeStatListener(); + CasDecoder* decoder = controller->GetCasDecoder(); + CasFirstVideoFrameListener* firstVideoFrameListener = controller->GetCasFirstVideoFrameListener(); + CasVideoDecodeStatListener* videoDecodeStatListener = controller->GetCasVideoDecodeStatListener(); // 支持网络缓存堆积时按照策略丢帧 bool isDiscard = true; const uint64_t discardBuffCount = 4; uint64_t frameBuffer = 0; - if (g_controller->m_InputFrameNum > g_controller->m_DisplayFrameNum) { - frameBuffer = g_controller->m_InputFrameNum - g_controller->m_DisplayFrameNum; + if (controller->m_InputFrameNum > controller->m_DisplayFrameNum) { + frameBuffer = controller->m_InputFrameNum - controller->m_DisplayFrameNum; } // 缓存堆积时,根据策略丢缓存后马上出下一帧,一次处理2帧 uint64_t reduceBegin = 0; @@ -108,46 +105,46 @@ void FrameCallback(long frameTime, void* data) uint64_t display1End = 0; if (frameBuffer >= discardBuffCount) { reduceBegin = casVideoUtil->GetNow(); - uint32_t updatePatternIdx = g_controller->ReduceFps(frameBuffer); - if (g_controller->isStartDiscard == false && g_controller->m_UpdatePatternIdx != updatePatternIdx - && g_controller->m_UpdatePatternIdx != FPSPATTERNS60) { - g_controller->isStartDiscard = true; + uint32_t updatePatternIdx = controller->ReduceFps(frameBuffer); + if (controller->isStartDiscard == false && controller->m_UpdatePatternIdx != updatePatternIdx + && controller->m_UpdatePatternIdx != FPSPATTERNS60) { + controller->isStartDiscard = true; } - isDiscard = g_controller->IsDiscardFrame(updatePatternIdx); + isDiscard = controller->IsDiscardFrame(updatePatternIdx); // 是否丢帧 if (isDiscard) { - g_controller->m_DiscardFrameNum++; + controller->m_DiscardFrameNum++; CasVideoUtil::GetInstance()->DropOneFrame(); INFO("FrameCallback start discard, frameBuffer=%lu, index=%u, last index=%lu, m_DiscardFrameNum=%lu, m_InputFrameNum=%lu, m_DisplayFrameNum=%lu", - frameBuffer, updatePatternIdx, g_controller->m_UpdatePatternIdx, g_controller->m_DiscardFrameNum, g_controller->m_InputFrameNum, g_controller->m_DisplayFrameNum); + frameBuffer, updatePatternIdx, controller->m_UpdatePatternIdx, controller->m_DiscardFrameNum, controller->m_InputFrameNum, controller->m_DisplayFrameNum); } reduceEnd = casVideoUtil->GetNow(); display1Begin = casVideoUtil->GetNow(); iRet = static_cast(decoder->OutputAndDisplay(!isDiscard)); display1End = casVideoUtil->GetNow(); if (iRet != DECODER_OUTPUT_RETRY) { - g_controller->m_DisplayFrameNum++; + controller->m_DisplayFrameNum++; } else if (iRet == DECODER_OUTPUT_ERR) { ERR("FrameCallback OutputAndDisplay error, ret = %d, isDiscard = %d", iRet, isDiscard); } } else { - if (g_controller->isStartDiscard == true) { - g_controller->isStartDiscard = false; - g_controller->ClearDiscardFrameCache(); + if (controller->isStartDiscard == true) { + controller->isStartDiscard = false; + controller->ClearDiscardFrameCache(); } } uint64_t display2Begin = casVideoUtil->GetNow(); if (isDiscard) { iRet = static_cast(decoder->OutputAndDisplay(true)); if (iRet != DECODER_OUTPUT_RETRY) { - g_controller->m_DisplayFrameNum++; + controller->m_DisplayFrameNum++; } } uint64_t display2End = casVideoUtil->GetNow(); if (iRet == DECODER_SUCCESS) { - if (!isDecodeFirstFrame) { - isDecodeFirstFrame = true; + if (!controller->m_isFirstFrame) { + controller->m_isFirstFrame = true; if (firstVideoFrameListener != nullptr) { firstVideoFrameListener->OnFirstFrame(); } @@ -163,9 +160,9 @@ void FrameCallback(long frameTime, void* data) } else if (iRet == DECODER_OUTPUT_ERR) { ERR("FrameCallback OutputAndDisplay error, ret=%d", iRet); } - if (g_controller->m_InputFrameNum % 500 == 1) { + if (controller->m_InputFrameNum % 500 == 1) { INFO("FrameCallback m_DisplayFrameNum=%lu, m_InputFrameNum=%lu, m_DiscardFrameNum=%lu", - g_controller->m_DisplayFrameNum, g_controller->m_InputFrameNum, g_controller->m_DiscardFrameNum); + controller->m_DisplayFrameNum, controller->m_InputFrameNum, controller->m_DiscardFrameNum); } uint64_t vsyncEnd = casVideoUtil->GetNow(); uint64_t duration = vsyncEnd - startUs; @@ -177,7 +174,7 @@ void FrameCallback(long frameTime, void* data) duration, reduceTime, display1Time, display2Time); } - AChoreographer_postFrameCallback(choreographer, FrameCallback, nullptr); + AChoreographer_postFrameCallback(controller->m_choreographer, FrameCallback, (void *)controller); } #endif @@ -196,39 +193,6 @@ CasDecodeController::~CasDecodeController() this->Destroy(); } -/* - * @fn GetInstance - * @brief to get CasDecodeController singleton - */ -CasDecodeController *CasDecodeController::GetInstance() -{ - if (g_instance == nullptr) { - g_instance = new (std::nothrow) CasDecodeController(); - if (g_instance == nullptr) { - ERR("Failed to instantiate."); - return nullptr; - } - } - return g_instance; -} - -/* - * @fn DestroyInstance - * @brief to release CasDecodeController singleton - */ -uint32_t CasDecodeController::DestroyInstance() -{ - if (g_instance != nullptr) { - g_instance->Destroy(); - delete g_instance; - g_instance = nullptr; - INFO("DestroyInstance success."); - return SUCCESS; - } - INFO("Instance already destroyed."); - return VIDEO_ENGINE_CLIENT_DESTROY_ERR; -} - CasDecoder* CasDecodeController::GetCasDecoder() { return m_decoder; @@ -251,26 +215,28 @@ CasVideoDecodeStatListener* CasDecodeController::GetCasVideoDecodeStatListener() */ void OutputTaskEntry(CasDecodeController *controller) { + INFO("begin"); if (controller == nullptr) { return; } #if __ANDROID_API__ >= 24 - g_controller = controller; ALooper* looper = ALooper_forThread(); if (looper == nullptr) { ERR("Failed to get looper, preparing new one"); looper = ALooper_prepare(0); } - g_controller->SetSubThreadStatus(true); - choreographer = AChoreographer_getInstance(); - AChoreographer_postFrameCallback(choreographer, FrameCallback, nullptr); + controller->SetSubThreadStatus(true); + controller->m_choreographer = AChoreographer_getInstance(); + + AChoreographer_postFrameCallback(controller->m_choreographer, FrameCallback, (void *)controller); while (controller->IsStatus(EngineStat::ENGINE_RUNNING)) { int id; int events; void* data; - while ((id = ALooper_pollAll(0, nullptr, &events, &data)) >= 0) { + const int timeout = 5; + while ((id = ALooper_pollAll(timeout, nullptr, &events, &data)) >= 0) { if (!controller->IsStatus(EngineStat::ENGINE_RUNNING)) { controller->SetSubThreadStatus(false); return; @@ -289,7 +255,7 @@ void OutputTaskEntry(CasDecodeController *controller) if (casVideoUtil != nullptr) { startUs = casVideoUtil->GetNow(); } - iRet = static_cast(controller->m_decoder->OutputAndDisplay()); + iRet = static_cast(controller->m_decoder->OutputAndDisplay(true)); if (iRet == DECODER_OUTPUT_ERR) { ERR("Sub-Thread exited."); break; @@ -311,6 +277,7 @@ void OutputTaskEntry(CasDecodeController *controller) } controller->SetSubThreadStatus(false); #endif + INFO("end"); } /* @@ -372,9 +339,12 @@ uint32_t CasDecodeController::Start() return VIDEO_ENGINE_CLIENT_START_ERR; } } + m_isFirstFrame = false; SetStatus(EngineStat::ENGINE_RUNNING); + SetSubThreadStatus(true); // to start sub-thread for the output of decoded frame std::thread outputTask(OutputTaskEntry, this); + pthread_setname_np(outputTask.native_handle(), "OutputTaskEntry"); if (outputTask.joinable()) { outputTask.detach(); } diff --git a/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.h b/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.h index a93fc76eeeaf77d19212c4c4c0b341f12ac5671f..83c8ce775d9cee95a532ce1ad875d1a970284902 100644 --- a/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.h +++ b/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.h @@ -15,6 +15,11 @@ #ifndef CLOUDAPPSDK_CASDECODECONTROLLER_H #define CLOUDAPPSDK_CASDECODECONTROLLER_H +#if __ANDROID_API__ >= 24 +#include +#include +#endif + #include #include #include @@ -45,16 +50,16 @@ const char UPDATE_FPSPATTERNS[FPSPATTERNS_MAX][8] = { class CasDecodeController { public: /* - * @fn GetInstance - * @brief to get CasDecodeController singleton + * @fn CasDecodeController + * @brief constructor */ - static CasDecodeController *GetInstance(); + CasDecodeController(); /* - * @fn DestroyInstance - * @brief to release CasDecodeController singleton + * @fn CasDecodeController + * @brief destructor */ - static uint32_t DestroyInstance(); + ~CasDecodeController(); /* * @fn Init @@ -179,27 +184,16 @@ public: uint32_t m_UpdatePatternOffset = 0; uint32_t m_HoldFrames = 0; + bool m_isFirstFrame = false; bool isStartDiscard = false; // 是否触发丢帧策略,进行日志打印 + AChoreographer *m_choreographer = nullptr; private: - /* - * @fn CasDecodeController - * @brief constructor - */ - CasDecodeController(); - /* - * @fn CasDecodeController - * @brief destructor - */ - ~CasDecodeController(); - - static CasDecodeController *g_instance; std::atomic m_subThreadRunning {false}; std::atomic m_engineStatus {EngineStat::ENGINE_INVALID}; CasDecoder *m_decoder = nullptr; CasFirstVideoFrameListener *m_firstFrameListener = nullptr; CasVideoDecodeStatListener *m_decodeStatListener = nullptr; - }; #endif // CLOUDAPPSDK_CASDECODECONTROLLER_H \ No newline at end of file diff --git a/cloudphone/src/main/cpp/cas_decoder/CasVideoEngine.cpp b/cloudphone/src/main/cpp/cas_decoder/CasVideoEngine.cpp index a3d49e28869915161e29ef00d78b4e1f0aa027ff..0fe4183a312d411501a9eaaf64e9b26c18d8c6d4 100644 --- a/cloudphone/src/main/cpp/cas_decoder/CasVideoEngine.cpp +++ b/cloudphone/src/main/cpp/cas_decoder/CasVideoEngine.cpp @@ -30,7 +30,13 @@ CasVideoEngine::CasVideoEngine() = default; * @fn CasVideoEngine * @brief destructor */ -CasVideoEngine::~CasVideoEngine() = default; +CasVideoEngine::~CasVideoEngine() +{ + if (mDecodeController != nullptr) { + delete mDecodeController; + mDecodeController = nullptr; + } +} /* * @fn InitDecoder @@ -54,12 +60,12 @@ uint32_t CasVideoEngine::InitDecoder(ANativeWindow *nativeWindow, DecoderType ty ERR("Unsupported DecoderType."); return VIDEO_ENGINE_CLIENT_PARAM_UNSUPPORTED; } - CasDecodeController *decodeController = CasDecodeController::GetInstance(); - if (decodeController == nullptr) { + mDecodeController = new (std::nothrow) CasDecodeController(); + if (mDecodeController == nullptr) { ERR("Failed to instantiate."); return VIDEO_ENGINE_CLIENT_INIT_FAIL; } - return decodeController->Init(nativeWindow, frameType, rotationDegrees); + return mDecodeController->Init(nativeWindow, frameType, rotationDegrees); } /* @@ -71,12 +77,11 @@ uint32_t CasVideoEngine::InitDecoder(ANativeWindow *nativeWindow, DecoderType ty uint32_t CasVideoEngine::StartDecoder() { std::lock_guard lockGuard(m_lock); - CasDecodeController *decodeController = CasDecodeController::GetInstance(); - if (decodeController == nullptr) { + if (mDecodeController == nullptr) { ERR("Failed to instantiate."); return VIDEO_ENGINE_CLIENT_START_ERR; } - return decodeController->Start(); + return mDecodeController->Start(); } /* @@ -99,12 +104,11 @@ uint32_t CasVideoEngine::DecodeFrame(uint8_t *buf, size_t length) ERR("Exceed max data length"); return VIDEO_ENGINE_CLIENT_PARAM_INVALID; } - CasDecodeController *decodeController = CasDecodeController::GetInstance(); - if (decodeController == nullptr) { + if (mDecodeController == nullptr) { ERR("Failed to instantiate."); return VIDEO_ENGINE_CLIENT_DECODE_ERR; } - return decodeController->Decode(buf, length); + return mDecodeController->Decode(buf, length); } /* @@ -116,12 +120,11 @@ uint32_t CasVideoEngine::DecodeFrame(uint8_t *buf, size_t length) uint32_t CasVideoEngine::StopDecoder() { std::lock_guard lockGuard(m_lock); - CasDecodeController *decodeController = CasDecodeController::GetInstance(); - if (decodeController == nullptr) { + if (mDecodeController == nullptr) { ERR("Failed to instantiate."); return VIDEO_ENGINE_CLIENT_STOP_ERR; } - return decodeController->Stop(); + return mDecodeController->Stop(); } /* @@ -133,7 +136,11 @@ uint32_t CasVideoEngine::StopDecoder() uint32_t CasVideoEngine::DestroyDecoder() { std::lock_guard lockGuard(m_lock); - return CasDecodeController::DestroyInstance(); + if (mDecodeController != nullptr) { + delete mDecodeController; + mDecodeController = nullptr; + } + return SUCCESS; } /* @@ -145,12 +152,11 @@ uint32_t CasVideoEngine::DestroyDecoder() */ uint32_t CasVideoEngine::GetDecoderStatus(EngineStat &stat) { - CasDecodeController *decodeController = CasDecodeController::GetInstance(); - if (decodeController == nullptr) { + if (mDecodeController == nullptr) { ERR("Failed to instantiate."); return VIDEO_ENGINE_CLIENT_GET_STATUS_ERR; } - stat = decodeController->GetStatus(); + stat = mDecodeController->GetStatus(); return SUCCESS; } @@ -163,31 +169,28 @@ uint32_t CasVideoEngine::GetDecoderStatus(EngineStat &stat) */ uint32_t CasVideoEngine::GetDecoderStatistics(DecoderStatistics &statistics) { - CasDecodeController *decodeController = CasDecodeController::GetInstance(); - if (decodeController == nullptr) { + if (mDecodeController == nullptr) { ERR("Failed to instantiate."); return VIDEO_ENGINE_CLIENT_GET_STAT_ERR; } - statistics = decodeController->GetStatistics(); + statistics = mDecodeController->GetStatistics(); return SUCCESS; } void CasVideoEngine::SetFirstVidFrameListener(CasFirstVideoFrameListener *listener) { - CasDecodeController *decodeController = CasDecodeController::GetInstance(); - if (decodeController == nullptr) { + if (mDecodeController == nullptr) { ERR("Failed to instantiate."); return; } - decodeController->SetFirstFrameListener(listener); + mDecodeController->SetFirstFrameListener(listener); } void CasVideoEngine::SetVideoDecodeStatListener(CasVideoDecodeStatListener *listener) { - CasDecodeController *decodeController = CasDecodeController::GetInstance(); - if (decodeController == nullptr) { + if (mDecodeController == nullptr) { ERR("Failed to instantiate."); return; } - decodeController->SetDecodeStatListener(listener); + mDecodeController->SetDecodeStatListener(listener); } \ No newline at end of file diff --git a/cloudphone/src/main/cpp/cas_decoder/CasVideoEngine.h b/cloudphone/src/main/cpp/cas_decoder/CasVideoEngine.h index 18a954ec5bc69605cd2831d89831a8aba74ce423..ac0e2e295fac298842a66c841454d11887f789db 100644 --- a/cloudphone/src/main/cpp/cas_decoder/CasVideoEngine.h +++ b/cloudphone/src/main/cpp/cas_decoder/CasVideoEngine.h @@ -19,6 +19,7 @@ #include #include #include "CasVideoEngineCommon.h" +#include "CasDecodeController.h" class CasVideoEngine { public: @@ -114,5 +115,6 @@ public: private: std::mutex m_lock = {}; + CasDecodeController *mDecodeController = nullptr; }; #endif // CLOUDAPPSDK_CASVIDEOENGINE_H \ No newline at end of file diff --git a/cloudphone/src/main/cpp/cas_service/CasCmdController.cpp b/cloudphone/src/main/cpp/cas_service/CasCmdController.cpp index 66eab1ce4450437996af59ef2af80dc0b928c297..52156412683dd3edd8e88cc8ed638a1fd0b7cdef 100644 --- a/cloudphone/src/main/cpp/cas_service/CasCmdController.cpp +++ b/cloudphone/src/main/cpp/cas_service/CasCmdController.cpp @@ -24,7 +24,7 @@ using namespace std; -CasCmdController::CasCmdController(CasStreamBuildSender *streamBuildSender) +CasCmdController::CasCmdController(std::shared_ptr streamBuildSender) { m_ctrlListener = nullptr; m_streamBuildSender = streamBuildSender; diff --git a/cloudphone/src/main/cpp/cas_service/CasCmdController.h b/cloudphone/src/main/cpp/cas_service/CasCmdController.h index ac0d4b87ae99dbba14d74c07d58cbd50a93a9215..28cf96280ea1651703ff847f3ff652f16c725588 100644 --- a/cloudphone/src/main/cpp/cas_service/CasCmdController.h +++ b/cloudphone/src/main/cpp/cas_service/CasCmdController.h @@ -33,7 +33,7 @@ public: class CasCmdController { public: - explicit CasCmdController(CasStreamBuildSender *streamBuildSender); + explicit CasCmdController(std::shared_ptr streamBuildSender); ~CasCmdController(); @@ -45,7 +45,7 @@ public: private: CasControllerListener *m_ctrlListener; - CasStreamBuildSender *m_streamBuildSender; + std::shared_ptr m_streamBuildSender; }; #endif // CLOUDAPPSDK_CASCMDCONTROLLER_H diff --git a/cloudphone/src/main/cpp/cas_service/CasDataPipe.cpp b/cloudphone/src/main/cpp/cas_service/CasDataPipe.cpp index d3091a23e1fd2e3fccdd3415b313310ba45c54c8..b42decc9e8065a439e4e6f98be8a2c0938682447 100644 --- a/cloudphone/src/main/cpp/cas_service/CasDataPipe.cpp +++ b/cloudphone/src/main/cpp/cas_service/CasDataPipe.cpp @@ -38,6 +38,7 @@ CasDataPipe::~CasDataPipe() void CasDataPipe::Clear() noexcept { + lock_guard lck(this->m_lock); while (!(this->m_deque.empty())) { void *pPkt = this->m_deque.front(); this->m_deque.pop_front(); @@ -47,8 +48,8 @@ void CasDataPipe::Clear() noexcept void CasDataPipe::Exit() { + lock_guard lck(this->m_lock); this->m_status = false; - unique_lock lck(this->m_lock); this->m_cv.notify_all(); } diff --git a/cloudphone/src/main/cpp/cas_service/CasTouch.cpp b/cloudphone/src/main/cpp/cas_service/CasTouch.cpp index 549c8c5d3e49b14949e5072c69e30ed15a0bbe6e..469902f24cad7957a3f38a9e2d367dcc7304eec2 100644 --- a/cloudphone/src/main/cpp/cas_service/CasTouch.cpp +++ b/cloudphone/src/main/cpp/cas_service/CasTouch.cpp @@ -16,9 +16,9 @@ #include "CasTouch.h" #include "CasLog.h" -CasTouch::CasTouch(CasSocket *casSocket) +CasTouch::CasTouch(std::shared_ptr casTransmission) { - Init(casSocket); + Init(casTransmission); } CasTouch::~CasTouch() @@ -26,9 +26,9 @@ CasTouch::~CasTouch() delete m_streamBuildSender; } -void CasTouch::Init(CasSocket *casSocket) +void CasTouch::Init(std::shared_ptr &casTransmission) { - m_streamBuildSender = new CasStreamBuildSender(casSocket); + m_streamBuildSender = new CasStreamBuildSender(casTransmission); } bool CasTouch::SendTouchEvent(int id, int action, int x, int y, int pressure, int time, int orientation, int height, int width) diff --git a/cloudphone/src/main/cpp/cas_service/CasTouch.h b/cloudphone/src/main/cpp/cas_service/CasTouch.h index c12ec48c31ec2c99e575debc6a3c876b016a4e64..12c8fe3ac73eacd9217527cdcd5c8be705e72a7c 100644 --- a/cloudphone/src/main/cpp/cas_service/CasTouch.h +++ b/cloudphone/src/main/cpp/cas_service/CasTouch.h @@ -21,11 +21,11 @@ class CasTouch { public: - explicit CasTouch(CasSocket *casSocket); + explicit CasTouch(std::shared_ptr casTransmission); ~CasTouch(); - void Init(CasSocket *casSocket); + void Init(std::shared_ptr &casTransmission); bool SendTouchEvent(int id, int action, int x, int y, int pressure, int time, int orientation, int height, int width); diff --git a/cloudphone/src/main/cpp/cas_service/CasVideoHDecodeThread.cpp b/cloudphone/src/main/cpp/cas_service/CasVideoHDecodeThread.cpp index ab3045189b32b02430f25d77e4287099377d9dee..239088e9897b42e5a4488f46c7797d2b1ba1bd43 100644 --- a/cloudphone/src/main/cpp/cas_service/CasVideoHDecodeThread.cpp +++ b/cloudphone/src/main/cpp/cas_service/CasVideoHDecodeThread.cpp @@ -22,16 +22,6 @@ using namespace std; -class CasFirstVideoFrameImpl : public CasFirstVideoFrameListener { -public: - void OnFirstFrame() override - { - if (CasController::GetInstance() != nullptr) { - CasController::GetInstance()->NotifyFirstVideoFrame(); - } - } -}; - class CasVideoDecodeStatImpl : public CasVideoDecodeStatListener { public: void OnDecodeOneFrame(uint64_t decTime) override @@ -40,7 +30,9 @@ public: } }; -CasVideoHDecodeThread::CasVideoHDecodeThread(ANativeWindow *nativeWindow, FrameType frameType, int rotationDegrees) +CasVideoHDecodeThread:: +CasVideoHDecodeThread(ANativeWindow *nativeWindow, FrameType frameType, + int rotationDegrees, CasFirstVideoFrameListener *listener) { this->m_nativeWindow = nativeWindow; this->m_videoEngine = nullptr; @@ -51,6 +43,7 @@ CasVideoHDecodeThread::CasVideoHDecodeThread(ANativeWindow *nativeWindow, FrameT this->m_threadStatus = CAS_THREAD_INIT; this->m_frameType = frameType; this->m_rotationDegrees = rotationDegrees; + this->m_firstVideoFrame = listener; } CasVideoHDecodeThread::~CasVideoHDecodeThread() @@ -60,11 +53,6 @@ CasVideoHDecodeThread::~CasVideoHDecodeThread() this->m_videoEngine = nullptr; } - if (this->m_firstVideoFrame != nullptr) { - delete this->m_firstVideoFrame; - this->m_firstVideoFrame = nullptr; - } - if (this->m_videoDecodeStat != nullptr) { delete this->m_videoDecodeStat; this->m_videoDecodeStat = nullptr; @@ -179,29 +167,22 @@ int CasVideoHDecodeThread::Start() ERR("CasVideoEngine new, return nullptr."); return -1; } - if (this->m_firstVideoFrame != nullptr) { - delete this->m_firstVideoFrame; - this->m_firstVideoFrame = nullptr; - } + if (this->m_videoDecodeStat != nullptr) { delete this->m_videoDecodeStat; this->m_videoDecodeStat = nullptr; } - this->m_firstVideoFrame = new (std::nothrow) CasFirstVideoFrameImpl(); - if (this->m_firstVideoFrame == nullptr) { - ERR("First video frame is null."); - return -1; - } - this->m_videoEngine->SetFirstVidFrameListener(this->m_firstVideoFrame); + this->m_videoDecodeStat = new (std::nothrow) CasVideoDecodeStatImpl(); if (this->m_videoDecodeStat == nullptr) { ERR("Video decode stat is null."); return -1; } - this->m_videoEngine->SetVideoDecodeStatListener(m_videoDecodeStat); uint32_t initRet = this->m_videoEngine->InitDecoder(this->m_nativeWindow, DecoderType::DECODER_TYPE_HW, m_frameType, m_rotationDegrees); + this->m_videoEngine->SetFirstVidFrameListener(this->m_firstVideoFrame); + this->m_videoEngine->SetVideoDecodeStatListener(m_videoDecodeStat); if (initRet != 0) { ERR("Init error %u", initRet); EngineStat status; @@ -270,10 +251,7 @@ int CasVideoHDecodeThread::Exit() this->m_videoEngine->StopDecoder(); this->m_videoEngine->DestroyDecoder(); } - if (this->m_firstVideoFrame != nullptr) { - delete this->m_firstVideoFrame; - this->m_firstVideoFrame = nullptr; - } + this->m_firstVideoFrame = nullptr; return 0; } INFO("Video decode thread exit."); diff --git a/cloudphone/src/main/cpp/cas_service/CasVideoHDecodeThread.h b/cloudphone/src/main/cpp/cas_service/CasVideoHDecodeThread.h index be697bf06f92809dbaf17be1cefa055c977b7a78..13299a0fbe312bc77351ee9f39544af4c776efed 100644 --- a/cloudphone/src/main/cpp/cas_service/CasVideoHDecodeThread.h +++ b/cloudphone/src/main/cpp/cas_service/CasVideoHDecodeThread.h @@ -20,12 +20,12 @@ #include "../cas_decoder/CasVideoEngine.h" #include "CasDataPipe.h" -class CasFirstVideoFrameImpl; class CasVideoDecodeStatImpl; class CasVideoHDecodeThread { public: - explicit CasVideoHDecodeThread(ANativeWindow *nativeWindow, FrameType frameType, int rotationDegrees); + explicit CasVideoHDecodeThread(ANativeWindow *nativeWindow, FrameType frameType, + int rotationDegrees, CasFirstVideoFrameListener *listener); ~CasVideoHDecodeThread(); @@ -49,7 +49,7 @@ public: CasDataPipe *m_videoPktStream; CasVideoEngine *m_videoEngine; - CasFirstVideoFrameImpl *m_firstVideoFrame; + CasFirstVideoFrameListener *m_firstVideoFrame; CasVideoDecodeStatImpl *m_videoDecodeStat; private: diff --git a/cloudphone/src/main/cpp/cas_stream/CMakeLists.txt b/cloudphone/src/main/cpp/cas_stream/CMakeLists.txt index 30e9046b8aa17788f33a0161bd745a1ed0ef10c0..99929d96e55f57e5572c3f0e59945a995b62d0b6 100644 --- a/cloudphone/src/main/cpp/cas_stream/CMakeLists.txt +++ b/cloudphone/src/main/cpp/cas_stream/CMakeLists.txt @@ -2,7 +2,7 @@ aux_source_directory(. CAS_STREAM_SRC_LIST) add_library(cas_stream STATIC ${CAS_STREAM_SRC_LIST}) target_link_libraries( # Specifies the target library. cas_stream - cas_common cas_socket + cas_controller ) \ No newline at end of file diff --git a/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp b/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp index cb201a2c50b245ff963c051755a28e74c6f78a48..61b372648f5678c7b75df5df4ca4668547ce6404 100644 --- a/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp +++ b/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp @@ -19,25 +19,21 @@ #include "CasMsg.h" #include "CasStreamBuildSender.h" -CasStreamBuildSender::CasStreamBuildSender(CasSocket *socket) +CasStreamBuildSender::CasStreamBuildSender(std::shared_ptr casTransmission) { - this->m_socket = socket; + this->m_casTransmission = casTransmission; } CasStreamBuildSender::~CasStreamBuildSender() { - this->m_socket = nullptr; -} - -void CasStreamBuildSender::SetNetTrans(NetTrans *netTrans) -{ - m_mtrans = netTrans; + INFO("Destructor m_casTransmission %d", m_casTransmission.use_count()); + this->m_casTransmission = nullptr; } int CasStreamBuildSender::SendDataToServer(CasMsgType type, const void *buf, size_t len) { - if (m_socket == nullptr) { - ERR("Failed to send data, socket is null."); + if (m_casTransmission == nullptr) { + ERR("Failed to send data, client is null."); return -1; } @@ -118,14 +114,7 @@ int CasStreamBuildSender::SendDataToServer(CasMsgType type, const void *buf, siz } for (size_t pos = 0; pos < dataLen;) { - ssize_t stat = m_socket->Send(outBuffer + pos, dataLen - pos); - -#if MTRANS_ENABLED - if (m_mtrans != nullptr && type == HeartBeat && m_socket->GetStatus() == SOCKET_STATUS_RUNNING) { - m_mtrans->SendCmdData(reinterpret_cast(outBuffer + pos), dataLen - pos); - } -#endif - + ssize_t stat = m_casTransmission->send((uint8_t*)outBuffer + pos, dataLen - pos); if (stat < 0) { ERR("Socket send failed: %s.", strerror(errno)); free(outBuffer); diff --git a/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.h b/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.h index 5c8c40f470e75f14270045232f2f71b5095ff142..f0be7aa9ab7236772097f5903d2a66d9143ee67c 100644 --- a/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.h +++ b/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.h @@ -18,10 +18,11 @@ #include "CasMsg.h" #include "CasSocket.h" #include "../libs/mtrans/include/net_trans.h" +#include "../cas_controller/CasTransmission.h" class CasStreamBuildSender { public: - explicit CasStreamBuildSender(CasSocket *socket); + explicit CasStreamBuildSender(std::shared_ptr casTransmission); ~CasStreamBuildSender(); @@ -30,8 +31,7 @@ public: int SendDataToServer(CasMsgType type, const void *buf, size_t len); private: - CasSocket *m_socket = nullptr; - NetTrans *m_mtrans = nullptr; + std::shared_ptr m_casTransmission = nullptr; }; #endif // CLOUDAPPSDK_CASSTREAMBUILDSENDER_H diff --git a/cloudphone/src/main/cpp/cas_stream/CasStreamRecvParser.cpp b/cloudphone/src/main/cpp/cas_stream/CasStreamRecvParser.cpp index a47fd2833cdafd5ccc6f976b6b4d62cb3cbe0bb5..85ac47f8c803941bbd9e1e6cff88c8ecaf837e9b 100644 --- a/cloudphone/src/main/cpp/cas_stream/CasStreamRecvParser.cpp +++ b/cloudphone/src/main/cpp/cas_stream/CasStreamRecvParser.cpp @@ -34,28 +34,6 @@ using namespace std; const int STREAM_MAX_BUF_LEN = 32 * 1024 * 1024; -CasStreamRecvParser *CasStreamRecvParser::g_instance = nullptr; - -CasStreamRecvParser *CasStreamRecvParser::GetInstance() -{ - if (g_instance == nullptr) { - g_instance = new (std::nothrow) CasStreamRecvParser(); - if (g_instance == nullptr) { - ERR("Failed to new stream recv parser."); - return nullptr; - } - } - return g_instance; -} - -void CasStreamRecvParser::DestroyInstance() -{ - if (g_instance != nullptr) { - delete g_instance; - g_instance = nullptr; - } -} - CasStreamRecvParser::CasStreamRecvParser() { m_serviceHandles.fill(nullptr); @@ -91,26 +69,6 @@ CasStreamParseThread::~CasStreamParseThread() this->m_streamRecvParser = nullptr; } -int FastDecodeFrame(uint8_t *buf, size_t length) -{ - CasController *casController = CasController::GetInstance(); - if (casController == nullptr) { - ERR("Get CasController failed."); - return VIDEO_ENGINE_CLIENT_DESTROY_ERR; - } - CasVideoHDecodeThread *thread = casController->GetVideoDecodeThread(); - if (thread == nullptr) { - ERR("Get CasVideoHDecodeThread failed."); - return VIDEO_ENGINE_CLIENT_DESTROY_ERR; - } - CasVideoEngine *engine = thread->GetVideoEngine(); - if (engine == nullptr) { - ERR("Get CasVideoEngine failed."); - return VIDEO_ENGINE_CLIENT_DESTROY_ERR; - } - return engine->DecodeFrame(buf, length); -} - void HandleCompletePktMsg(CasStreamParseThread *streamParseThread, streamMsgHead *msgHead, unsigned char *recvBuf, unsigned int pktStartPos) { @@ -120,19 +78,6 @@ void HandleCompletePktMsg(CasStreamParseThread *streamParseThread, streamMsgHead unsigned int dataCompleteLen = msgHead->GetPayloadSize() + sizeof(stream_msg_head_t); CasPktHandle *serviceHandle = streamParseThread->m_streamRecvParser->GetServiceHandle(msgHead->type); if (serviceHandle) { - // 直接喂数据给解码控制器逻辑,处理缓存队列解码和直接解码并发问题 - if (CasController::GetInstance()->IsMtransValid() && msgHead->type == Video) { - CasDataPipe *videoCasDataPipe = (CasDataPipe *)serviceHandle; - if (videoCasDataPipe->m_unprocessedSize == 0) { - // Video Data pipe 无数据,就转为直接提交MediaCodec - int32_t dataSize = static_cast(((streamMsgHead *)recvBuf)->GetPayloadSize()); - int ret = FastDecodeFrame((uint8_t *)recvBuf + pktStartPos + sizeof(streamParseThread), dataSize); - if (ret == SUCCESS) { - return; - } - } - } - void *pTmp = AllocBuffer(dataCompleteLen); if (pTmp != nullptr) { if (EOK != memcpy_s(pTmp, dataCompleteLen, recvBuf + pktStartPos, dataCompleteLen)) { diff --git a/cloudphone/src/main/cpp/cas_stream/CasStreamRecvParser.h b/cloudphone/src/main/cpp/cas_stream/CasStreamRecvParser.h index a297140b081d5f86eb42c9953ae40abf8f49d058..2959f5fef1ee659d636cf593fc538e714b4a4ed6 100644 --- a/cloudphone/src/main/cpp/cas_stream/CasStreamRecvParser.h +++ b/cloudphone/src/main/cpp/cas_stream/CasStreamRecvParser.h @@ -25,18 +25,16 @@ class CasStreamRecvParser { public: - static CasStreamRecvParser *GetInstance(); + CasStreamRecvParser(); - static void DestroyInstance(); + ~CasStreamRecvParser(); CasPktHandle *GetServiceHandle(unsigned char type); void SetServiceHandle(unsigned char type, CasPktHandle *serviceHandle); private: - CasStreamRecvParser(); - ~CasStreamRecvParser(); static CasStreamRecvParser *g_instance; std::array m_serviceHandles; diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/api/CloudAppScreenshotListener.java b/cloudphone/src/main/java/com/huawei/cloudphone/api/CloudAppScreenshotListener.java new file mode 100644 index 0000000000000000000000000000000000000000..7a4acb3381f9f522ac7f580e0c3e6301b213585b --- /dev/null +++ b/cloudphone/src/main/java/com/huawei/cloudphone/api/CloudAppScreenshotListener.java @@ -0,0 +1,19 @@ +package com.huawei.cloudphone.api; + +public interface CloudAppScreenshotListener { + + /** + * 截图图片回调 + * + * @param picture 图片数组 + */ + void onRecvPicture(byte[] picture); + + /** + * 运行状态回调 + * + * @param state 状态码 + * @param msg 状态描述信息 + */ + void onNotify(int state, String msg); +} diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/api/CloudPhoneManager.java b/cloudphone/src/main/java/com/huawei/cloudphone/api/CloudPhoneManager.java index e5c43cb9ca278e3199bd94150570a33b65830517..5dd26b1b167d15d2c5e4b6018fbd0cef8c301fde 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/api/CloudPhoneManager.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/api/CloudPhoneManager.java @@ -16,19 +16,13 @@ package com.huawei.cloudphone.api; +import android.content.Context; + import com.huawei.cloudphone.apiimpl.CloudPhoneImpl; public class CloudPhoneManager { - static volatile private ICloudPhone cloudPhoneObj = null; public static ICloudPhone createCloudPhoneInstance() { - if (cloudPhoneObj == null) { - synchronized (CloudPhoneManager.class) { - if (cloudPhoneObj == null) { - cloudPhoneObj = new CloudPhoneImpl(); - } - } - } - return cloudPhoneObj; + return new CloudPhoneImpl(); } } diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/api/ICloudPhoneScreenshot.java b/cloudphone/src/main/java/com/huawei/cloudphone/api/ICloudPhoneScreenshot.java new file mode 100644 index 0000000000000000000000000000000000000000..77232196a793bb8f3e748c5e4f040b67cb91a992 --- /dev/null +++ b/cloudphone/src/main/java/com/huawei/cloudphone/api/ICloudPhoneScreenshot.java @@ -0,0 +1,28 @@ +package com.huawei.cloudphone.api; + +import android.view.MotionEvent; + +import java.util.HashMap; +import java.util.Map; + +public interface ICloudPhoneScreenshot { + /** + * 设置截图的宽高 + */ + void setMediaConfig(final HashMap params); + + /** + * 开始截图 + */ + boolean startScreenshot(final Map params, CloudAppScreenshotListener captureListener); + + /** + * 停止截图 + */ + void stopScreenshot(); + + /** + * 发送触控 + */ + boolean sendTouchEvent(MotionEvent event, int width, int height, int orientation); +} diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImpl.java b/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImpl.java index 51a20670fa8ce6f12f4b9f03d390f4c1df78f7fc..7b3167bbaaa73ff177033dc10954c5a9dacf0d35 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImpl.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImpl.java @@ -28,6 +28,7 @@ import android.app.Activity; import android.content.Context; import android.content.pm.ActivityInfo; import android.graphics.PixelFormat; +import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; @@ -37,7 +38,6 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; -import android.view.VelocityTracker; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; @@ -59,6 +59,7 @@ import com.huawei.cloudphone.common.CasParcelableMap; import com.huawei.cloudphone.common.CasState; import com.huawei.cloudphone.jniwrapper.JNIWrapper; import com.huawei.cloudphone.service.CasProcessor; +import com.huawei.cloudphone.service.CasProcessorListener; import com.huawei.cloudphone.virtualdevice.VirtualDeviceSession; import com.huawei.cloudphone.virtualdevice.camera.VirtualCameraManager; import com.huawei.cloudphone.virtualdevice.common.RingBufferVirtualDeviceIO; @@ -66,6 +67,7 @@ import com.huawei.cloudphone.virtualdevice.common.VirtualDeviceManager; import com.huawei.cloudphone.virtualdevice.common.VirtualDeviceProtocol; import com.huawei.cloudphone.virtualdevice.sensor.VirtualSensorManager; +import java.io.File; import java.security.SecureRandom; import java.util.HashMap; import java.util.Map; @@ -96,7 +98,7 @@ public class CloudPhoneImpl implements ICloudPhone { private static final byte CUSTOM_DATA = 18; private static final byte IME_DATA =14; public static final int RECONNECT_RETRY_MAX_COUNT = 10; - public static final int SEND_IME_DATA_RETRY_MAX_COUNT = 100; // 300ms*100,与重连总时长保持一致 + public static final int SEND_IME_DATA_RETRY_MAX_COUNT = 10; public static final int RECONNECT_INTERVAL_TIME = 2; // 间隔2s,实际执行为3s private final Object mCloudPhoneLock = new Object(); @@ -131,7 +133,6 @@ public class CloudPhoneImpl implements ICloudPhone { private boolean mReconnecting = false; private int mBgTimeout = 60 * 1000; private int mTouchCount = 0; - private VelocityTracker mVelocityTracker = VelocityTracker.obtain(); private Toast toast = null; private int surfaceId = -1; // 避免快速连接时新的窗口已建立,旧的窗口执行销毁操作发送pause @@ -140,8 +141,8 @@ public class CloudPhoneImpl implements ICloudPhone { private CloudPhoneImeMgr mImeMgr; //virtual devices - VirtualDeviceSession mVirtualDeviceSession = null; - RingBufferVirtualDeviceIO mRingBufferVirtualDeviceIO = null; + private VirtualDeviceSession mVirtualDeviceSession = null; + private RingBufferVirtualDeviceIO mRingBufferVirtualDeviceIO = null; public CloudPhoneImpl() { mCurrentState = STATE_DEINIT; @@ -160,11 +161,16 @@ public class CloudPhoneImpl implements ICloudPhone { throw new IllegalStateException("Not ready for init."); } + File externalDir = context.getExternalFilesDir(null); + String packageName = context.getPackageName(); + String externalPath = externalDir.getAbsolutePath(); + String logPath = externalPath + packageName + "/"; + mProccessor = new CasProcessor(); mListener = new CASListener(); mContext = context; - mProccessor.init(); + mProccessor.init(logPath); mProccessor.registerListener(mListener); initVirtualDeviceSession(); @@ -188,7 +194,9 @@ public class CloudPhoneImpl implements ICloudPhone { CASLog.i(TAG, "deinit called"); mCmdHandlerThread.quit(); mCmdHandlerThread.join(); + mProccessor.stop(false); mProccessor.registerListener(null); + mProccessor.deinit(); mCmdHandlerThread = null; mCurrentState = STATE_DEINIT; mProccessor = null; @@ -399,7 +407,7 @@ public class CloudPhoneImpl implements ICloudPhone { } CASLog.i(TAG, "handleStartCmd start"); mCurrentState = STATE_STARTING; - //创建SurfaceView + // 创建SurfaceView if (mDisplayMode == DISPLAY_MODE_FILL) { mSurfaceView = new PhoneView(activity, true); } else { @@ -460,9 +468,6 @@ public class CloudPhoneImpl implements ICloudPhone { synchronized (mCloudPhoneLock) { try { CASLog.i(TAG, "surfaceDestroyed, surfaceId:" + surfaceId); - if (mProccessor != null && mProccessor.getState() != JNIState.JNI_STOPPED) { - mProccessor.stop(true); - } if (mCmdHandler != null) { Message msg = new Message(); msg.what = CMD_SURFACE_DESTROY; @@ -596,8 +601,7 @@ public class CloudPhoneImpl implements ICloudPhone { mCurrentState = STATE_START; } else if (mCurrentState == STATE_PAUSE) { mProccessor.setSurface(mSurfaceView.getHolder().getSurface()); - mProccessor.startJniRecv(); - mProccessor.start(true); + mProccessor.resume(); mCurrentState = STATE_START; if (mBackGroundTimer != null) { mBackGroundTimer.cancel(); @@ -758,25 +762,22 @@ public class CloudPhoneImpl implements ICloudPhone { mProccessor.stopJniRecv(); long currentTs = System.currentTimeMillis(); if (mProccessor.reconnect()) { - int status = mProccessor.getState(); - if (status == JNIState.JNI_CONNECTED) { - mProccessor.startJniRecv(); - mAutoReconnectCount = 0; - mAutoReconnectTimer = null; - mReconnecting = false; + mProccessor.startJniRecv(); + mAutoReconnectCount = 0; + mAutoReconnectTimer = null; + mReconnecting = false; - if (System.currentTimeMillis() - currentTs < 1000) { - try { - // 重连时长不足1s等待1s,防止只出现重连成功的提示 - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } + if (System.currentTimeMillis() - currentTs < 1000) { + try { + // 重连时长不足1s等待1s,防止只出现重连成功的提示 + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); } - toast.cancel(); - Toast.makeText(mContext, "Reconnect success!", Toast.LENGTH_SHORT).show(); - return; } + toast.cancel(); + Toast.makeText(mContext, "Reconnect success!", Toast.LENGTH_SHORT).show(); + return; } CASLog.i(TAG, "reconnect failed"); mAutoReconnectCount++; @@ -846,7 +847,7 @@ public class CloudPhoneImpl implements ICloudPhone { private void initVirtualDeviceSession() { mVirtualDeviceSession = new VirtualDeviceSession(mContext); - mRingBufferVirtualDeviceIO = new RingBufferVirtualDeviceIO(); + mRingBufferVirtualDeviceIO = new RingBufferVirtualDeviceIO(this); mVirtualDeviceSession.setVirtualDeviceIoHook(mRingBufferVirtualDeviceIO); if (mPermissionListener != null) { mVirtualDeviceSession.setPermissionListener(mPermissionListener); @@ -920,7 +921,8 @@ public class CloudPhoneImpl implements ICloudPhone { * listener *

receive jni notify msg

*/ - public class CASListener { + public class CASListener implements CasProcessorListener { + @Override public void onRotationDirectionChange(int orientation) throws RemoteException { synchronized (this) { if (mOrientationChangeListener != null) { @@ -929,6 +931,7 @@ public class CloudPhoneImpl implements ICloudPhone { } } + @Override public void onCmdRecv(int code, String describe) throws RemoteException { CASLog.i(TAG, "code = " + code + " msg = " + describe); Message msg = new Message(); @@ -940,23 +943,30 @@ public class CloudPhoneImpl implements ICloudPhone { } } + @Override public void onChannelDataRecv(byte[] data) throws RemoteException { if (mChannelDataListener != null) { mChannelDataListener.onRecvCloudAppData(data); } } + @Override public void onVirtualDevDataRecv(byte[] data) throws RemoteException { if (mVirtualDevDataListener != null) { mVirtualDevDataListener.onRecvVirtualDevData(data, data.length); } } + @Override public void onImeMsgRecv(byte[] data) throws RemoteException { if (mImeMgr != null) { mImeMgr.processImeMsg(data); } } + + @Override + public void onScreenCaptureRecv(byte[] data) throws RemoteException { + } } private class BackgroundTimerTask extends TimerTask { @@ -1026,13 +1036,13 @@ public class CloudPhoneImpl implements ICloudPhone { if (mCurrentState != STATE_START) { return; } - while (!mProccessor.isConnect() || mProccessor.getState() != JNIState.JNI_CONNECTED) { + while (!mProccessor.isConnect() || mProccessor.getState() != JNIState.JNI_START) { if (tryCount >= SEND_IME_DATA_RETRY_MAX_COUNT) { CASLog.e(TAG, "onTextChange retry failed. processor connect:" + mProccessor.isConnect() + ", processor state:" + mProccessor.getState()); return; } try { - Thread.sleep(300); + Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneScreenshotImpl.java b/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneScreenshotImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..8021b232c1dd792d1eee13ede353bdce7f835964 --- /dev/null +++ b/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneScreenshotImpl.java @@ -0,0 +1,242 @@ +package com.huawei.cloudphone.apiimpl; + +import android.content.pm.ActivityInfo; +import android.os.RemoteException; +import android.view.MotionEvent; + +import com.huawei.cloudphone.api.CloudAppScreenshotListener; +import com.huawei.cloudphone.api.ICloudPhoneScreenshot; +import com.huawei.cloudphone.common.CASLog; +import com.huawei.cloudphone.common.CasConnectorInfo; +import com.huawei.cloudphone.common.CasParcelableMap; +import com.huawei.cloudphone.common.CasState; +import com.huawei.cloudphone.service.CasProcessor; +import com.huawei.cloudphone.service.CasProcessorListener; + +import java.util.HashMap; +import java.util.Map; + +public class CloudPhoneScreenshotImpl implements ICloudPhoneScreenshot { + private static final String TAG = "CloudPhoneScreenshotImpl"; + private HashMap mMediaConfig = null; + private CasProcessor mProccessor = null; + private CloudAppScreenshotListener mListener = null; + private boolean mCheckConnection = false; + private CheckConntionThread mCheckConntionThread; + private Object mLock = new Object(); + private boolean mIsStart = false; + + @Override + public void setMediaConfig(final HashMap config) { + mMediaConfig = config; + } + + @Override + public boolean startScreenshot(Map params, CloudAppScreenshotListener captureListener) { + synchronized (mLock) { + if (mIsStart) { + return false; + } + mListener = captureListener; + CasParcelableMap parcelableMap = new CasParcelableMap(); + parcelableMap.setParcelableMap(mMediaConfig); + + mProccessor = new CasProcessor(); + mProccessor.init(null); + mProccessor.registerListener(new CASListener()); + mProccessor.setMediaConfig(parcelableMap); + + CasConnectorInfo connectorInfo = new CasConnectorInfo(); + if (!connectorInfo.initConnectorParams(params)) { + CASLog.e(TAG, "Init connector params failed."); + mProccessor = null; + return false; + } + mProccessor.setCasConnectorInfo(connectorInfo); + mProccessor.startJniRecv(); + if (!mProccessor.startCapture()) { + CASLog.e(TAG, "Start screenshot failed."); + mProccessor = null; + return false; + } + mCheckConnection = true; + mCheckConntionThread = new CheckConntionThread(); + mCheckConntionThread.start(); + mIsStart = true; + CASLog.i(TAG, "Start screenshot success."); + return true; + } + } + + @Override + public void stopScreenshot() { + synchronized (mLock) { + if (!mIsStart) { + return; + } + if (mCheckConntionThread != null) { + mCheckConnection = false; + mCheckConntionThread.interrupt(); + mCheckConntionThread = null; + } + if (mProccessor != null) { + mProccessor.stopCapture(); + mProccessor.deinit(); + mProccessor = null; + } + mIsStart = false; + } + } + + @Override + public boolean sendTouchEvent(MotionEvent event, int displayWidth, int displayHeight, int orientation) { + int id; + int rawX; + int rawY; + int index; + boolean result = false; + int action = event.getActionMasked(); + switch (action) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + case MotionEvent.ACTION_POINTER_UP: + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + action = event.getActionMasked(); + index = event.getActionIndex(); + id = event.getPointerId(index); + rawX = (int) event.getX(index); + rawY = (int) event.getY(index); + result = sendTouchEvent(id, action, rawX, rawY, displayWidth, displayHeight, orientation); + break; + case MotionEvent.ACTION_MOVE: + final int historySize = event.getHistorySize(); + final int pointerCount = event.getPointerCount(); + for (int i = 0; i < historySize; i++) { + for (int j = 0; j < pointerCount; j++) { + id = event.getPointerId(j); + rawX = (int) event.getHistoricalX(j, i); + rawY = (int) event.getHistoricalY(j, i); + result = sendTouchEvent(id, action, rawX, rawY, displayWidth, displayHeight, orientation); + } + } + for (int i = 0; i < pointerCount; i++) { + id = event.getPointerId(i); + rawX = (int) event.getX(i); + rawY = (int) event.getY(i); + result = sendTouchEvent(id, action, rawX, rawY, displayWidth, displayHeight, orientation); + } + break; + default: + CASLog.e(TAG, "unknown action"); + break; + } + return result; + } + + private boolean sendTouchEvent(final int id, final int action, final int x1, final int y1, + final int displayWidth, final int displayHeight, final int orientation) { + int newOrientation; + switch (orientation) { + case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT: + case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT: + newOrientation = 0; + break; + case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE: + case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE: + newOrientation = 1; + break; + case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT: + newOrientation = 2; + break; + case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE: + newOrientation = 3; + break; + default: + CASLog.i(TAG, "invalid directation " + orientation); + return false; + } + + int x = x1, y = y1; + if (newOrientation == 1 || newOrientation == 3) { + x = x1 * displayWidth / displayHeight; + y = y1 * displayHeight / displayWidth; + } + synchronized (mLock) { + if (mProccessor != null) { + return mProccessor.sendTouchEvent(id, action, x, y, 0, -1, + newOrientation, displayHeight, displayWidth); + } + } + return false; + } + + private class CASListener implements CasProcessorListener { + + @Override + public void onRotationDirectionChange(int orientation) throws RemoteException { + } + + @Override + public void onCmdRecv(int code, String describe) throws RemoteException { + if (mListener != null) { + mListener.onNotify(code, describe); + } + } + + @Override + public void onChannelDataRecv(byte[] data) throws RemoteException { + } + + @Override + public void onVirtualDevDataRecv(byte[] data) throws RemoteException { + } + + @Override + public void onImeMsgRecv(byte[] data) throws RemoteException { + } + + @Override + public void onScreenCaptureRecv(byte[] data) throws RemoteException { + if (mListener != null) { + mListener.onRecvPicture(data); + } + } + } + + private class CheckConntionThread extends Thread { + @Override + public void run() { + final int MAX_RECONNECT_TIMES = 3; + int autoConnectTimes = 0; + while (mCheckConnection) { + boolean isConnected = mProccessor.isConnect(); + if (!isConnected) { + if (mProccessor.reconnect()) { + autoConnectTimes++; + if (autoConnectTimes > MAX_RECONNECT_TIMES && mListener != null) { + mListener.onNotify(CasState.CAS_CONNECT_LOST, "Connect lost"); + } + } + } else { + autoConnectTimes = 0; + } + + int sleepTimes; // 100毫秒 + if (autoConnectTimes > 0) { + sleepTimes = 2000; // 2秒 + } else { + sleepTimes = 100; // 100毫秒 + } + + try { + if (!Thread.interrupted()) { + Thread.sleep(sleepTimes); + } + } catch (InterruptedException e) { + CASLog.e(TAG, "thread interrupted. " + e.getMessage()); + } + } + } + } +} diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/audio/AudioTrackerCallback.java b/cloudphone/src/main/java/com/huawei/cloudphone/audio/AudioTrackerCallback.java index 76013a2627b8a3febdc635e8db8c05bc92e588cb..2c0034e912af94ffac56b2fe90a6996df8dca8f7 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/audio/AudioTrackerCallback.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/audio/AudioTrackerCallback.java @@ -23,7 +23,6 @@ import android.util.SparseArray; import com.huawei.cloudphone.common.CASLog; import com.huawei.cloudphone.common.CasRemoteMessage; -import com.huawei.cloudphone.datacenter.NewPacketCallback; import com.huawei.cloudphone.jniwrapper.OpusJNIWrapper; import java.util.ArrayList; @@ -32,7 +31,7 @@ import java.util.Arrays; /** * AudioTrackerCallback */ -public class AudioTrackerCallback implements NewPacketCallback { +public class AudioTrackerCallback { private static final String TAG = "CASAudioTrackerCallback"; private SparseArray sTrackMap = new SparseArray<>(32); @@ -47,7 +46,6 @@ public class AudioTrackerCallback implements NewPacketCallback { sTrackMap.clear(); } - @Override public void onNewPacket(byte[] data) { // put the message to its thread's message queue. short[] music = (!isBigEnd()) ? byteArray2ShortArrayLittle(data, data.length / 2) : @@ -157,7 +155,7 @@ public class AudioTrackerCallback implements NewPacketCallback { public void onReceiveMsg(CasRemoteMessage msg) { handleSet(msg); - handleStart(msg); + //handleStart(msg); handleWrite(msg); } @@ -197,6 +195,7 @@ public class AudioTrackerCallback implements NewPacketCallback { } catch (IllegalArgumentException e) { CASLog.e(TAG, "failed to new audioTrackPlayer"); } + mTrackPlayer.play(); } private void handleStart(CasRemoteMessage msg) { diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/datacenter/CasRecvPktDispatcher.java b/cloudphone/src/main/java/com/huawei/cloudphone/datacenter/CasRecvPktDispatcher.java deleted file mode 100644 index 1120b55d6e2ac93607dba7a64a86eb04505da973..0000000000000000000000000000000000000000 --- a/cloudphone/src/main/java/com/huawei/cloudphone/datacenter/CasRecvPktDispatcher.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.huawei.cloudphone.datacenter; - -import com.huawei.cloudphone.common.CASLog; -import com.huawei.cloudphone.jniwrapper.JNIWrapper; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -public class CasRecvPktDispatcher { - private static final int MAX_BUF_LEN = 1048576; // 1MB - private static final String TAG = "CasRecvPktDispatcher"; - private static Map newPacketCallback = new ConcurrentHashMap<>(); - private volatile boolean stopFlag = false; - private volatile boolean stopped = false; - - public void addNewPacketCallback(Byte tag, NewPacketCallback callback) { - CASLog.e(TAG, "callback added " + tag); - newPacketCallback.put(tag, callback); - } - - public void deleteNewPacketCallback(Byte tag) { - CASLog.e(TAG, "callback removed " + tag); - newPacketCallback.remove(tag); - } - - public void stopBlocked() { - stopFlag = true; - while (!stopped) { - try { - Thread.sleep(10); - } catch (InterruptedException e) { - CASLog.i(TAG, "sleep interrupted"); - } - } - } - - public void start() { - NewPacketCallback callback = newPacketCallback.get(JNIWrapper.AUDIO); - if (callback != null) { - new Thread(new ConsumerThread(JNIWrapper.AUDIO, callback)) - .start(); - } - - callback = newPacketCallback.get(JNIWrapper.RECORDER); - if (callback != null) { - new Thread(new ConsumerThread(JNIWrapper.RECORDER, callback)) - .start(); - } - - new Thread(new Runnable() { - @Override - public void run() { - byte[] recvBuf = new byte[MAX_BUF_LEN]; - - while (!stopFlag) { - for (Map.Entry entry : newPacketCallback.entrySet()) { - // AudioTrack and AudioRecord has some issue in Android 8.0, pick audio out. - if (entry.getKey().equals(JNIWrapper.AUDIO) || entry.getKey().equals(JNIWrapper.RECORDER)) { - continue; - } - - int packetLen = JNIWrapper.recvData(entry.getKey(), recvBuf, recvBuf.length); - if (packetLen <= 0) { - continue; - } - - byte[] copyData = new byte[packetLen]; - System.arraycopy(recvBuf, 0, copyData, 0, packetLen); - entry.getValue().onNewPacket(copyData); - } - - try { - Thread.sleep(10); - } catch (InterruptedException e) { - CASLog.i(TAG, "sleep interrupted, it's OK"); - } - } - - stopped = true; - } - }).start(); - } - - class ConsumerThread implements Runnable { - NewPacketCallback mCallback; - Byte mType; - - ConsumerThread(Byte datatype, NewPacketCallback callback) { - mCallback = callback; - mType = datatype; - } - - @Override - public void run() { - byte[] recvBuf = new byte[MAX_BUF_LEN]; - - while (!stopFlag) { - int packetLen = JNIWrapper.recvData(mType, recvBuf, recvBuf.length); - if (packetLen <= 0) { - try { - Thread.sleep(3); - } catch (InterruptedException e) { - CASLog.e(TAG, "sleep interrupted."); - } - continue; - } - - byte[] copyData = new byte[packetLen]; - System.arraycopy(recvBuf, 0, copyData, 0, packetLen); - mCallback.onNewPacket(copyData); - } - } - } -} diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JNIWrapper.java b/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JNIWrapper.java index 33787f1f3e24d59e78ba14764f59a67402ee26be..c26eda23d384f77eadb5d3576bc75bfe093c028c 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JNIWrapper.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JNIWrapper.java @@ -17,7 +17,6 @@ package com.huawei.cloudphone.jniwrapper; import android.view.Surface; - import java.util.HashMap; public class JNIWrapper { @@ -45,7 +44,7 @@ public class JNIWrapper { public static final byte TOUCH_INPUT = 6; public static final byte CONTROL = 7; public static final byte ORIENTATION = 9; - + public static final byte SCREENSHOT = 10; public static final byte RECORDER = 11; public static final byte IMEDATA = 14; @@ -60,42 +59,62 @@ public class JNIWrapper { public static final byte SENSOR_DATA = 23; public static final byte LOCATION_DATA = 24; + private long mNativeObject; + static { System.loadLibrary("cloudapp"); } - public static native int recvData(byte type, byte[] data, int length); + public JNIWrapper(String logPath) { + mNativeObject = init(logPath); + } + + public long getNativeObj() { + return mNativeObject; + } + + public native long init(String logPth); + + public native void deinit(long nativeObject); + + public native int sendData(long nativeObject, byte type, byte[] data, int length); + + public native boolean sendTouchEvent(long nativeObject, final int id, final int action, final int x1, final int y1, final int pressure, long time, int orientation, int height, int width); + + public native boolean sendKeyEvent(long nativeObject, final int keycode, final int action); + + public native boolean sendMotionEvent(long nativeObject, final int masterAxis, final int masterValue, final int secondaryAxis, final int secondaryValue); - public static native int sendData(byte type, byte[] data, int length); + public native void setJniConf(long nativeObject, String key, String value); - public static native boolean sendTouchEvent(final int id, final int action, final int x1, final int y1, final int pressure, long time, int orientation, int height, int width); + public native boolean start(long nativeObject, Surface surface, boolean isHome); - public static native boolean sendKeyEvent(final int keycode, final int action); + public native void stop(long nativeObject, boolean isHome); - public static native boolean sendMotionEvent(final int masterAxis, final int masterValue, final int secondaryAxis, final int secondaryValue); + public native boolean pause(long nativeObject); - public static native void setJniConf(String key, String value); + public native boolean resume(long nativeObject, Surface surface); - public static native boolean start(Surface surface, boolean isHome); + public native boolean startScreenshot(long nativeObject); - public static native void stop(boolean isHome); + public native boolean stopScreenshot(long nativeObject); - public static native boolean reconnect(); + public native boolean reconnect(long nativeObject); - public static native int getJniStatus(); + public native int getJniStatus(long nativeObject); - public static native boolean getConnectStatus(); + public native boolean getConnectStatus(long nativeObject); - public static native void registerCasJNICallback(Object obj); + public native void registerCasJNICallback(long nativeObject, Object obj); - public static native int getLag(); + public native int getLag(long nativeObject); - public static native String getVideoStreamStats(); + public native String getVideoStreamStats(long nativeObject); - public static native String getSimpleStreamStats(); + public native String getSimpleStreamStats(long nativeObject); - public static native boolean setMediaConfig(HashMap mediaConfigMap); + public native boolean setMediaConfig(long nativeObject, HashMap mediaConfigMap); - public static native boolean setRotation(int rotation); + public native boolean setRotation(long nativeObject, int rotation); } \ No newline at end of file diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JniBridge.java b/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JniBridge.java index 7c0ee2e6eaa987723383a55ee35d52b287e2c6c8..22377f69b2430d83de1eecfccda0033d98ac9382 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JniBridge.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JniBridge.java @@ -21,91 +21,163 @@ import android.view.Surface; import java.util.HashMap; public class JniBridge { - static JniBridge g_jniBridge = null; + private JNIWrapper mJniWrapper; + private long mNativeObject; + private final String mLogPath; - public static synchronized JniBridge getInstance() { - if (g_jniBridge == null) { - g_jniBridge = new JniBridge(); - } - return g_jniBridge; + public JniBridge(String logPath) { + mLogPath = logPath; + mJniWrapper = null; + mNativeObject = 0; } - private JniBridge() { + public boolean init() { + mJniWrapper = new JNIWrapper(mLogPath); + mNativeObject = mJniWrapper.getNativeObj(); + return true; } - public int sendData(byte type, byte[] data, int length) { - return JNIWrapper.sendData(type, data, length); + public void deinit() { + if (mNativeObject != 0) { + mJniWrapper.deinit(mNativeObject); + mNativeObject = 0; + } } - public int recvData(byte type, byte[] data, int length) { - return JNIWrapper.recvData(type, data, length); + public int sendData(byte type, byte[] data, int length) { + if (mNativeObject == 0) { + return 0; + } + return mJniWrapper.sendData(mNativeObject, type, data, length); } - public boolean sendTouchEvent(final int id, final int action, final int x1, final int y1, final int pressure, long time, int orientation, int height, int width) { - return JNIWrapper.sendTouchEvent(id, action, x1, y1, pressure, time, orientation, height, width); + public boolean sendTouchEvent(final int id, final int action, final int x1, final int y1, + final int pressure, long time, int orientation, int height, int width) { + if (mNativeObject == 0) { + return false; + } + return mJniWrapper.sendTouchEvent(mNativeObject, id, action, x1, y1, pressure, time, orientation, height, width); } public boolean sendKeyEvent(final int keycode, final int action) { - return JNIWrapper.sendKeyEvent(keycode, action); + if (mNativeObject == 0) { + return false; + } + return mJniWrapper.sendKeyEvent(mNativeObject, keycode, action); } public boolean sendMotionEvent(final int masterAxis, final int masterValue, final int secondaryAxis, final int secondaryValue) { - return JNIWrapper.sendMotionEvent(masterAxis, masterValue, secondaryAxis, secondaryValue); + if (mNativeObject == 0) { + return false; + } + return mJniWrapper.sendMotionEvent(mNativeObject, masterAxis, masterValue, secondaryAxis, secondaryValue); } public void setJniConf(String key, String value) { - JNIWrapper.setJniConf(key, value); + if (mNativeObject != 0) { + mJniWrapper.setJniConf(mNativeObject, key, value); + } } public boolean start(Surface surface, boolean isHome) { - return JNIWrapper.start(surface, isHome); + if (mNativeObject == 0) { + return false; + } + return mJniWrapper.start(mNativeObject, surface, isHome); } public void stop(boolean isHome) { - JNIWrapper.stop(isHome); + if (mNativeObject != 0) { + mJniWrapper.stop(mNativeObject, isHome); + } + } + + public boolean pause() { + if (mNativeObject == 0) { + return false; + } + return mJniWrapper.pause(mNativeObject); + } + + public boolean resume(Surface surface) { + if (mNativeObject == 0) { + return false; + } + return mJniWrapper.resume(mNativeObject, surface); + } + + public boolean startScreenshot() { + if (mNativeObject == 0) { + return false; + } + return mJniWrapper.startScreenshot(mNativeObject); + } + + public void stopScreenshot() { + if (mNativeObject != 0) { + mJniWrapper.stopScreenshot(mNativeObject); + } } public boolean reconnect() { - return JNIWrapper.reconnect(); + if (mNativeObject == 0) { + return false; + } + return mJniWrapper.reconnect(mNativeObject); } public int getJniStatus() { - return JNIWrapper.getJniStatus(); + if (mNativeObject == 0) { + return -1; + } + return mJniWrapper.getJniStatus(mNativeObject); } public boolean getConnectStatus() { - return JNIWrapper.getConnectStatus(); + if (mNativeObject == 0) { + return false; + } + return mJniWrapper.getConnectStatus(mNativeObject); } - public void registerCasJNICallback(Object obj) { - JNIWrapper.registerCasJNICallback(obj); + public void registerCasJNICallback(Object callback) { + if (mNativeObject != 0) { + mJniWrapper.registerCasJNICallback(mNativeObject, callback); + } } public int getLag() { - return JNIWrapper.getLag(); + if (mNativeObject != 0) { + return -1; + } + return mJniWrapper.getLag(mNativeObject); } public String getVideoStreamStats() { - return JNIWrapper.getVideoStreamStats(); + if (mNativeObject == 0) { + return ""; + } + return mJniWrapper.getVideoStreamStats(mNativeObject); } public String getSimpleStreamStats() { - return JNIWrapper.getSimpleStreamStats(); + if (mNativeObject == 0) { + return ""; + } + return mJniWrapper.getSimpleStreamStats(mNativeObject); } - public boolean setMediaConfig(HashMap mediaConfigMap) { - if (mediaConfigMap != null) { - return JNIWrapper.setMediaConfig(mediaConfigMap); - } else { + if (mNativeObject == 0) { return false; } + return mJniWrapper.setMediaConfig(mNativeObject, mediaConfigMap); } public boolean setRotation(int rotation) { - return JNIWrapper.setRotation(rotation); + if (mNativeObject == 0) { + return false; + } + return mJniWrapper.setRotation(mNativeObject, rotation); } - - - } diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/datacenter/NewPacketCallback.java b/cloudphone/src/main/java/com/huawei/cloudphone/service/CasCallback.java similarity index 64% rename from cloudphone/src/main/java/com/huawei/cloudphone/datacenter/NewPacketCallback.java rename to cloudphone/src/main/java/com/huawei/cloudphone/service/CasCallback.java index c640d8dbc0e6385f98d09b35a4de4f2ab6d330dc..bdbd08e9fd69ac5118f431242416e30786c2e374 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/datacenter/NewPacketCallback.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/service/CasCallback.java @@ -14,8 +14,21 @@ * limitations under the License. */ -package com.huawei.cloudphone.datacenter; +package com.huawei.cloudphone.service; -public interface NewPacketCallback { - void onNewPacket(byte[] data); +import android.os.RemoteException; + +/** + * CasCallback + */ +public interface CasCallback { + /** + * on cmd receive + */ + void onCmdReceive(int code, String msg); + + /** + * on message receive + */ + void onMessageReceive(int type, byte[] data, int length) throws RemoteException; } diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/service/CasProcessor.java b/cloudphone/src/main/java/com/huawei/cloudphone/service/CasProcessor.java index 1967f41a4b82de4760efdfc019166531d49e8eb2..1d92ed0731c6019253eb72aa045916bdd7ff140a 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/service/CasProcessor.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/service/CasProcessor.java @@ -16,18 +16,29 @@ package com.huawei.cloudphone.service; +import static com.huawei.cloudphone.jniwrapper.JNIWrapper.KEY_AES_IV; +import static com.huawei.cloudphone.jniwrapper.JNIWrapper.KEY_AUTH_TS; +import static com.huawei.cloudphone.jniwrapper.JNIWrapper.KEY_BACKGROUND_TIMEOUT; +import static com.huawei.cloudphone.jniwrapper.JNIWrapper.KEY_CLIENT_MODE; +import static com.huawei.cloudphone.jniwrapper.JNIWrapper.KEY_ENCRYPTED_DATA; +import static com.huawei.cloudphone.jniwrapper.JNIWrapper.KEY_IP; +import static com.huawei.cloudphone.jniwrapper.JNIWrapper.KEY_PORT; +import static com.huawei.cloudphone.jniwrapper.JNIWrapper.KEY_PROTOCOL_VERSION; +import static com.huawei.cloudphone.jniwrapper.JNIWrapper.KEY_REGION_ID; +import static com.huawei.cloudphone.jniwrapper.JNIWrapper.KEY_SDK_VERSION; +import static com.huawei.cloudphone.jniwrapper.JNIWrapper.KEY_SESSION_ID; +import static com.huawei.cloudphone.jniwrapper.JNIWrapper.KEY_TICKET; +import static com.huawei.cloudphone.jniwrapper.JNIWrapper.KEY_VERIFY_DATA; + import android.os.RemoteException; import android.view.Surface; -import com.huawei.cloudphone.apiimpl.CloudPhoneImpl; import com.huawei.cloudphone.audio.AudioTrackerCallback; import com.huawei.cloudphone.common.CASLog; -import com.huawei.cloudphone.datacenter.CasRecvPktDispatcher; -import com.huawei.cloudphone.datacenter.NewPacketCallback; -import com.huawei.cloudphone.jniwrapper.JNIWrapper; -import com.huawei.cloudphone.jniwrapper.JniBridge; import com.huawei.cloudphone.common.CasConnectorInfo; import com.huawei.cloudphone.common.CasParcelableMap; +import com.huawei.cloudphone.jniwrapper.JNIWrapper; +import com.huawei.cloudphone.jniwrapper.JniBridge; import com.huawei.cloudphone.utils.CasAESUtils; import org.json.JSONException; @@ -38,20 +49,6 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.HashMap; -import static com.huawei.cloudphone.jniwrapper.JNIWrapper.KEY_AES_IV; -import static com.huawei.cloudphone.jniwrapper.JNIWrapper.KEY_AUTH_TS; -import static com.huawei.cloudphone.jniwrapper.JNIWrapper.KEY_CLIENT_MODE; -import static com.huawei.cloudphone.jniwrapper.JNIWrapper.KEY_ENCRYPTED_DATA; -import static com.huawei.cloudphone.jniwrapper.JNIWrapper.KEY_BACKGROUND_TIMEOUT; -import static com.huawei.cloudphone.jniwrapper.JNIWrapper.KEY_IP; -import static com.huawei.cloudphone.jniwrapper.JNIWrapper.KEY_PORT; -import static com.huawei.cloudphone.jniwrapper.JNIWrapper.KEY_PROTOCOL_VERSION; -import static com.huawei.cloudphone.jniwrapper.JNIWrapper.KEY_REGION_ID; -import static com.huawei.cloudphone.jniwrapper.JNIWrapper.KEY_SDK_VERSION; -import static com.huawei.cloudphone.jniwrapper.JNIWrapper.KEY_SESSION_ID; -import static com.huawei.cloudphone.jniwrapper.JNIWrapper.KEY_TICKET; -import static com.huawei.cloudphone.jniwrapper.JNIWrapper.KEY_VERIFY_DATA; - /** * CasProcessor */ @@ -62,21 +59,11 @@ public class CasProcessor { */ private static final String TAG = "CasProcessor"; - /** - * upstream receive dispatcher - */ - private CasRecvPktDispatcher mUpstreamReceiveDispatcher = null; - /** * audio tracker callback */ private AudioTrackerCallback mAudioTrackerCallback = null; - /** - * channel data callback - */ - private NewChannelDataPacket mChannelDataCallback = null; - /** * surface */ @@ -85,14 +72,20 @@ public class CasProcessor { /** * listener */ - private CloudPhoneImpl.CASListener mListener = null; - private NewRotationDirectionPacket mNewRotationDirPkt; - private NewVirtualDevDataPacket mVirtualDevDataPkt; - private NewImeDataPacket mImeDataPkt; + private CasProcessorListener mListener = null; + private boolean mJniCallback; + private Object mJniCallbackLock = new Object(); + private JniBridge mJniBridge; + + public CasProcessor() { + mJniCallback = false; + } - public void init() { + public void init(String logPath) { + mJniBridge = new JniBridge(logPath); + mJniBridge.init(); CASLog.i(TAG, "init...."); - CasInteractiveStateCallback interactiveStateCallback = new CasInteractiveStateCallback() { + CasCallback casCallback = new CasCallback() { @Override public void onCmdReceive(int code, String msg) { if (mListener != null) { @@ -103,13 +96,52 @@ public class CasProcessor { } } } + + @Override + public void onMessageReceive(int type, byte[] data, int length) throws RemoteException { + synchronized (mJniCallbackLock) { + if (!mJniCallback) { + return; + } + switch (type) { + case JNIWrapper.AUDIO: + mAudioTrackerCallback.onNewPacket(data); + break; + case JNIWrapper.IMEDATA: + mListener.onImeMsgRecv(data); + break; + case JNIWrapper.ORIENTATION: + int rotation = data[0] & 0xFF; + mListener.onRotationDirectionChange(rotation); + break; + case JNIWrapper.CHANNEL: + mListener.onChannelDataRecv(data); + break; + case JNIWrapper.SCREENSHOT: + mListener.onScreenCaptureRecv(data); + break; + case JNIWrapper.CAMERA_DATA: + case JNIWrapper.MICROPHONE_DATA: + case JNIWrapper.SENSOR_DATA: + case JNIWrapper.LOCATION_DATA: + mListener.onVirtualDevDataRecv(data); + break; + default: + break; + } + } + } }; - JniBridge.getInstance().registerCasJNICallback(interactiveStateCallback); + mJniBridge.registerCasJNICallback(casCallback); + } + + public void deinit() { + mJniBridge.deinit(); } public void setEncryptData(String encryptData) { CASLog.i(TAG, "setEncryptData...."); - JniBridge.getInstance().setJniConf(KEY_ENCRYPTED_DATA, encryptData); + mJniBridge.setJniConf(KEY_ENCRYPTED_DATA, encryptData); } public void setSurface(Surface suf) { @@ -119,40 +151,53 @@ public class CasProcessor { public boolean start(boolean isHome) { CASLog.i(TAG, "start...."); - return JniBridge.getInstance().start(mSurface, isHome); + return mJniBridge.start(mSurface, isHome); } public void stop(final boolean isHome) { CASLog.i(TAG, "stop...."); - JniBridge.getInstance().stop(isHome); + mJniBridge.stop(isHome); + } + + public boolean startCapture() { + CASLog.i(TAG, "startCapture...."); + return mJniBridge.startScreenshot(); + } + + public void stopCapture() { + CASLog.i(TAG, "stopCapture...."); + mJniBridge.stopScreenshot(); } public void pause() { CASLog.i(TAG, "pause...."); + mJniBridge.pause(); stopJniRecv(); } public void resume() { CASLog.i(TAG, "resume...."); + mJniBridge.resume(mSurface); + startJniRecv(); } public boolean isConnect() { - boolean isConnect = JNIWrapper.getConnectStatus(); + boolean isConnect = mJniBridge.getConnectStatus(); return isConnect; } public int getState() { CASLog.i(TAG, "getState...."); - return JNIWrapper.getJniStatus(); + return mJniBridge.getJniStatus(); } public boolean reconnect() { - boolean ret = JNIWrapper.reconnect(); + boolean ret = mJniBridge.reconnect(); CASLog.i(TAG, "reconnect ret = " + ret); return ret; } - public void registerListener(CloudPhoneImpl.CASListener listener) { + public void registerListener(CasProcessorListener listener) { CASLog.i(TAG, "registerListener...."); mListener = listener; } @@ -163,89 +208,77 @@ public class CasProcessor { } public boolean sendTouchEvent(int id, int action, int x, int y, int pressure, long time, int orientation, int height, int width) { - return JniBridge.getInstance().sendTouchEvent(id, action, x, y, pressure, time, orientation, height, width); + return mJniBridge.sendTouchEvent(id, action, x, y, pressure, time, orientation, height, width); } public boolean sendKeyEvent(int keycode, int action) { CASLog.i(TAG, "sendKeyEvent, keycode:" + keycode + "action:" + action); - return JniBridge.getInstance().sendKeyEvent(keycode, action); + return mJniBridge.sendKeyEvent(keycode, action); } public boolean sendMotionEvent(int materAxis, int materValue, int secondaryAxis, int secondaryValue) { CASLog.i(TAG, "sendMotionEvent, materAxis:" + materAxis + "materValue:" + materValue + "secondaryAxis:" + secondaryAxis + "secondaryValue:" + secondaryValue); - return JniBridge.getInstance().sendMotionEvent(materAxis, materValue, secondaryAxis, secondaryValue); + return mJniBridge.sendMotionEvent(materAxis, materValue, secondaryAxis, secondaryValue); } public void setMediaConfig(final CasParcelableMap mediaConfigMap) { HashMap mediaConfig = mediaConfigMap.getParcelableMap(); - JniBridge.getInstance().setMediaConfig(mediaConfig); + mJniBridge.setMediaConfig(mediaConfig); } public boolean setRotation(int rotation) { CASLog.i(TAG, "setRotation... "); - return JniBridge.getInstance().setRotation(rotation); + return mJniBridge.setRotation(rotation); } public int getLag() { - return JniBridge.getInstance().getLag(); + return mJniBridge.getLag(); } public String getVideoStreamStats() { - return JniBridge.getInstance().getVideoStreamStats(); + return mJniBridge.getVideoStreamStats(); } public String getSimpleStreamStats() { - return JniBridge.getInstance().getSimpleStreamStats(); + return mJniBridge.getSimpleStreamStats(); } public boolean startJniRecv() { - CASLog.i(TAG, "startJniRecv... "); - mUpstreamReceiveDispatcher = new CasRecvPktDispatcher(); - mAudioTrackerCallback = new AudioTrackerCallback(); - mNewRotationDirPkt = new NewRotationDirectionPacket(); - mChannelDataCallback = new NewChannelDataPacket(); - mVirtualDevDataPkt = new NewVirtualDevDataPacket(); - mImeDataPkt = new NewImeDataPacket(); - - mUpstreamReceiveDispatcher.addNewPacketCallback((byte) JNIWrapper.ORIENTATION, mNewRotationDirPkt); - mUpstreamReceiveDispatcher.addNewPacketCallback((byte) JNIWrapper.AUDIO, mAudioTrackerCallback); - mUpstreamReceiveDispatcher.addNewPacketCallback((byte) JNIWrapper.CHANNEL, mChannelDataCallback); - mUpstreamReceiveDispatcher.addNewPacketCallback((byte) JNIWrapper.VIRTUAL_DEVICE_DATA, mVirtualDevDataPkt); - mUpstreamReceiveDispatcher.addNewPacketCallback((byte) JNIWrapper.IMEDATA, mImeDataPkt); - mUpstreamReceiveDispatcher.start(); + CASLog.i(TAG, "startJniRecv..... "); + synchronized (mJniCallbackLock) { + CASLog.i(TAG, "startJniRecv... "); + mAudioTrackerCallback = new AudioTrackerCallback(); + mJniCallback = true; + } return true; } public void stopJniRecv() { - CASLog.i(TAG, "stopJniRecv... "); - if (mUpstreamReceiveDispatcher != null) { - mUpstreamReceiveDispatcher.deleteNewPacketCallback((byte) JNIWrapper.AUDIO); - mUpstreamReceiveDispatcher.deleteNewPacketCallback((byte) JNIWrapper.ORIENTATION); - mUpstreamReceiveDispatcher.deleteNewPacketCallback((byte) JNIWrapper.CHANNEL); - mUpstreamReceiveDispatcher.deleteNewPacketCallback((byte) JNIWrapper.VIRTUAL_DEVICE_DATA); - mUpstreamReceiveDispatcher.deleteNewPacketCallback((byte) JNIWrapper.IMEDATA); - mUpstreamReceiveDispatcher.stopBlocked(); - mUpstreamReceiveDispatcher = null; - mAudioTrackerCallback.closeAudioTrack(); - mAudioTrackerCallback = null; + synchronized (mJniCallbackLock) { + CASLog.i(TAG, "stopJniRecv... "); + if (mJniCallback) { + mAudioTrackerCallback.closeAudioTrack(); + mAudioTrackerCallback = null; + mJniCallback = false; + } } } public void setCasConnectorInfo(CasConnectorInfo info) { CASLog.i(TAG, "setCasConnectorInfo..."); - JniBridge.getInstance().setJniConf(KEY_IP, info.getConnectIp()); - JniBridge.getInstance().setJniConf(KEY_PORT, info.getConnectPort()); - JniBridge.getInstance().setJniConf(KEY_TICKET, info.getTicket()); - JniBridge.getInstance().setJniConf(KEY_SESSION_ID, info.getSessionId()); - JniBridge.getInstance().setJniConf(KEY_AES_IV, info.getAesIv()); - JniBridge.getInstance().setJniConf(KEY_AUTH_TS, info.getAuthTs()); - JniBridge.getInstance().setJniConf(KEY_SDK_VERSION, info.getSdkVersion()); - JniBridge.getInstance().setJniConf(KEY_BACKGROUND_TIMEOUT, info.getBackgroundTimeout()); - JniBridge.getInstance().setJniConf(KEY_PROTOCOL_VERSION, info.getProtocolVersion()); - JniBridge.getInstance().setJniConf(KEY_VERIFY_DATA, buildVerifyDataInfo(info)); - JniBridge.getInstance().setJniConf(KEY_REGION_ID, info.getRegionId()); - JniBridge.getInstance().setJniConf(KEY_CLIENT_MODE, info.getClientMode()); + mJniBridge.setJniConf(KEY_IP, info.getConnectIp()); + mJniBridge.setJniConf(KEY_PORT, info.getConnectPort()); + mJniBridge.setJniConf(KEY_TICKET, info.getTicket()); + mJniBridge.setJniConf(KEY_SESSION_ID, info.getSessionId()); + mJniBridge.setJniConf(KEY_AES_IV, info.getAesIv()); + mJniBridge.setJniConf(KEY_AUTH_TS, info.getAuthTs()); + mJniBridge.setJniConf(KEY_SDK_VERSION, info.getSdkVersion()); + mJniBridge.setJniConf(KEY_BACKGROUND_TIMEOUT, info.getBackgroundTimeout()); + mJniBridge.setJniConf(KEY_PROTOCOL_VERSION, info.getProtocolVersion()); + mJniBridge.setJniConf(KEY_VERIFY_DATA, buildVerifyDataInfo(info)); + mJniBridge.setJniConf(KEY_REGION_ID, info.getRegionId()); + mJniBridge.setJniConf(KEY_CLIENT_MODE, info.getClientMode()); String encryptedData = ""; try { @@ -253,11 +286,11 @@ public class CasProcessor { } catch (JSONException e) { CASLog.e(TAG, "failed to build encrypted data info."); } - JniBridge.getInstance().setJniConf(KEY_ENCRYPTED_DATA, encryptedData); + mJniBridge.setJniConf(KEY_ENCRYPTED_DATA, encryptedData); } public void sendData(byte devType, byte[] data) { - JniBridge.getInstance().sendData(devType, data, data.length); + mJniBridge.sendData(devType, data, data.length); } /** @@ -300,58 +333,4 @@ public class CasProcessor { String encryptedData = CasAESUtils.encryptWithAESGCM(jsonObj.toString(), connectorInfo.getAesKey(), connectorInfo.getAesIv()); return encryptedData; } - - private class NewRotationDirectionPacket implements NewPacketCallback { - @Override - public void onNewPacket(byte[] data) { - int rotation = data[0] & 0xFF; - CASLog.i(TAG, "rotation change " + rotation); - if (null != mListener) { - try { - mListener.onRotationDirectionChange(rotation); - } catch (RemoteException e) { - CASLog.e(TAG, "call onRotationDirectionChange failed."); - } - } - } - } - - private class NewChannelDataPacket implements NewPacketCallback { - @Override - public void onNewPacket(byte[] data) { - if (mListener != null) { - try { - mListener.onChannelDataRecv(data); - } catch (RemoteException e) { - CASLog.e(TAG, "call onChannelDataRecv failed." + e.getMessage()); - } - } - } - } - - private class NewVirtualDevDataPacket implements NewPacketCallback { - @Override - public void onNewPacket(byte[] data) { - if (mListener != null) { - try { - mListener.onVirtualDevDataRecv(data); - } catch (RemoteException e) { - CASLog.e(TAG, "call onVirtualDevDataRecv failed." + e.getMessage()); - } - } - } - } - - private class NewImeDataPacket implements NewPacketCallback { - @Override - public void onNewPacket(byte[] data) { - if (mListener != null) { - try { - mListener.onImeMsgRecv(data); - } catch (RemoteException e) { - CASLog.e(TAG, "call onImeMsgRecv failed."); - } - } - } - } } diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/service/CasProcessorListener.java b/cloudphone/src/main/java/com/huawei/cloudphone/service/CasProcessorListener.java new file mode 100644 index 0000000000000000000000000000000000000000..a54d5d184fb4e708cd4ef7e1ecf26b56505a46c0 --- /dev/null +++ b/cloudphone/src/main/java/com/huawei/cloudphone/service/CasProcessorListener.java @@ -0,0 +1,17 @@ +package com.huawei.cloudphone.service; + +import android.os.RemoteException; + +public interface CasProcessorListener { + void onRotationDirectionChange(int orientation) throws RemoteException; + + void onCmdRecv(int code, String describe) throws RemoteException; + + void onChannelDataRecv(byte[] data) throws RemoteException; + + void onVirtualDevDataRecv(byte[] data) throws RemoteException; + + void onImeMsgRecv(byte[] data) throws RemoteException; + + void onScreenCaptureRecv(byte[] data) throws RemoteException; +} diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/common/RingBufferVirtualDeviceIO.java b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/common/RingBufferVirtualDeviceIO.java index 4a8f7ed6c89cae4c4fd99214de7630ca58b70307..7d9c093ccbaf411613c76cf0fcdac52273e234b5 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/common/RingBufferVirtualDeviceIO.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/virtualdevice/common/RingBufferVirtualDeviceIO.java @@ -16,6 +16,7 @@ package com.huawei.cloudphone.virtualdevice.common; import com.huawei.cloudphone.api.CloudPhoneManager; +import com.huawei.cloudphone.api.ICloudPhone; import java.nio.ByteBuffer; @@ -25,15 +26,17 @@ public class RingBufferVirtualDeviceIO implements IVirtualDeviceIO{ private int mDataOffset; private byte[] mDataBuffer; private Object mObjectLock = new Object(); + private ICloudPhone mCloudPhone; private static final int VIRTUAL_CAMERA = 0; private static final int VIRTUAL_MICROPHONE = 1; private static final int VIRTUAL_SENSOR = 2; - public RingBufferVirtualDeviceIO() { + public RingBufferVirtualDeviceIO(ICloudPhone cloudPhone) { mDataOffset = 0; mDataBuffer = null; mDataLen = 0; mRingBuffer = new RingBuffer(); + mCloudPhone = cloudPhone; } @@ -63,7 +66,7 @@ public class RingBufferVirtualDeviceIO implements IVirtualDeviceIO{ @Override public int writeN(byte[] data, int offset, int length, int deviceType) { synchronized (mObjectLock) { - CloudPhoneManager.createCloudPhoneInstance().sendVirtualDeviceData((byte) deviceType, data); + mCloudPhone.sendVirtualDeviceData((byte) deviceType, data); } return length; }