From 6dff8b8928661f1a46116b98c40935b170daa92d Mon Sep 17 00:00:00 2001 From: CaiFeng <2397707574@qq.com> Date: Sat, 21 Sep 2024 11:25:04 +0800 Subject: [PATCH 1/3] snapshot --- .../common/CasImageQualityManager.java | 50 - .../ui/activity/CasCloudPhoneActivity.java | 30 +- cloudphone/src/main/cpp/CMakeLists.txt | 3 +- cloudphone/src/main/cpp/CasController.cpp | 1384 ++++------------- cloudphone/src/main/cpp/CasController.h | 112 +- cloudphone/src/main/cpp/CasJniBridge.cpp | 290 +++- cloudphone/src/main/cpp/CasJniBridge.h | 56 +- cloudphone/src/main/cpp/cas_common/CasMsg.h | 1 + .../main/cpp/cas_controller/CMakeLists.txt | 10 + .../src/main/cpp/cas_controller/CasClient.cpp | 178 +++ .../src/main/cpp/cas_controller/CasClient.h | 53 + .../main/cpp/cas_controller/CasCmdHandler.cpp | 60 + .../main/cpp/cas_controller/CasCmdHandler.h | 21 + .../cpp/cas_controller/CasCommonHandler.cpp | 36 + .../cpp/cas_controller/CasCommonHandler.h | 21 + .../cpp/cas_controller/CasHandlerManager.cpp | 148 ++ .../cpp/cas_controller/CasHandlerManager.h | 37 + .../cpp/cas_controller/CasHearbeatHandler.cpp | 145 ++ .../cpp/cas_controller/CasHearbeatHandler.h | 45 + .../cpp/cas_controller/CasMessageHandler.h | 31 + .../src/main/cpp/cas_controller/CasMright.cpp | 119 ++ .../src/main/cpp/cas_controller/CasMright.h | 26 + .../cpp/cas_controller/CasVideoHandler.cpp | 78 + .../cpp/cas_controller/CasVideoHandler.cpp+++ | 129 ++ .../main/cpp/cas_controller/CasVideoHandler.h | 36 + .../cpp/cas_controller/CasVideoHandler.h+++ | 35 + .../cpp/cas_decoder/CasDecodeController.cpp | 98 +- .../cpp/cas_decoder/CasDecodeController.h | 31 +- .../main/cpp/cas_decoder/CasVideoEngine.cpp | 55 +- .../src/main/cpp/cas_decoder/CasVideoEngine.h | 2 + .../src/main/cpp/cas_service/CasDataPipe.cpp | 7 +- .../src/main/cpp/cas_service/CasTouch.cpp | 8 +- .../src/main/cpp/cas_service/CasTouch.h | 4 +- .../cpp/cas_service/CasVideoHDecodeThread.cpp | 6 +- .../cpp/cas_socket/CasTcpClientSocket.cpp | 9 + .../src/main/cpp/cas_socket/CasTcpSocket.h | 2 + .../src/main/cpp/cas_stream/CMakeLists.txt | 2 +- .../cpp/cas_stream/CasStreamBuildSender.cpp | 16 +- .../cpp/cas_stream/CasStreamBuildSender.h | 5 +- .../cpp/cas_stream/CasStreamRecvParser.cpp | 37 +- .../main/cpp/cas_stream/CasStreamRecvParser.h | 6 +- .../api/CloudAppScreenshotListener.java | 5 + .../cloudphone/api/CloudPhoneManager.java | 12 +- .../huawei/cloudphone/api/ICloudPhone.java | 10 + .../cloudphone/apiimpl/CloudPhoneImpl.java | 98 +- .../audio/AudioTrackerCallback.java | 7 +- .../datacenter/CasRecvPktDispatcher.java | 130 -- .../cloudphone/jniwrapper/JNIWrapper.java | 62 +- .../cloudphone/jniwrapper/JniBridge.java | 75 +- .../jniwrapper/JniMessageListener.java | 10 + .../CasCallback.java} | 19 +- .../cloudphone/service/CasProcessor.java | 217 ++- 52 files changed, 2283 insertions(+), 1784 deletions(-) delete mode 100644 app/src/main/java/com/huawei/cloudapp/common/CasImageQualityManager.java create mode 100644 cloudphone/src/main/cpp/cas_controller/CMakeLists.txt create mode 100644 cloudphone/src/main/cpp/cas_controller/CasClient.cpp create mode 100644 cloudphone/src/main/cpp/cas_controller/CasClient.h create mode 100644 cloudphone/src/main/cpp/cas_controller/CasCmdHandler.cpp create mode 100644 cloudphone/src/main/cpp/cas_controller/CasCmdHandler.h create mode 100644 cloudphone/src/main/cpp/cas_controller/CasCommonHandler.cpp create mode 100644 cloudphone/src/main/cpp/cas_controller/CasCommonHandler.h create mode 100644 cloudphone/src/main/cpp/cas_controller/CasHandlerManager.cpp create mode 100644 cloudphone/src/main/cpp/cas_controller/CasHandlerManager.h create mode 100644 cloudphone/src/main/cpp/cas_controller/CasHearbeatHandler.cpp create mode 100644 cloudphone/src/main/cpp/cas_controller/CasHearbeatHandler.h create mode 100644 cloudphone/src/main/cpp/cas_controller/CasMessageHandler.h create mode 100644 cloudphone/src/main/cpp/cas_controller/CasMright.cpp create mode 100644 cloudphone/src/main/cpp/cas_controller/CasMright.h create mode 100644 cloudphone/src/main/cpp/cas_controller/CasVideoHandler.cpp create mode 100644 cloudphone/src/main/cpp/cas_controller/CasVideoHandler.cpp+++ create mode 100644 cloudphone/src/main/cpp/cas_controller/CasVideoHandler.h create mode 100644 cloudphone/src/main/cpp/cas_controller/CasVideoHandler.h+++ create mode 100644 cloudphone/src/main/java/com/huawei/cloudphone/api/CloudAppScreenshotListener.java delete mode 100644 cloudphone/src/main/java/com/huawei/cloudphone/datacenter/CasRecvPktDispatcher.java create mode 100644 cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JniMessageListener.java rename cloudphone/src/main/java/com/huawei/cloudphone/{datacenter/NewPacketCallback.java => service/CasCallback.java} (64%) diff --git a/app/src/main/java/com/huawei/cloudapp/common/CasImageQualityManager.java b/app/src/main/java/com/huawei/cloudapp/common/CasImageQualityManager.java deleted file mode 100644 index 30746f2..0000000 --- a/app/src/main/java/com/huawei/cloudapp/common/CasImageQualityManager.java +++ /dev/null @@ -1,50 +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.cloudapp.common; - -public class CasImageQualityManager { - - public static final int QUALITY_BEST = 0; - public static final int QUALITY_MAIN = 1; - public static final int QUALITY_BASIC = 2; - private volatile static CasImageQualityManager manager = null; - private int imageQualityStatus = 0; - - private CasImageQualityManager() { - } - - public static CasImageQualityManager getInstance() { - if (null == manager) { - synchronized (CasImageQualityManager.class) { - if (null == manager) { - manager = new CasImageQualityManager(); - } - } - } - - return manager; - } - - public int getImageQualityStatus() { - return this.imageQualityStatus; - } - - public void setImageQualityStatus(int qualityStatus) { - this.imageQualityStatus = qualityStatus; - } - -} diff --git a/app/src/main/java/com/huawei/cloudapp/ui/activity/CasCloudPhoneActivity.java b/app/src/main/java/com/huawei/cloudapp/ui/activity/CasCloudPhoneActivity.java index 2063876..80ead31 100644 --- a/app/src/main/java/com/huawei/cloudapp/ui/activity/CasCloudPhoneActivity.java +++ b/app/src/main/java/com/huawei/cloudapp/ui/activity/CasCloudPhoneActivity.java @@ -66,6 +66,7 @@ import android.content.res.Configuration; import android.graphics.Color; import android.os.Build; import android.os.Bundle; +import android.os.Environment; import android.os.Handler; import android.os.Message; import android.telephony.CellSignalStrength; @@ -119,6 +120,7 @@ import com.huawei.cloudapp.utils.CasAESKeystoreUtils; import com.huawei.cloudapp.utils.CasAdaptPhoneUtils; import com.huawei.cloudapp.utils.CasCommonUtils; import com.huawei.cloudphone.api.CloudAppDataListener; +import com.huawei.cloudphone.api.CloudAppScreenshotListener; import com.huawei.cloudphone.api.CloudPhoneClipboardListener; import com.huawei.cloudphone.api.CloudPhoneManager; import com.huawei.cloudphone.api.CloudPhoneOrientationChangeListener; @@ -128,6 +130,9 @@ import com.huawei.cloudphone.api.CloudPhonePermissionRequestListener; import com.huawei.cloudphone.api.CloudPhoneStateListener; import com.huawei.cloudphone.api.ICloudPhone; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.HashMap; @@ -195,6 +200,7 @@ public class CasCloudPhoneActivity extends FragmentActivity implements IHandleDa private Toast mToast; private int storagePermission = 1; + private ICloudPhone mCloudPhoneScreenshot = null; private Handler handler = new Handler() { @SuppressLint("RestrictedApi") @@ -218,6 +224,8 @@ public class CasCloudPhoneActivity extends FragmentActivity implements IHandleDa case MSG_RECONNECT_FAILED: try { mCloudPhone.exitCloudPhone(); +// mCloudPhoneScreenshot.stopScreenshot(); +// mCloudPhoneScreenshot.exitCloudPhone(); } catch (Exception e) { CASLog.e(TAG, "exitCloudPhone failed"); } @@ -346,7 +354,7 @@ public class CasCloudPhoneActivity extends FragmentActivity implements IHandleDa handler.removeMessages(MSG_PRESS_APP_SWITCH); break; case MotionEvent.ACTION_MOVE: - if (!isInView(motionEvent, view)) { + if (!isInView(motionEvent, view)) { handler.removeMessages(MSG_PRESS_HOME); handler.removeMessages(MSG_PRESS_APP_SWITCH); } @@ -371,10 +379,13 @@ public class CasCloudPhoneActivity extends FragmentActivity implements IHandleDa mCloudPhone.registerCloudAppDataListener(new CloudAppDataListenerImpl()); mCloudPhone.registerClipboardListener(new CloudPhoneClipboardListenerImpl()); +// mCloudPhoneScreenshot = CloudPhoneManager.createCloudPhoneInstance(); +// mCloudPhoneScreenshot.init(this, DEV_PHONE); + if (mMediaConfig == null) { mMediaConfig = new HashMap(); } - if (isSupportH265()) { + if (false/*isSupportH265()*/) { mMediaConfig.put(FRAME_TYPE, "h265"); } else { mMediaConfig.put(FRAME_TYPE, "h264"); @@ -386,6 +397,10 @@ public class CasCloudPhoneActivity extends FragmentActivity implements IHandleDa mMediaConfig.put(PHYSICAL_WIDTH, String.valueOf(metrics.widthPixels)); mMediaConfig.put(PHYSICAL_HEIGHT, String.valueOf(metrics.heightPixels)); mCloudPhone.setMediaConfig(mMediaConfig); + HashMap mediaConfig = new HashMap<>(); + mediaConfig.put("stream_width", "720"); + mediaConfig.put("stream_height", "1280"); +// mCloudPhoneScreenshot.setMediaConfig(mediaConfig); getPhoneConnectInfo(mPhoneId); } catch (IllegalArgumentException e) { @@ -1104,6 +1119,17 @@ public class CasCloudPhoneActivity extends FragmentActivity implements IHandleDa public void run() { try { mCloudPhone.startCloudPhone(CasCloudPhoneActivity.this, mFrameLayout, parasMap); +// mCloudPhoneScreenshot.startScreenshot(parasMap, new CloudAppScreenshotListener() { +// @Override +// public void onRecvPicture(byte[] picture) { +// File extDir = Environment.getExternalStorageDirectory(); +// File file = new File(extDir, "phone.jpg"); +// try (FileOutputStream fos = new FileOutputStream(file)) { +// fos.write(picture); +// } catch (IOException e) { +// } +// } +// }); } catch (IllegalArgumentException e) { showDialog(getResources().getString(R.string.cas_phone_input_param_invalid)); } catch (Exception e) { diff --git a/cloudphone/src/main/cpp/CMakeLists.txt b/cloudphone/src/main/cpp/CMakeLists.txt index b831d16..0adea55 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 7734d60..9cf3f7a 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/CasClient.h" +#include "cas_controller/CasHandlerManager.h" using namespace std; @@ -42,230 +44,34 @@ 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; + 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; + 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_casMessages.clear(); + m_casMessages.push_back(CasMsgType::Video); + m_casMessages.push_back(CasMsgType::HeartBeat); + m_casMessages.push_back(CasMsgType::CmdControl); + m_casMessages.push_back(CasMsgType::Orientation); + m_casMessages.push_back(CasMsgType::Audio); + 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) @@ -273,33 +79,18 @@ 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; + 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_casClient->stop(); + m_casClient->disconnect(); + m_casHandlerManager->stop(); + ReleaseResource(); m_rotationDegrees = 0; m_orientation = 0; m_mediaConfig.clear(); @@ -307,281 +98,71 @@ 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); + 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 lockGuard(this->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; + m_casHandlerManager->stop(); + m_casHandlerManager->start(); + 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); - } - } -#endif - m_streamParseThread->Start(); - m_heartbeatThread->Start(); - m_controlThread->Start(); - INFO("Succeed to start workers"); - return true; + m_snapshotFlag = true; + prepareParameters(); + m_casMessages.clear(); + m_casMessages.push_back(CasMsgType::HeartBeat); + m_casMessages.push_back(CasMsgType::CmdControl); + m_casMessages.push_back(CasMsgType::Orientation); + m_casMessages.push_back(CasMsgType::ScreenShot); + return ProcessStart(); } -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) @@ -590,18 +171,16 @@ bool CasController::SetMediaConfig(map 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 +190,157 @@ 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) { + std::lock_guard lockGuard(m_lock); + if (this->GetState() != START_SUCCESS) { + INFO("Failed to send touch event because phone not start."); return false; } - return m_touch->SendTouchEvent(id, action, x, y, pressure, new_time, orientation, height, width); + INFO("SendTouchEvent ..."); + return m_touch->SendTouchEvent(id, action, x, y, pressure, (int)time, orientation, height, width); } bool CasController::SendKeyEvent(uint16_t keycode, uint16_t action) { - std::lock_guard lockGuard(this->m_lock); - if (this->GetState() == STOPPED) { + std::lock_guard lockGuard(m_lock); + if (this->GetState() != START_SUCCESS) { 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; - } + INFO("SendKeyEvent ..."); return m_touch->SendKeyEvent(keycode, action); } bool CasController::SendMotionEvent(uint16_t masterAxis, int32_t masterValue, uint16_t secondaryAxis, - int32_t secondaryValue) + 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; - } -#endif - - if (m_touch == nullptr) { + std::lock_guard lockGuard(m_lock); + if (this->GetState() != START_SUCCESS) { + INFO("Failed to send motion event because phone not start."); return false; } - + INFO("SendMotionEvent ..."); 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; - } - } -#endif - - if (m_streamBuildSender == nullptr) { - return -1; + std::lock_guard lockGuard(m_lock); + if (this->GetState() != START_SUCCESS) { + INFO("Failed to send data because phone not start."); + return false; } return length == m_streamBuildSender->SendDataToServer(type, data, length); } -int CasController::JniRecvData(CasMsgType type, uint8_t *data, int length) +JNIState CasController::GetState() { - 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; - } + return this->m_state; +} - if (pPkt == nullptr) { - DBG("Packet is null."); - return 0; +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 || !containsStreamHeight) { + ERR("Stream width or Stream height is not included"); + return false; } - - if (pPkt != nullptr) { - free(pPkt); - pPkt = nullptr; + 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; } + return true; +} - return dataLen; +string CasController::CalcMaxDisconnectDuration(string backgroundTimeout) +{ + int maxDisconnectDuration = 60; + int timeout = atoi(backgroundTimeout.c_str()); + if (timeout > 120) { + maxDisconnectDuration = timeout - 60; + } + return to_string(maxDisconnectDuration); } -void CasController::IsNeedRotation(int orientation) +bool CasController::BuildConnection() { - INFO("ORIENTATION is %d, old ORIENTATION is %d.", orientation, m_orientation); + NotifyCommand(CAS_CONNECTING, CasMsgCode::GetMsg(CAS_CONNECTING)); + int connectRes = m_casClient->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; +} - if (orientation == 8) { - m_rotationDegrees = 270; - } else if (orientation == 24) { - m_rotationDegrees = 90; - } else if (orientation == 16) { - m_rotationDegrees = 180; - } else { - m_rotationDegrees = 0; +bool CasController::SendCommand(map parameters) +{ + 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 0; } 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 +354,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,51 +372,11 @@ bool CasController::ProcessEnterForeground(ANativeWindow *nativeWindow) ERR("Failed to send resume command"); return false; } + SetState(START_SUCCESS); + 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) -{ - 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; - } -} - bool CasController::ForceIFrame() { std::lock_guard lockGuard(this->m_lock); @@ -1065,12 +384,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 +401,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, std::move(msg), m_callbackOpaque); } } @@ -1108,20 +418,31 @@ 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]; + if (orientation == 8) { + m_rotationDegrees = 270; + } else if (orientation == 24) { + m_rotationDegrees = 90; + } else if (orientation == 16) { + m_rotationDegrees = 180; + } else { + m_rotationDegrees = 0; } + m_casHandlerManager->stopVideoHandler(); + m_casHandlerManager->startVideoHandler(); + ForceIFrame(); + } + if (m_casMessageCallback != nullptr) { + m_casMessageCallback(header, body, length, m_callbackOpaque); } - CreateDecWorker(m_nativeWindow, m_needVideoDecode); - StartDecWorker(false); } bool CasController::SendStartCmd() @@ -1145,142 +466,14 @@ bool CasController::SendStartCmd() { KEY_REGION_ID, m_conf.regionId }, { KEY_MEDIA_CONFIG, mediaConfigStr }, { KEY_MAX_DISCONNECT_DURATION, m_maxDisconnectDuration } }; - - return SendCommand(parameters); -} - -void CasController::RecvdVideoData(uint8_t *data, int length) -{ - if (m_videoPacketStream != nullptr) { - uint8_t *videoData = new uint8_t[length]; - memcpy(videoData, data, length); - m_videoPacketStream->Handle(videoData); - CalculateFPS(); - } - m_isMTransValid = true; -} - -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); - } -} - -bool CasController::IsMtransValid() -{ - return m_isMTransValid; -} - -int32_t OnRecvVideoStreamData(uint8_t* data, uint32_t length) -{ - CasController::GetInstance()->RecvdVideoData(data, length); - return 0; -} - -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; - } - 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), dataLen - sizeof(stream_msg_head_t), data, length)) { - ERR("Copy msg data fail."); - free(outBuffer); - return 0; - } - - CasController::GetInstance()->RecvdAudioData(reinterpret_cast(outBuffer), dataLen); - 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) { - 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; -} - -void OnGotTransLog(const char* str, uint32_t length) -{ - if (hrtpLogger == nullptr) { - return; + if (m_snapshotFlag) { + parameters["operate_type"] = "screenshot"; } - hrtpLogger->info(str); -} - -void OnNeedKeyFrameCallback() -{ - INFO("Need key frame callback.."); - CasController::GetInstance()->NotifyCommand(CAS_REQUEST_CAMERA_KEY_FRAME, "camera need key frame"); -} - -int32_t OnRecvCmdData(uint8_t* data, uint32_t length) -{ - INFO("OnRecvCmdData"); - CasController::GetInstance()->HandleCmdData(reinterpret_cast(data), length); - return 0; + return SendCommand(parameters); } -void CasController::HandleCmdData(uint8_t *data, int length) +std::string CasController::GetSimpleRecvStats() { - 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; - } - 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); - } - } -} - -std::string CasController::GetSimpleRecvStats() { std::string statsString; std::stringstream stream; @@ -1291,60 +484,15 @@ std::string CasController::GetSimpleRecvStats() { stream << lag << "ms"; } -#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"; - } - } -#endif - stream << " " << GetCurrentFPS() << "FPS"; statsString = stream.str(); return statsString; } -std::string CasController::GetVideoRecvStats() { +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; - - 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; - } - } -#endif stream << "帧率 : " << GetCurrentFPS() << "fps" << std::endl; stream << "策略丢帧 : " << CasVideoUtil::GetInstance()->GetCurrentDropFPS() << "fps" << std::endl; @@ -1352,7 +500,8 @@ std::string CasController::GetVideoRecvStats() { return statsString; } -void CasController::CalculateFPS() { +void CasController::CalculateFPS() +{ uint64_t currentTime = CasVideoUtil::GetInstance()->GetNow(); m_videoDataCount++; @@ -1367,10 +516,141 @@ 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 +} + +bool CasController::prepareParameters() +{ + 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; + } + m_frameType = FrameType::H264; + return true; +} + +bool CasController::ProcessStart() +{ + m_casClient = new (std::nothrow) CasClient(); + if (m_casClient == nullptr) { + ERR("Couldn't create CasClient."); + return false; + } + m_casHandlerManager = new (std::nothrow) CasHandlerManager(this); + if (m_casHandlerManager == nullptr) { + ERR("Couldn't create CasHandlerManager."); + goto FAILED; + } + m_streamBuildSender = new (std::nothrow) CasStreamBuildSender(m_casClient); + if (m_streamBuildSender == nullptr) { + ERR("Couldn't create CasStreamBuildSender."); + goto FAILED; + } + m_cmdController = new (std::nothrow) CasCmdController(m_streamBuildSender); + if (m_cmdController == nullptr) { + ERR("Couldn't create CasCmdController."); + goto FAILED; + } + m_touch = new (std::nothrow) CasTouch(m_casClient); + if (m_touch == nullptr) { + ERR("Couldn't create CasTouch."); + goto FAILED; + } + m_casClient->init(m_ip, m_port); + m_casClient->setListner(m_casHandlerManager); + + for (auto &it : m_casMessages) { + m_casHandlerManager->createHandler(it); + } + if (!BuildConnection()) { + ERR("Connect failed."); + goto FAILED; + } + m_casHandlerManager->start(); + if (m_casClient->start() != 0) { + ERR("Failed to build connection"); + goto FAILED; + } + if (!SendStartCmd()) { + ERR("Send start cmd failed."); + goto FAILED; + } + return true; + +FAILED: + ReleaseResource(); + return false; +} + +bool CasController::ProcessReconnect() +{ + NotifyCommand(CAS_RECONNECTING, CasMsgCode::GetMsg(CAS_RECONNECTING)); + int connectRes = m_casClient->reconnect(); + if (connectRes != 0) { + 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)); + 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 } }; + + bool res = SendCommand(parameters); + if (!res) { + ERR("Failed to send reconnect command"); + return false; + } + SetConnectStatus(true); + doForceIFrame(); + SetState(START_SUCCESS); + return true; +} + +void CasController::ReleaseResource() +{ + if (m_casClient != nullptr) { + delete m_casClient; + m_casClient = nullptr; + } + if (m_casHandlerManager != nullptr) { + m_casHandlerManager->clear(); + delete m_casHandlerManager; + m_casHandlerManager = nullptr; + } + if (m_streamBuildSender != nullptr) { + delete m_streamBuildSender; + m_streamBuildSender = nullptr; + } + if (m_cmdController != nullptr) { + delete m_cmdController; + m_cmdController = nullptr; + } + if (m_touch != nullptr) { + delete m_touch; + m_touch = nullptr; + } +} diff --git a/cloudphone/src/main/cpp/CasController.h b/cloudphone/src/main/cpp/CasController.h index 86def00..a02cb8e 100644 --- a/cloudphone/src/main/cpp/CasController.h +++ b/cloudphone/src/main/cpp/CasController.h @@ -18,6 +18,7 @@ #include #include #include +#include #include "CasDataPipe.h" #include "CasTouch.h" #include "CasConf.h" @@ -34,12 +35,14 @@ #include "CasStreamBuildSender.h" #include "libs/mtrans/include/net_trans.h" -class CasController : public CasControllerListener { -public: - static CasController *GetInstance(); +class CasClient; +class CasHandlerManager; - 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 +51,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,20 +71,15 @@ 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); - uint64_t GetLag(); JNIState GetState(); @@ -82,87 +88,46 @@ public: 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 onMessageRecv(stream_msg_head_t *header, uint8_t *body, int length); - void ResetDecoder(bool isClearStream); + friend class CasHandlerManager; +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); - 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; + CasCmdCallback m_casCmdCallbck = nullptr; + CasMessageCallback m_casMessageCallback = 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; @@ -173,7 +138,6 @@ private: 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 +150,19 @@ 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; + CasClient *m_casClient = nullptr; + CasHandlerManager *m_casHandlerManager = nullptr; + + std::vector m_casMessages; + bool m_isconnect; }; #endif // CLOUDAPPSDK_CASCONTROLLRT_H \ No newline at end of file diff --git a/cloudphone/src/main/cpp/CasJniBridge.cpp b/cloudphone/src/main/cpp/CasJniBridge.cpp index b4b470e..9f2ab3f 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,34 @@ 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) +{ + return reinterpret_cast(new 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 +85,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 +100,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 void JNICALL JNI(pause)(JNIEnv *, jobject clazz, jlong nativeObject) +{ + CasController *casController = reinterpret_cast(nativeObject); + if (casController == nullptr) { + ERR("native CasController object is nullptr."); + return; + } + casController->Pause(); +} + +JNIEXPORT void 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; + } + gANativeWindow = ANativeWindow_fromSurface(env, surface); + 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 +199,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,14 +358,19 @@ 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; @@ -257,24 +383,62 @@ void *invokeCmdCallBack(int type, string msg) } mNeedDetach = JNI_TRUE; } - jclass javaClass = env->GetObjectClass(jniCallback); + jobject jniCallbackObject = static_cast(opaque); + INFO("jniCallbackObject %p", jniCallbackObject); + 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())); + jstring jmessage = env->NewStringUTF(msg.c_str()); + env->CallVoidMethod(jniCallbackObject, javaCallbackId, type, jmessage); + // env->ReleaseStringUTFChars(jmessage, msg.c_str()); INFO("Invoke cmd callback end."); + 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 1c385b7..5530899 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); -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 void JNICALL JNI(pause)(JNIEnv *env, jobject clazz, jlong nativeObject); -JNIEXPORT jstring JNICALL JNI(getSimpleStreamStats)(JNIEnv *env, jclass); +JNIEXPORT void 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 f591ff8..1fcb7f6 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 0000000..ab9a526 --- /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/CasClient.cpp b/cloudphone/src/main/cpp/cas_controller/CasClient.cpp new file mode 100644 index 0000000..113101a --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasClient.cpp @@ -0,0 +1,178 @@ +#include "CasClient.h" +#include "../cas_socket/CasTcpSocket.h" +#include "../cas_common/CasLog.h" +#include "../cas_common/CasBuffer.h" + +CasClient::CasClient() +{ + INFO("CasClient constructure."); + m_ip = 0; + m_port = 0; + m_listener = nullptr; + m_casClientSocket = nullptr; +#if 1 + if (true) { + m_casMright = new CasMright(); + } +#endif +} + +CasClient::~CasClient() +{ + INFO("CasClient deconstructure."); +} + +int CasClient::init(uint32_t ip, uint16_t port) +{ + INFO("Init, ip=%d, port=%d", ip, port); + std::lock_guard lock(m_lock); + m_ip = ip; + m_port = port; + if (m_casMright != nullptr) { + + } + return 0; +} + +int CasClient::deinit() +{ + INFO("Deinit."); + return 0; +} + +int CasClient::connect() +{ + INFO("Connect."); + std::lock_guard lock(m_lock); + m_casClientSocket = new (std::nothrow) CasTcpClientSocket(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."); + return result; + } + INFO("Connect server success."); + return 0; +} + +int CasClient::disconnect() +{ + INFO("Disconnect."); + std::lock_guard lock(m_lock); + if (m_casClientSocket != nullptr) { + delete m_casClientSocket; + m_casClientSocket = nullptr; + } + return 0; +} + +int CasClient::start() +{ + INFO("Start."); + std::lock_guard lock(m_lock); + m_isTaskRun = true; + m_task = std::thread(&CasClient::recvTask, this); + return 0; +} + +int CasClient::stop() +{ + INFO("Stop."); + std::lock_guard lock(m_lock); + if (m_isTaskRun) { + m_isTaskRun = false; + m_casClientSocket->Close(); + m_task.join(); + } + INFO("client stop success."); + return 0; +} + +int CasClient::reconnect() +{ + INFO("Reconnect."); + stop(); + if (connect() != 0) { + ERR("Reconnect failed."); + return -1; + } + return start();; +} + +int CasClient::send(uint8_t *buffer, uint32_t length) +{ + std::lock_guard lock(m_lock); + if (m_casClientSocket == nullptr) { + ERR("client socket is nullptr."); + return -1; + } + return m_casClientSocket->Send(buffer, length); +} + +void CasClient::setListner(CasClientListener *listener) +{ + m_listener = listener; +} + +int CasClient::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; + } else { + if ((ret < 0) && ((0 == errno) || (EAGAIN == errno) || + (EWOULDBLOCK == errno) || (EINTR == errno) || (ETIMEDOUT == errno))) { + return 0; + } else { + return -1; + } + } + } + return readLen; +} + +void CasClient::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) { + ERR("Read message header failed ret = %d headerLength = %d.", ret, headerLength); + usleep(1000); + continue; + } + uint32_t bodyLength = msgHead.GetPayloadSize(); + uint8_t *message = (uint8_t*)AllocBuffer(bodyLength + headerLength); + ret = readN(message + headerLength, bodyLength); + if (ret != bodyLength) { + ERR("Read message body failed ret = %d bodyLength %d type= %d.", ret, bodyLength, msgHead.type); + FreeBuffer(message); + continue; + } + (void)memcpy_s(message, headerLength, &msgHead, headerLength); + dispatchMessage(msgHead, message, bodyLength + headerLength); + } +} + +void CasClient::dispatchMessage(streamMsgHead &header, uint8_t *message, uint32_t length) +{ + if (m_listener != nullptr) { + m_listener->onNewPacket(header.type, message, length); + } +} \ No newline at end of file diff --git a/cloudphone/src/main/cpp/cas_controller/CasClient.h b/cloudphone/src/main/cpp/cas_controller/CasClient.h new file mode 100644 index 0000000..895d5c3 --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasClient.h @@ -0,0 +1,53 @@ +#ifndef CLOUDAPPSDK_CASCLIENT_H +#define CLOUDAPPSDK_CASCLIENT_H + +#include +#include +#include +#include "../cas_socket/CasSocket.h" +#include "../cas_common/CasMsg.h" +#include "CasTcpSocket.h" +#include "CasMright.h" + +class CasClientListener { +public: + CasClientListener() {}; + virtual ~CasClientListener() {}; + virtual void onNewPacket(int type, uint8_t *buffer, uint32_t length) = 0; +}; + +class CasClient { +public: + CasClient(); + ~CasClient(); + int init(uint32_t ip, uint16_t port); + int deinit(); + int connect(); + int disconnect(); + int start(); + int stop(); + int reconnect(); + int send(uint8_t *buffer, uint32_t length); + void setListner(CasClientListener *listener); + +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::mutex m_lock; + std::thread m_task; + CasTcpClientSocket *m_casClientSocket; + CasClientListener *m_listener; + +#if MTRANS_ENABLED + CasMright *m_casMright; +#endif +}; + + +#endif //CLOUDAPPSDK_CASCLIENT_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 0000000..2942f15 --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasCmdHandler.cpp @@ -0,0 +1,60 @@ +#include +#include +#include +#include "CasCmdHandler.h" +#include "../cas_common/CasMsg.h" +#include "../cas_service/CasAppCtrlCmdUtils.h" + +CasCmdHandler::CasCmdHandler(CasMessageResult *result) + :CasMessageHandler(result) +{ + INFO("cmd handler constructor."); +} + +CasCmdHandler::~CasCmdHandler() +{ + INFO("cmd handler deconstructor."); +} + +int CasCmdHandler::start() +{ + INFO("cmd handler start."); + return 0; +} + +int CasCmdHandler::stop() +{ + INFO("cmd handler stop."); + return 0; +} + +void CasCmdHandler::handleMeassage(uint8_t *message, uint32_t length) +{ + INFO("cmd handler handle message."); + + 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); + } +} \ 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 0000000..a5c3562 --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasCmdHandler.h @@ -0,0 +1,21 @@ +#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(CasMessageResult *result); + ~CasCmdHandler(); + + int start() override; + int stop() override; + void handleMeassage(uint8_t *message, uint32_t length) override; + +private: +}; + +#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 0000000..64494c8 --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasCommonHandler.cpp @@ -0,0 +1,36 @@ +#include "CasCommonHandler.h" +#include "../cas_common/CasMsg.h" +#include "../cas_common/CasBuffer.h" + +CasCommonHandler::CasCommonHandler(CasMessageResult *result) + :CasMessageHandler(result) +{ +} + +CasCommonHandler::~CasCommonHandler() +{ +} + +int CasCommonHandler::start() +{ + return 0; +} + +int CasCommonHandler::stop() +{ + return 0; +} + +void CasCommonHandler::handleMeassage(uint8_t *message, uint32_t length) +{ + if (message == nullptr) { + return; + } + 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 0000000..14fb50b --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasCommonHandler.h @@ -0,0 +1,21 @@ +#ifndef CLOUDAPPSDK_CASCOMMONHANDLER_H +#define CLOUDAPPSDK_CASCOMMONHANDLER_H + +#include +#include "../cas_common/CasMsg.h" +#include "CasMessageHandler.h" + +class CasCommonHandler : public CasMessageHandler { +public: + CasCommonHandler(CasMessageResult *result); + ~CasCommonHandler(); + + int start() override; + int stop() override; + void handleMeassage(uint8_t *message, uint32_t length) override; + +private: + +}; + +#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 0000000..2c61eb2 --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasHandlerManager.cpp @@ -0,0 +1,148 @@ +#include "CasHandlerManager.h" +#include "../CasController.h" +#include "CasVideoHandler.h" +#include "CasHandlerManager.h" +#include "CasCmdHandler.h" +#include "CasCommonHandler.h" +#include "CasHearbeatHandler.h" +#include "../cas_common/CasLog.h" + +CasHandlerManager::CasHandlerManager(CasController *casController) +{ + INFO("CasHandlerManager constructor."); + m_casController = casController; +} + +CasHandlerManager::~CasHandlerManager() +{ + INFO("CasHandlerManager deconstructor."); + m_casController = nullptr; +} + +int CasHandlerManager::start() +{ + INFO("CasHandlerManager start."); + for (auto &it : m_handlerMap) { + CasMessageHandler *handler = it.second; + if (handler->start() != 0) { + break; + } + } + return 0; +} + +int CasHandlerManager::stop() +{ + INFO("CasHandlerManager stop."); + for (auto &it : m_handlerMap) { + CasMessageHandler *handler = it.second; + handler->stop(); + } + INFO("CasHandlerManager stop end."); + return 0; +} + +void CasHandlerManager::clear() +{ + INFO("CasHandlerManager clear."); + for (auto &it : m_handlerMap) { + CasMessageHandler *handler = it.second; + } + m_handlerMap.clear(); +} + +int CasHandlerManager::createHandler(CasMsgType type) +{ + CasMessageHandler *handler; + CasVideoHandler *videoHandler; + CasHearbeatHandler *hearbeatHandler; + + int value = 0; + switch (type) { + case CasMsgType::Video: + videoHandler = new (std::nothrow) CasVideoHandler(this); + videoHandler->setParameters(m_casController->m_nativeWindow, + m_casController->m_frameType, + m_casController->m_rotationDegrees); + handler = static_cast(videoHandler); + break; + case CasMsgType::HeartBeat: + hearbeatHandler = new (std::nothrow) CasHearbeatHandler(this); + hearbeatHandler->setParameters(m_casController->m_streamBuildSender); + handler = static_cast(hearbeatHandler); + break; + case CasMsgType::CmdControl: + handler = new (std::nothrow) CasCmdHandler(this); + break; + case CasMsgType::Audio: + case CasMsgType::Channel: + case CasMsgType::Orientation: + case CasMsgType::ImeData: + case CasMsgType::VirtualDevice: + case CasMsgType::ScreenShot: + handler = new (std::nothrow) CasCommonHandler(this); + break; + default: + return -1; + } + addHandler(type, handler); + return 0; +} + +void CasHandlerManager::addHandler(CasMsgType type, CasMessageHandler *handler) +{ + m_handlerMap[type] = handler; +} + +int CasHandlerManager::startVideoHandler() +{ + if (m_handlerMap.count(CasMsgType::Video) == 0) { + return -1; + } + CasVideoHandler *handler = (CasVideoHandler*)m_handlerMap[CasMsgType::Video]; + handler->setParameters(m_casController->m_nativeWindow, + m_casController->m_frameType, + m_casController->m_rotationDegrees); + handler->start(); + return 0; +} + +int CasHandlerManager::stopVideoHandler() +{ + if (m_handlerMap.count(CasMsgType::Video) == 0) { + return -1; + } + CasVideoHandler *handler = (CasVideoHandler*)m_handlerMap[CasMsgType::Video]; + handler->stop(); + return 0; +} + +void CasHandlerManager::onNewPacket(int type, uint8_t *buffer, uint32_t length) +{ + if (m_handlerMap.count(type)) { + CasMessageHandler *handler = m_handlerMap[type]; + handler->handleMeassage(buffer, length); + } else { + delete[] buffer; + } +} + +void CasHandlerManager::onCmdRecv(int code, std::string &message) +{ + if (m_casController != nullptr) { + m_casController->OnCmdRecv(code, message); + } +} +void CasHandlerManager::onMessageRecv(stream_msg_head_t *header, uint8_t *body) +{ + if (m_casController != nullptr) { + m_casController->onMessageRecv(header, body, header->GetPayloadSize()); + } +} + +void CasHandlerManager::onConnectionStatus(bool status) +{ + if (m_casController != nullptr) { + m_casController->SetConnectStatus(status); + } +} \ No newline at end of file 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 0000000..ef87706 --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasHandlerManager.h @@ -0,0 +1,37 @@ +#ifndef CLOUDAPPSDK__CASHANDLERMANAGER_H +#define CLOUDAPPSDK__CASHANDLERMANAGER_H + +#include +#include +#include +#include "CasClient.h" +#include "CasMessageHandler.h" +#include "../CasController.h" + +class CasHandlerManager : public CasClientListener, public CasMessageResult { +public: + CasHandlerManager(CasController *casController); + ~CasHandlerManager(); + + int start(); + int stop(); + void clear(); + int createHandler(CasMsgType type); + int startVideoHandler(); + int stopVideoHandler(); + +public: + void addHandler(CasMsgType type, CasMessageHandler *handler); + +public: + void onNewPacket(int type, uint8_t *buffer, uint32_t length) override; + void onCmdRecv(int code, std::string &msg) override; + void onMessageRecv(stream_msg_head_t *header, uint8_t *body) override; + void onConnectionStatus(bool status) override; + +private: + CasController *m_casController; + std::map m_handlerMap; +}; + +#endif //CLOUDAPPSDK_CASHANDLERMANAGER_H diff --git a/cloudphone/src/main/cpp/cas_controller/CasHearbeatHandler.cpp b/cloudphone/src/main/cpp/cas_controller/CasHearbeatHandler.cpp new file mode 100644 index 0000000..4060d85 --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasHearbeatHandler.cpp @@ -0,0 +1,145 @@ +#include +#include "CasHearbeatHandler.h" +#include "CasLog.h" +#include "../cas_service/CasAppCtrlCmdUtils.h" + +namespace { + const uint64_t MAX_LAGS_NUM = 5; + const uint64_t MAX_LAG = 10 * 1000000; +} + +CasHearbeatHandler::CasHearbeatHandler(CasMessageResult *result) + :CasMessageHandler(result) +{ + m_conditionWait = false; + m_heartbeatTaskRun = false; +} + +CasHearbeatHandler::~CasHearbeatHandler() +{ +} + +int CasHearbeatHandler::start() +{ + m_heartbeatPktStream = new (std::nothrow) CasDataPipe(true); + if (m_heartbeatPktStream == nullptr) { + ERR("create heartbeat pipe failed."); + return -1; + } + m_heartbeatTaskRun = true; + m_heartbeatThread = std::thread(&CasHearbeatHandler::heartbeatThread, this); + return 0; +} + +int CasHearbeatHandler::stop() +{ + if (m_heartbeatTaskRun) { + m_heartbeatTaskRun = false; + m_heartbeatPktStream->Exit(); + interruptSleep(); + m_heartbeatThread.join(); + delete m_heartbeatPktStream; + m_heartbeatPktStream = nullptr; + } + return 0; +} + +void CasHearbeatHandler::handleMeassage(uint8_t *message, uint32_t length) +{ + if (m_heartbeatPktStream != nullptr) { + m_heartbeatPktStream->Handle(message); + } +} + +void CasHearbeatHandler::setParameters(CasStreamBuildSender *streamBuildSender) +{ + m_streamBuildSender = streamBuildSender; +} + +bool CasHearbeatHandler::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; + } + return true; +} + +void CasHearbeatHandler::heartbeatThread() +{ + struct timeval sendtime; + struct timeval recvtime; + int heatbeatFailedCount = 0; + const int maxFailedTimes = 5; + uint64_t lag; + bool isConnect = false; + + while (m_heartbeatTaskRun) { + gettimeofday(&sendtime, nullptr); + bool ret = sendHeartbeat(); + if (!ret) { + heatbeatFailedCount++; + if (heatbeatFailedCount > maxFailedTimes && isConnect) { + isConnect = false; + m_messageResult->onConnectionStatus(false); + ERR("client is disconnect."); + } + lag = MAX_LAG; + } 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->onConnectionStatus(true); + } + } + updateLag(lag); + sleepFor(200000); + } + INFO("HeartbeatThread thread exit."); +} + +void CasHearbeatHandler::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; +} + +void CasHearbeatHandler::sleepFor(const int timeout) +{ + std::unique_lock lock(m_mutex); + m_cv.wait_for(lock, std::chrono::microseconds(timeout), + [this]() { return m_conditionWait; }); +} + +void CasHearbeatHandler::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/CasHearbeatHandler.h b/cloudphone/src/main/cpp/cas_controller/CasHearbeatHandler.h new file mode 100644 index 0000000..30cf19b --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasHearbeatHandler.h @@ -0,0 +1,45 @@ +#ifndef CLOUDAPPSDK_CASHEARBEATHANDLER_H +#define CLOUDAPPSDK_CASHEARBEATHANDLER_H + +#include +#include +#include +#include +#include +#include "CasMessageHandler.h" +#include "CasClient.h" +#include "CasStreamBuildSender.h" +#include "../cas_service/CasDataPipe.h" + +class CasHearbeatHandler : public CasMessageHandler{ +public: + CasHearbeatHandler(CasMessageResult *result); + ~CasHearbeatHandler(); + int start() override; + int stop() override; + void handleMeassage(uint8_t *message, uint32_t length) override; + void setParameters(CasStreamBuildSender *streamBuildSender); + uint64_t getLag() { + return m_lag; + } + +private: + void heartbeatThread(); + bool sendHeartbeat(); + void sleepFor(int ms); + void interruptSleep(); + void updateLag(uint64_t lag); + +private: + uint64_t m_lag = 0; + bool m_heartbeatTaskRun; + std::thread m_heartbeatThread; + CasStreamBuildSender *m_streamBuildSender; + std::deque m_lagDeque; + std::mutex m_mutex; + std::condition_variable m_cv; + bool m_conditionWait; + CasDataPipe *m_heartbeatPktStream; +}; + +#endif //CLOUDAPPSDK_CASHEARBEATHANDLER_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 0000000..1c0a4cd --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasMessageHandler.h @@ -0,0 +1,31 @@ +#ifndef CLOUDAPPSDK_CASMESSAGEHANDLER_H +#define CLOUDAPPSDK_CASMESSAGEHANDLER_H + +#include +#include +#include "CasMsg.h" + +class CasMessageResult { +public: + CasMessageResult() = default; + virtual ~CasMessageResult() {} + virtual void onCmdRecv(int code, std::string &msg) = 0; + virtual void onMessageRecv(stream_msg_head_t *header, uint8_t *body) = 0; + virtual void onConnectionStatus(bool status) = 0; +}; + +class CasMessageHandler { +public: + CasMessageHandler(CasMessageResult *result) { + m_messageResult = result; + } + virtual ~CasMessageHandler() {} + virtual int start() = 0; + virtual int stop() = 0; + virtual void handleMeassage(uint8_t *message, uint32_t length) = 0; + +protected: + CasMessageResult *m_messageResult; +}; + +#endif //CLOUDAPPSDK_CASMESSAGEHANDLER_H diff --git a/cloudphone/src/main/cpp/cas_controller/CasMright.cpp b/cloudphone/src/main/cpp/cas_controller/CasMright.cpp new file mode 100644 index 0000000..5b2424d --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasMright.cpp @@ -0,0 +1,119 @@ +#include "CasMright.h" +#include "CasLog.h" + +static int32_t OnRecvVideoStreamData(uint8_t*, uint32_t); +static int32_t OnRecvAudioStreamData(uint8_t*, uint32_t); +static int32_t OnRecvAudioDecodeCallback(int32_t, AudioJbDecode*); +static int32_t OnRecvCmdData(uint8_t*, uint32_t); +static void OnGotTransLog(const char*, uint32_t length); +static void OnNeedKeyFrameCallback(); + + +CasMright::CasMright() +{ + INFO("CasMright constructor."); + m_mtrans = nullptr; + m_listener = nullptr; +} + +CasMright::~CasMright() +{ + INFO("CasMright deconstructor."); + m_mtrans = nullptr; +} + +bool CasMright::init(std::string &ip, uint16_t port) +{ + INFO("CasMright init ip = %s port = %d.", ip.c_str(), port); + m_mtrans = new (std::nothrow) NetTrans(); + if (m_mtrans == nullptr) { + ERR("Create mtrans instance failed."); + return false; + } + + TransConfigParam param; + param.minVideoSendBitrate = 500; + param.maxVideoSendBitrate = 10000; + + m_mtrans->Init(PEER_CLIENT, ip.c_str(), port, param, + OnRecvVideoStreamData, + OnRecvAudioStreamData, + nullptr, + OnNeedKeyFrameCallback, + OnRecvCmdData, + OnRecvAudioDecodeCallback, + OnGotTransLog); + return true; +} + +bool CasMright::deinit() +{ + INFO("CasMright deinit."); + if (m_mtrans != nullptr) { + delete m_mtrans; + m_mtrans = nullptr; + } + return true; +} + +bool CasMright::start() +{ + INFO("CasMright start."); + if (m_mtrans == nullptr) { + ERR("Mtrans instance is null."); + return false; + } + int ret = m_mtrans->Start(); + if (ret < 0) { + ERR("Mtrans start failed."); + } else { + INFO("Mtrans start success."); + } + return (ret >= 0); +} + +bool CasMright::stop() +{ + INFO("CasMright stop."); + if (m_mtrans == nullptr) { + ERR("Mtrans instance is null."); + return false; + } + m_mtrans->Stop(); + return true; +} + +void CasMright::setListener(CasClientListener *listener) +{ + m_listener = listener; +} + +int32_t OnRecvVideoStreamData(uint8_t* data, uint32_t length) +{ + return 0; +} + +int32_t OnRecvAudioStreamData(uint8_t* data, uint32_t length) +{ + return 0; +} + +int32_t OnRecvAudioDecodeCallback(int32_t streamId, AudioJbDecode* audioJbDecode) +{ + return 0; +} + +void OnGotTransLog(const char* str, uint32_t length) +{ +} + +void OnNeedKeyFrameCallback() +{ + INFO("Need key frame callback.."); +} + +int32_t OnRecvCmdData(uint8_t* data, uint32_t length) +{ + INFO("OnRecvCmdData"); + return 0; +} \ No newline at end of file diff --git a/cloudphone/src/main/cpp/cas_controller/CasMright.h b/cloudphone/src/main/cpp/cas_controller/CasMright.h new file mode 100644 index 0000000..a1f5450 --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasMright.h @@ -0,0 +1,26 @@ +#ifndef CLOUDAPPSDK_CASMRIGHT_H +#define CLOUDAPPSDK_CASMRIGHT_H + +#include +#include "net_trans.h" + +class CasClientListener; + +class CasMright { +public: + CasMright(); + ~CasMright(); + + bool init(std::string &ip, uint16_t port); + bool deinit(); + bool start(); + bool stop(); + bool isOk(); + void setListener(CasClientListener *listener); + +private: + NetTrans *m_mtrans; + CasClientListener *m_listener; +}; + +#endif //CLOUDAPPSDK_CASMRIGHT_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 0000000..83c86f1 --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.cpp @@ -0,0 +1,78 @@ +#include +#include "CasVideoHandler.h" +#include "../cas_decoder/CasVideoEngineCommon.h" +#include "../cas_common/CasMsg.h" +#include "../cas_common/CasBuffer.h" + +#define DIRECT_DECODE 0 + +CasVideoHandler::CasVideoHandler(CasMessageResult *result) + :CasMessageHandler(result) +{ + INFO("CasVideoHandler constructor."); + m_videoPktStream = nullptr; + m_nativeWindow = nullptr; + m_frameType = FrameType::H264; + m_rotation = 0; +} + +CasVideoHandler::~CasVideoHandler() +{ + INFO("CasVideoHandler deconstructor."); + stop(); +} + +int CasVideoHandler::start() +{ + INFO("Start."); + std::lock_guard lockGuard(m_lock); + m_videoThread = + std::make_shared(m_nativeWindow, m_frameType, m_rotation); + if (m_videoThread == nullptr) { + ERR("Couldn't create CasVideoHDecodeThread."); + return -1; + } + m_videoPktStream =std::make_shared(true); + if (m_videoThread == nullptr) { + ERR("Couldn't create CasDataPipe."); + m_videoThread = nullptr; + return -1; + } + m_videoThread->SetDecodePktHandle(m_videoPktStream.get()); + if (m_videoThread->Start() != 0) { + m_videoThread = nullptr; + m_videoPktStream = nullptr; + } + return 0; +} + +int CasVideoHandler::stop() +{ + INFO("Stop."); + std::lock_guard lockGuard(m_lock); + if (m_videoThread == nullptr) { + ERR("Already stop."); + return -1; + } + m_videoThread->Exit(); + m_videoThread = nullptr; + m_videoPktStream = nullptr; + return 0; +} + +void CasVideoHandler::handleMeassage(uint8_t *message, uint32_t length) +{ + std::lock_guard lockGuard(m_lock); + if (m_videoPktStream != nullptr) { + m_videoPktStream->Handle(message); + } +} + +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; +} 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 0000000..5150e9d --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.cpp+++ @@ -0,0 +1,129 @@ +#include +#include "CasVideoHandler.h" +#include "../cas_decoder/CasVideoEngineCommon.h" +#include "../cas_common/CasMsg.h" +#include "../cas_common/CasBuffer.h" + +#define DIRECT_DECODE 0 + +CasVideoHandler::CasVideoHandler(CasMessageResult *result) + :CasMessageHandler(result) +{ + INFO("video handler constructor."); + m_decodeTaskRun = false; + m_rotation = 0; + m_videoPktStream = nullptr; + m_videoEngine = nullptr; + m_nativeWindow = nullptr; +} + +CasVideoHandler::~CasVideoHandler() +{ + INFO("video handler deconstructor."); + stop(); +} + +int CasVideoHandler::start() +{ + INFO("Strat video handler."); + m_videoEngine = new (std::nothrow) CasVideoEngine(); + m_videoPktStream = new (std::nothrow) CasDataPipe(true); + if (m_videoEngine == nullptr || m_videoPktStream == nullptr) { + ERR("create video instance failed, m_videoEngine = %p m_videoPktStream = %p", + m_videoEngine, m_videoPktStream); + goto FAILED; + } + m_videoEngine->InitDecoder(m_nativeWindow, DecoderType::DECODER_TYPE_HW, + m_frameType, m_rotation); + m_videoEngine->StartDecoder(); + m_decodeTaskRun = true; + m_decodeThread = std::thread(&CasVideoHandler::decodeThread, this); + return 0; + +FAILED: + if (m_videoEngine != nullptr) { + delete m_videoEngine; + m_videoEngine = nullptr; + } + if (m_videoPktStream == nullptr) { + delete m_videoPktStream; + m_videoPktStream = nullptr; + } + return -1; +} + +int CasVideoHandler::stop() +{ + INFO("Stop video handler."); + if (m_decodeTaskRun) { + m_decodeTaskRun = false; + m_videoPktStream->Exit(); + m_decodeThread.join(); + } + if (m_videoEngine != nullptr) { + m_videoEngine->StopDecoder(); + m_videoEngine->DestroyDecoder(); + delete m_videoEngine; + m_videoEngine = nullptr; + } + if (m_videoPktStream != nullptr) { + delete m_videoPktStream; + m_videoPktStream = nullptr; + } + return 0; +} + +void CasVideoHandler::handleMeassage(uint8_t *message, uint32_t length) +{ + if (DIRECT_DECODE) { + stream_msg_head_t *header = (stream_msg_head_t *) message; + uint32_t datasize = header->GetPayloadSize(); + uint8_t *videoData = message + sizeof(stream_msg_head_t); + int ret = m_videoEngine->DecodeFrame(videoData, datasize); + if (ret == VIDEO_ENGINE_CLIENT_DECODE_ERR) { + ERR("video engine decode error."); + } else if (ret == VIDEO_ENGINE_CLIENT_PARAM_INVALID) { + ERR("video engine params invalid."); + } + FreeBuffer(message); + } else { + if (m_videoPktStream != nullptr) { + m_videoPktStream->Handle(message); + } + } +} + +void CasVideoHandler::setParameters(ANativeWindow *nativeWindow, FrameType frameType, int rotation) +{ + m_nativeWindow = nativeWindow; + m_frameType = frameType; + m_rotation = rotation; +} + +void CasVideoHandler::decodeThread() +{ + const int timeout = 10; // 5毫秒 + uint8_t *onePkt; + while (m_decodeTaskRun) { + onePkt = (uint8_t*)m_videoPktStream->GetNextPktWaitFor(timeout); + if (onePkt == nullptr) { + //WARN("video package is empty"); + continue; + } + stream_msg_head_t *header = (stream_msg_head_t *) onePkt; + uint32_t datasize = header->GetPayloadSize(); + uint8_t *videoData = onePkt + sizeof(stream_msg_head_t); + + int ret = 0; + do { + ret = m_videoEngine->DecodeFrame(videoData, datasize); + if (ret == VIDEO_ENGINE_CLIENT_DECODE_ERR) { + ERR("video engine decode error."); + } else if (ret == VIDEO_ENGINE_CLIENT_PARAM_INVALID) { + ERR("video engine params invalid."); + } + } while(ret != SUCCESS && m_decodeTaskRun); + + FreeBuffer(onePkt); + } +} 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 0000000..c2ce50d --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.h @@ -0,0 +1,36 @@ +#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: + CasVideoHandler(CasMessageResult *result); + ~CasVideoHandler(); + + int start() override; + int stop() override; + void handleMeassage(uint8_t *message, uint32_t length) override; + void setParameters(ANativeWindow *nativeWindow, FrameType frameType, int rotation); + +private: + void decodeThread(); + +private: + std::shared_ptr m_videoThread; + std::shared_ptr 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/CasVideoHandler.h+++ b/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.h+++ new file mode 100644 index 0000000..f8720e8 --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.h+++ @@ -0,0 +1,35 @@ +#ifndef CLOUDAPPSDK_CASVIDEOHANDLER_H +#define CLOUDAPPSDK_CASVIDEOHANDLER_H + +#include +#include +#include +#include "CasMessageHandler.h" +#include "../cas_decoder/CasVideoEngineCommon.h" +#include "../cas_service/CasDataPipe.h" +#include "../cas_decoder/CasVideoEngine.h" + +class CasVideoHandler : public CasMessageHandler{ +public: + CasVideoHandler(CasMessageResult *result); + ~CasVideoHandler(); + + int start() override; + int stop() override; + void handleMeassage(uint8_t *message, uint32_t length) override; + void setParameters(ANativeWindow *nativeWindow, FrameType frameType, int rotation); + +private: + void decodeThread(); + +private: + int m_rotation; + CasDataPipe *m_videoPktStream; + CasVideoEngine *m_videoEngine; + ANativeWindow *m_nativeWindow; + FrameType m_frameType; + bool m_decodeTaskRun; + std::thread m_decodeThread; +}; + +#endif //CLOUDAPPSDK_CASVIDEOHANDLER_H diff --git a/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.cpp b/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.cpp index 9167fa7..66aea83 100644 --- a/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.cpp +++ b/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.cpp @@ -12,11 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#if __ANDROID_API__ >= 24 -#include -#include -#endif - #include "CasDecodeController.h" #include "CasVideoUtil.h" @@ -24,11 +19,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,9 +66,10 @@ void CasDecodeController::ClearDiscardFrameCache() m_HoldFrames = 0; } -void FrameCallback(long frameTime, void* data) +void FrameCallback(long frameTime, void* opaque) { - if (!g_controller->IsStatus(EngineStat::ENGINE_RUNNING)) { + CasDecodeController *controller = (CasDecodeController *)opaque; + if (!controller->IsStatus(EngineStat::ENGINE_RUNNING)) { return; } @@ -90,16 +83,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,39 +101,39 @@ 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(); @@ -163,9 +156,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 +170,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 +189,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; @@ -255,16 +215,16 @@ void OutputTaskEntry(CasDecodeController *controller) return; } -#if __ANDROID_API__ >= 24 - g_controller = controller; +#if 0//__ANDROID_API__ >= 24 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(); + ERR("cxchun controller = %p controller->m_choreographer %p", controller, controller->m_choreographer); + AChoreographer_postFrameCallback(controller->m_choreographer, FrameCallback, (void *)controller); while (controller->IsStatus(EngineStat::ENGINE_RUNNING)) { int id; @@ -289,7 +249,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; diff --git a/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.h b/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.h index a93fc76..3552288 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 @@ -180,26 +185,14 @@ public: uint32_t m_HoldFrames = 0; 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 a3d49e2..0fe4183 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 18a954e..ac0e2e2 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/CasDataPipe.cpp b/cloudphone/src/main/cpp/cas_service/CasDataPipe.cpp index d3091a2..deb34c1 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,10 @@ void CasDataPipe::Clear() noexcept void CasDataPipe::Exit() { - this->m_status = false; - unique_lock lck(this->m_lock); + { + lock_guard lck(this->m_lock); + this->m_status = false; + } 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 549c8c5..7b670ae 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(CasClient *client) { - Init(casSocket); + Init(client); } CasTouch::~CasTouch() @@ -26,9 +26,9 @@ CasTouch::~CasTouch() delete m_streamBuildSender; } -void CasTouch::Init(CasSocket *casSocket) +void CasTouch::Init(CasClient *client) { - m_streamBuildSender = new CasStreamBuildSender(casSocket); + m_streamBuildSender = new CasStreamBuildSender(client); } 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 c12ec48..b89a3d0 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(CasClient *client); ~CasTouch(); - void Init(CasSocket *casSocket); + void Init(CasClient *client); 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 ab30451..b4b77d4 100644 --- a/cloudphone/src/main/cpp/cas_service/CasVideoHDecodeThread.cpp +++ b/cloudphone/src/main/cpp/cas_service/CasVideoHDecodeThread.cpp @@ -26,10 +26,13 @@ class CasFirstVideoFrameImpl : public CasFirstVideoFrameListener { public: void OnFirstFrame() override { +#if 0 // cxchun if (CasController::GetInstance() != nullptr) { CasController::GetInstance()->NotifyFirstVideoFrame(); } +#endif } + }; class CasVideoDecodeStatImpl : public CasVideoDecodeStatListener { @@ -40,7 +43,8 @@ public: } }; -CasVideoHDecodeThread::CasVideoHDecodeThread(ANativeWindow *nativeWindow, FrameType frameType, int rotationDegrees) +CasVideoHDecodeThread:: +CasVideoHDecodeThread(ANativeWindow *nativeWindow, FrameType frameType, int rotationDegrees) { this->m_nativeWindow = nativeWindow; this->m_videoEngine = nullptr; diff --git a/cloudphone/src/main/cpp/cas_socket/CasTcpClientSocket.cpp b/cloudphone/src/main/cpp/cas_socket/CasTcpClientSocket.cpp index 6e20589..50ea560 100644 --- a/cloudphone/src/main/cpp/cas_socket/CasTcpClientSocket.cpp +++ b/cloudphone/src/main/cpp/cas_socket/CasTcpClientSocket.cpp @@ -219,3 +219,12 @@ int CasTcpClientSocket::Reconnect() return this->Connect(); } + +void CasTcpClientSocket::Close() +{ + if (-1 != m_fd) { + shutdown(m_fd, SHUT_RDWR); + close(m_fd); + m_fd = -1; + } +} \ No newline at end of file diff --git a/cloudphone/src/main/cpp/cas_socket/CasTcpSocket.h b/cloudphone/src/main/cpp/cas_socket/CasTcpSocket.h index 5096052..b09af3b 100644 --- a/cloudphone/src/main/cpp/cas_socket/CasTcpSocket.h +++ b/cloudphone/src/main/cpp/cas_socket/CasTcpSocket.h @@ -58,6 +58,8 @@ public: int CreateSocket(); + void Close(); + private: int CasCreateTcpClient(uint32_t socketOption, uint32_t remoteIp, uint16_t remotePort, uint32_t localIp, uint16_t localPort); diff --git a/cloudphone/src/main/cpp/cas_stream/CMakeLists.txt b/cloudphone/src/main/cpp/cas_stream/CMakeLists.txt index 30e9046..99929d9 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 cb201a2..84cf51b 100644 --- a/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp +++ b/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp @@ -19,14 +19,14 @@ #include "CasMsg.h" #include "CasStreamBuildSender.h" -CasStreamBuildSender::CasStreamBuildSender(CasSocket *socket) +CasStreamBuildSender::CasStreamBuildSender(CasClient *client) { - this->m_socket = socket; + this->m_client = client; } CasStreamBuildSender::~CasStreamBuildSender() { - this->m_socket = nullptr; + this->m_client = nullptr; } void CasStreamBuildSender::SetNetTrans(NetTrans *netTrans) @@ -36,8 +36,8 @@ void CasStreamBuildSender::SetNetTrans(NetTrans *netTrans) 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_client == nullptr) { + ERR("Failed to send data, client is null."); return -1; } @@ -118,10 +118,10 @@ 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); + ssize_t stat = m_client->send((uint8_t*)outBuffer + pos, dataLen - pos); -#if MTRANS_ENABLED - if (m_mtrans != nullptr && type == HeartBeat && m_socket->GetStatus() == SOCKET_STATUS_RUNNING) { +#if 0 //MTRANS_ENABLED + if (m_mtrans != nullptr && type == HeartBeat && m_client->GetStatus() == SOCKET_STATUS_RUNNING) { m_mtrans->SendCmdData(reinterpret_cast(outBuffer + pos), dataLen - pos); } #endif diff --git a/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.h b/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.h index 5c8c40f..ecf5bd9 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/CasClient.h" class CasStreamBuildSender { public: - explicit CasStreamBuildSender(CasSocket *socket); + explicit CasStreamBuildSender(CasClient *client); ~CasStreamBuildSender(); @@ -30,7 +31,7 @@ public: int SendDataToServer(CasMsgType type, const void *buf, size_t len); private: - CasSocket *m_socket = nullptr; + CasClient *m_client = nullptr; NetTrans *m_mtrans = nullptr; }; diff --git a/cloudphone/src/main/cpp/cas_stream/CasStreamRecvParser.cpp b/cloudphone/src/main/cpp/cas_stream/CasStreamRecvParser.cpp index a47fd28..7de5128 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,6 +69,7 @@ CasStreamParseThread::~CasStreamParseThread() this->m_streamRecvParser = nullptr; } +#if 0 //cxchun int FastDecodeFrame(uint8_t *buf, size_t length) { CasController *casController = CasController::GetInstance(); @@ -110,6 +89,7 @@ int FastDecodeFrame(uint8_t *buf, size_t length) } return engine->DecodeFrame(buf, length); } +#endif void HandleCompletePktMsg(CasStreamParseThread *streamParseThread, streamMsgHead *msgHead, unsigned char *recvBuf, unsigned int pktStartPos) @@ -120,19 +100,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 a297140..2959f5f 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 0000000..2361971 --- /dev/null +++ b/cloudphone/src/main/java/com/huawei/cloudphone/api/CloudAppScreenshotListener.java @@ -0,0 +1,5 @@ +package com.huawei.cloudphone.api; + +public interface CloudAppScreenshotListener { + void onRecvPicture(byte[] picture); +} 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 e5c43cb..5dd26b1 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/ICloudPhone.java b/cloudphone/src/main/java/com/huawei/cloudphone/api/ICloudPhone.java index 016d07f..9c301e2 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/api/ICloudPhone.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/api/ICloudPhone.java @@ -62,6 +62,16 @@ public interface ICloudPhone { */ void exitCloudPhone() throws Exception; + /** + * 开始截图 + */ + void startScreenshot(final Map params, CloudAppScreenshotListener captureListener); + + /** + * 停止截图 + */ + void stopScreenshot(); + /** * 设置音视频参数 * 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 51a2067..64b2364 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImpl.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneImpl.java @@ -37,13 +37,13 @@ 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; import android.widget.Toast; import com.huawei.cloudphone.BuildConfig; +import com.huawei.cloudphone.api.CloudAppScreenshotListener; import com.huawei.cloudphone.api.CloudAppDataListener; import com.huawei.cloudphone.api.CloudPhoneClipboardListener; import com.huawei.cloudphone.api.CloudPhoneOrientationChangeListener; @@ -93,6 +93,8 @@ public class CloudPhoneImpl implements ICloudPhone { private static final int CMD_BACKGROUND_TIMEOUT = 15; private static final int CMD_RECONNECT = 16; private static final int CMD_SET_MEDIA_CONFIG = 17; + private static final byte CMD_START_SCREENSHOT = 18; + private static final byte CMD_STOP_SCREENSHOT = 19; private static final byte CUSTOM_DATA = 18; private static final byte IME_DATA =14; public static final int RECONNECT_RETRY_MAX_COUNT = 10; @@ -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,9 @@ public class CloudPhoneImpl implements ICloudPhone { private CloudPhoneImeMgr mImeMgr; //virtual devices - VirtualDeviceSession mVirtualDeviceSession = null; - RingBufferVirtualDeviceIO mRingBufferVirtualDeviceIO = null; + private VirtualDeviceSession mVirtualDeviceSession = null; + private RingBufferVirtualDeviceIO mRingBufferVirtualDeviceIO = null; + private CloudAppScreenshotListener mSnapshotListener = null; public CloudPhoneImpl() { mCurrentState = STATE_DEINIT; @@ -268,6 +270,23 @@ public class CloudPhoneImpl implements ICloudPhone { } } + @Override + public void startScreenshot(final Map params, + CloudAppScreenshotListener captureListener) { + mSnapshotListener = captureListener; + Message msg = new Message(); + msg.what = CMD_START_SCREENSHOT; + msg.obj = params; + mCmdHandler.sendMessage(msg); + } + + @Override + public void stopScreenshot() { + Message msg = new Message(); + msg.what = CMD_STOP_SCREENSHOT; + mCmdHandler.sendMessage(msg); + } + @Override public void setMediaConfig(HashMap mediaConfig) { if (mCmdHandler == null) { @@ -389,17 +408,13 @@ public class CloudPhoneImpl implements ICloudPhone { } public void handleStartCmd(final Activity activity, final ViewGroup views) { - if (null == activity) { - CASLog.e(TAG, "handle start cmd fail, activity is null."); - return; - } if (mCurrentState != STATE_INIT) { CASLog.e(TAG, "mCurrentState = " + mCurrentState); return; } CASLog.i(TAG, "handleStartCmd start"); mCurrentState = STATE_STARTING; - //创建SurfaceView + // 创建SurfaceView if (mDisplayMode == DISPLAY_MODE_FILL) { mSurfaceView = new PhoneView(activity, true); } else { @@ -596,8 +611,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 +772,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++; @@ -802,6 +813,25 @@ public class CloudPhoneImpl implements ICloudPhone { } } + private void handleStartCapture(Message message) { + Map parameters = (Map)message.obj; + mStartParas = parameters; + mConnectorInfo = new CasConnectorInfo(); + if (!mConnectorInfo.initConnectorParams(mStartParas)) { + CASLog.e(TAG, "Init connector params failed."); + throw new IllegalArgumentException("Init connector params failed."); + } + mProccessor.setCasConnectorInfo(mConnectorInfo); + mProccessor.startJniRecv(); + mProccessor.startCapture(); + } + + private void handleStopCapture(Message message) { + mProccessor.stopCapture(); + mProccessor.stopJniRecv(); + mSnapshotListener = null; + } + private boolean sendTouchEvent(int id, int action, final int x1, final int y1, final int pressure, long time) { int orientation = 0; int newOrientation = 0; @@ -910,6 +940,14 @@ public class CloudPhoneImpl implements ICloudPhone { CASLog.i(TAG, "CMD_RECONNECT"); handleReconnectCmd(); break; + case CMD_START_SCREENSHOT: + CASLog.i(TAG, "CMD_STARTCAPTURE"); + handleStartCapture(msg); + break; + case CMD_STOP_SCREENSHOT: + CASLog.i(TAG, "CMD_STOPCAPTURE"); + handleStopCapture(msg); + break; default: break; } @@ -957,6 +995,12 @@ public class CloudPhoneImpl implements ICloudPhone { mImeMgr.processImeMsg(data); } } + + public void onScreenCaptureRecv(byte[] data) throws RemoteException { + if (mSnapshotListener != null) { + mSnapshotListener.onRecvPicture(data); + } + } } private class BackgroundTimerTask extends TimerTask { 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 76013a2..2c0034e 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 1120b55..0000000 --- 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 33787f1..0090f1f 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,67 @@ 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() { + mNativeObject = init(); + } + + public void finalize() { + deinit(mNativeObject); + } + + public long getNativeObj() { + return mNativeObject; + } + + public native long init(); + + 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 void pause(long nativeObject); - public static native void setJniConf(String key, String value); + public native void 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 7c0ee2e..3a54b85 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,96 @@ import android.view.Surface; import java.util.HashMap; public class JniBridge { - static JniBridge g_jniBridge = null; + private JNIWrapper mJniWrapper; + private long mNativeObject; - public static synchronized JniBridge getInstance() { - if (g_jniBridge == null) { - g_jniBridge = new JniBridge(); - } - return g_jniBridge; - } - - private JniBridge() { + public JniBridge() { + mJniWrapper = new JNIWrapper(); + mNativeObject = mJniWrapper.getNativeObj(); } public int sendData(byte type, byte[] data, int length) { - return JNIWrapper.sendData(type, data, length); - } - - public int recvData(byte type, byte[] data, int length) { - return JNIWrapper.recvData(type, data, length); + 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) { + 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); + 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); + return mJniWrapper.sendMotionEvent(mNativeObject, masterAxis, masterValue, secondaryAxis, secondaryValue); } public void setJniConf(String key, String value) { - JNIWrapper.setJniConf(key, value); + mJniWrapper.setJniConf(mNativeObject, key, value); } public boolean start(Surface surface, boolean isHome) { - return JNIWrapper.start(surface, isHome); + return mJniWrapper.start(mNativeObject, surface, isHome); } public void stop(boolean isHome) { - JNIWrapper.stop(isHome); + mJniWrapper.stop(mNativeObject, isHome); + } + + public void pause() { + mJniWrapper.pause(mNativeObject); + } + + public void resume(Surface surface) { + mJniWrapper.resume(mNativeObject, surface); + } + + public boolean startScreenshot() { + return mJniWrapper.startScreenshot(mNativeObject); + } + + public void stopScreenshot() { + mJniWrapper.stopScreenshot(mNativeObject); } public boolean reconnect() { - return JNIWrapper.reconnect(); + return mJniWrapper.reconnect(mNativeObject); } public int getJniStatus() { - return JNIWrapper.getJniStatus(); + return mJniWrapper.getJniStatus(mNativeObject); } public boolean getConnectStatus() { - return JNIWrapper.getConnectStatus(); + return mJniWrapper.getConnectStatus(mNativeObject); } - public void registerCasJNICallback(Object obj) { - JNIWrapper.registerCasJNICallback(obj); + public void registerCasJNICallback(Object callback) { + mJniWrapper.registerCasJNICallback(mNativeObject, callback); } public int getLag() { - return JNIWrapper.getLag(); + return mJniWrapper.getLag(mNativeObject); } public String getVideoStreamStats() { - return JNIWrapper.getVideoStreamStats(); + return mJniWrapper.getVideoStreamStats(mNativeObject); } public String getSimpleStreamStats() { - return JNIWrapper.getSimpleStreamStats(); + return mJniWrapper.getSimpleStreamStats(mNativeObject); } - public boolean setMediaConfig(HashMap mediaConfigMap) { if (mediaConfigMap != null) { - return JNIWrapper.setMediaConfig(mediaConfigMap); + return mJniWrapper.setMediaConfig(mNativeObject, mediaConfigMap); } else { return false; } } public boolean setRotation(int rotation) { - return JNIWrapper.setRotation(rotation); + return mJniWrapper.setRotation(mNativeObject, rotation); } - - - } diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JniMessageListener.java b/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JniMessageListener.java new file mode 100644 index 0000000..1a97996 --- /dev/null +++ b/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JniMessageListener.java @@ -0,0 +1,10 @@ +package com.huawei.cloudphone.jniwrapper; + +public interface JniMessageListener { + void onGetAudioMessage(byte[] data, int length); + void onGetMessage(byte[] data, int length); + void onGetImeDataMessage(byte[] data, int length); + void onGetDataChannelMessage(byte[] data, int length); + void onGetVirtualMessage(byte[] data, int length); + void onGetCmdListener(byte[] data, int length); +} 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 c640d8d..bdbd08e 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 1967f41..3e00f51 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/service/CasProcessor.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/service/CasProcessor.java @@ -22,8 +22,6 @@ 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; @@ -62,21 +60,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 */ @@ -86,13 +74,18 @@ public class CasProcessor { * listener */ private CloudPhoneImpl.CASListener mListener = null; - private NewRotationDirectionPacket mNewRotationDirPkt; - private NewVirtualDevDataPacket mVirtualDevDataPkt; - private NewImeDataPacket mImeDataPkt; + private boolean mJniCallback; + private Object mJniCallbackLock = new Object(); + private JniBridge mJniBridge; + + public CasProcessor() { + mJniBridge = new JniBridge(); + mJniCallback = false; + } public void 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,43 @@ 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.VIRTUAL_DEVICE_DATA: + mListener.onVirtualDevDataRecv(data); + break; + case JNIWrapper.SCREENSHOT: + mListener.onScreenCaptureRecv(data); + break; + } + } + } }; - JniBridge.getInstance().registerCasJNICallback(interactiveStateCallback); + mJniBridge.registerCasJNICallback(casCallback); } 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,35 +142,48 @@ 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; } @@ -163,89 +199,76 @@ 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(); + 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 +276,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 +323,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."); - } - } - } - } } -- Gitee From 37fd2a00e34560b5db208c51c861af91fa71a25a Mon Sep 17 00:00:00 2001 From: CaiFeng <2397707574@qq.com> Date: Thu, 26 Sep 2024 15:36:20 +0800 Subject: [PATCH 2/3] =?UTF-8?q?AndroidSDK=E6=94=AF=E6=8C=81=E6=88=AA?= =?UTF-8?q?=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/proguard-rules.pro | 1 + .../common/CasImageQualityManager.java | 50 ++++ .../ui/activity/CasCloudPhoneActivity.java | 30 +-- .../ui/fragment/home/PhoneListFragment.java | 6 +- cloudphone/proguard-rules.pro | 1 + cloudphone/src/main/cpp/CasController.cpp | 143 ++++++---- cloudphone/src/main/cpp/CasController.h | 8 +- cloudphone/src/main/cpp/CasJniBridge.cpp | 29 +- cloudphone/src/main/cpp/CasJniBridge.h | 6 +- .../cpp/cas_controller/CasAudioHandler.cpp | 104 ++++++++ .../main/cpp/cas_controller/CasAudioHandler.h | 39 +++ .../src/main/cpp/cas_controller/CasClient.cpp | 250 ++++++++++-------- .../src/main/cpp/cas_controller/CasClient.h | 56 ++-- .../main/cpp/cas_controller/CasCmdHandler.cpp | 29 +- .../main/cpp/cas_controller/CasCmdHandler.h | 19 +- .../cpp/cas_controller/CasCommonHandler.cpp | 21 +- .../cpp/cas_controller/CasCommonHandler.h | 19 +- .../cpp/cas_controller/CasHandlerManager.cpp | 76 +++++- .../cpp/cas_controller/CasHandlerManager.h | 27 +- .../cpp/cas_controller/CasHearbeatHandler.cpp | 37 ++- .../cpp/cas_controller/CasHearbeatHandler.h | 19 +- .../cpp/cas_controller/CasMessageHandler.h | 20 +- .../src/main/cpp/cas_controller/CasMright.cpp | 238 +++++++++++++++-- .../src/main/cpp/cas_controller/CasMright.h | 41 ++- .../src/main/cpp/cas_controller/CasTcp.cpp | 188 +++++++++++++ .../src/main/cpp/cas_controller/CasTcp.h | 56 ++++ .../cpp/cas_controller/CasVideoHandler.cpp | 76 ++++-- .../cpp/cas_controller/CasVideoHandler.cpp+++ | 129 --------- .../main/cpp/cas_controller/CasVideoHandler.h | 29 +- .../cpp/cas_controller/CasVideoHandler.h+++ | 35 --- .../cpp/cas_controller/ICasTransmission.h | 50 ++++ .../cpp/cas_decoder/CasDecodeController.cpp | 15 +- .../cpp/cas_decoder/CasDecodeController.h | 1 + .../cpp/cas_service/CasVideoHDecodeThread.cpp | 38 +-- .../cpp/cas_service/CasVideoHDecodeThread.h | 6 +- .../cpp/cas_socket/CasTcpClientSocket.cpp | 9 - .../src/main/cpp/cas_socket/CasTcpSocket.h | 2 - .../cpp/cas_stream/CasStreamBuildSender.cpp | 7 - .../cpp/cas_stream/CasStreamRecvParser.cpp | 22 -- .../api/CloudAppScreenshotListener.java | 14 + .../huawei/cloudphone/api/ICloudPhone.java | 10 - .../cloudphone/api/ICloudPhoneScreenshot.java | 28 ++ .../cloudphone/apiimpl/CloudPhoneImpl.java | 86 ++---- .../apiimpl/CloudPhoneScreenshotImpl.java | 242 +++++++++++++++++ .../audio/AudioTrackerCallback.java | 4 + .../datacenter/CasRecvPktDispatcher.java | 130 +++++++++ .../cloudphone/jniwrapper/JNIWrapper.java | 15 +- .../cloudphone/jniwrapper/JniBridge.java | 95 ++++++- .../cloudphone/service/CasProcessor.java | 58 ++-- .../service/CasProcessorListener.java | 17 ++ .../common/RingBufferVirtualDeviceIO.java | 7 +- 51 files changed, 1930 insertions(+), 708 deletions(-) create mode 100644 app/src/main/java/com/huawei/cloudapp/common/CasImageQualityManager.java create mode 100644 cloudphone/src/main/cpp/cas_controller/CasAudioHandler.cpp create mode 100644 cloudphone/src/main/cpp/cas_controller/CasAudioHandler.h create mode 100644 cloudphone/src/main/cpp/cas_controller/CasTcp.cpp create mode 100644 cloudphone/src/main/cpp/cas_controller/CasTcp.h delete mode 100644 cloudphone/src/main/cpp/cas_controller/CasVideoHandler.cpp+++ delete mode 100644 cloudphone/src/main/cpp/cas_controller/CasVideoHandler.h+++ create mode 100644 cloudphone/src/main/cpp/cas_controller/ICasTransmission.h create mode 100644 cloudphone/src/main/java/com/huawei/cloudphone/api/ICloudPhoneScreenshot.java create mode 100644 cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneScreenshotImpl.java create mode 100644 cloudphone/src/main/java/com/huawei/cloudphone/datacenter/CasRecvPktDispatcher.java create mode 100644 cloudphone/src/main/java/com/huawei/cloudphone/service/CasProcessorListener.java diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index a0dc8d5..69f1375 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/common/CasImageQualityManager.java b/app/src/main/java/com/huawei/cloudapp/common/CasImageQualityManager.java new file mode 100644 index 0000000..30746f2 --- /dev/null +++ b/app/src/main/java/com/huawei/cloudapp/common/CasImageQualityManager.java @@ -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. + */ + +package com.huawei.cloudapp.common; + +public class CasImageQualityManager { + + public static final int QUALITY_BEST = 0; + public static final int QUALITY_MAIN = 1; + public static final int QUALITY_BASIC = 2; + private volatile static CasImageQualityManager manager = null; + private int imageQualityStatus = 0; + + private CasImageQualityManager() { + } + + public static CasImageQualityManager getInstance() { + if (null == manager) { + synchronized (CasImageQualityManager.class) { + if (null == manager) { + manager = new CasImageQualityManager(); + } + } + } + + return manager; + } + + public int getImageQualityStatus() { + return this.imageQualityStatus; + } + + public void setImageQualityStatus(int qualityStatus) { + this.imageQualityStatus = qualityStatus; + } + +} diff --git a/app/src/main/java/com/huawei/cloudapp/ui/activity/CasCloudPhoneActivity.java b/app/src/main/java/com/huawei/cloudapp/ui/activity/CasCloudPhoneActivity.java index 80ead31..2063876 100644 --- a/app/src/main/java/com/huawei/cloudapp/ui/activity/CasCloudPhoneActivity.java +++ b/app/src/main/java/com/huawei/cloudapp/ui/activity/CasCloudPhoneActivity.java @@ -66,7 +66,6 @@ import android.content.res.Configuration; import android.graphics.Color; import android.os.Build; import android.os.Bundle; -import android.os.Environment; import android.os.Handler; import android.os.Message; import android.telephony.CellSignalStrength; @@ -120,7 +119,6 @@ import com.huawei.cloudapp.utils.CasAESKeystoreUtils; import com.huawei.cloudapp.utils.CasAdaptPhoneUtils; import com.huawei.cloudapp.utils.CasCommonUtils; import com.huawei.cloudphone.api.CloudAppDataListener; -import com.huawei.cloudphone.api.CloudAppScreenshotListener; import com.huawei.cloudphone.api.CloudPhoneClipboardListener; import com.huawei.cloudphone.api.CloudPhoneManager; import com.huawei.cloudphone.api.CloudPhoneOrientationChangeListener; @@ -130,9 +128,6 @@ import com.huawei.cloudphone.api.CloudPhonePermissionRequestListener; import com.huawei.cloudphone.api.CloudPhoneStateListener; import com.huawei.cloudphone.api.ICloudPhone; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.HashMap; @@ -200,7 +195,6 @@ public class CasCloudPhoneActivity extends FragmentActivity implements IHandleDa private Toast mToast; private int storagePermission = 1; - private ICloudPhone mCloudPhoneScreenshot = null; private Handler handler = new Handler() { @SuppressLint("RestrictedApi") @@ -224,8 +218,6 @@ public class CasCloudPhoneActivity extends FragmentActivity implements IHandleDa case MSG_RECONNECT_FAILED: try { mCloudPhone.exitCloudPhone(); -// mCloudPhoneScreenshot.stopScreenshot(); -// mCloudPhoneScreenshot.exitCloudPhone(); } catch (Exception e) { CASLog.e(TAG, "exitCloudPhone failed"); } @@ -354,7 +346,7 @@ public class CasCloudPhoneActivity extends FragmentActivity implements IHandleDa handler.removeMessages(MSG_PRESS_APP_SWITCH); break; case MotionEvent.ACTION_MOVE: - if (!isInView(motionEvent, view)) { + if (!isInView(motionEvent, view)) { handler.removeMessages(MSG_PRESS_HOME); handler.removeMessages(MSG_PRESS_APP_SWITCH); } @@ -379,13 +371,10 @@ public class CasCloudPhoneActivity extends FragmentActivity implements IHandleDa mCloudPhone.registerCloudAppDataListener(new CloudAppDataListenerImpl()); mCloudPhone.registerClipboardListener(new CloudPhoneClipboardListenerImpl()); -// mCloudPhoneScreenshot = CloudPhoneManager.createCloudPhoneInstance(); -// mCloudPhoneScreenshot.init(this, DEV_PHONE); - if (mMediaConfig == null) { mMediaConfig = new HashMap(); } - if (false/*isSupportH265()*/) { + if (isSupportH265()) { mMediaConfig.put(FRAME_TYPE, "h265"); } else { mMediaConfig.put(FRAME_TYPE, "h264"); @@ -397,10 +386,6 @@ public class CasCloudPhoneActivity extends FragmentActivity implements IHandleDa mMediaConfig.put(PHYSICAL_WIDTH, String.valueOf(metrics.widthPixels)); mMediaConfig.put(PHYSICAL_HEIGHT, String.valueOf(metrics.heightPixels)); mCloudPhone.setMediaConfig(mMediaConfig); - HashMap mediaConfig = new HashMap<>(); - mediaConfig.put("stream_width", "720"); - mediaConfig.put("stream_height", "1280"); -// mCloudPhoneScreenshot.setMediaConfig(mediaConfig); getPhoneConnectInfo(mPhoneId); } catch (IllegalArgumentException e) { @@ -1119,17 +1104,6 @@ public class CasCloudPhoneActivity extends FragmentActivity implements IHandleDa public void run() { try { mCloudPhone.startCloudPhone(CasCloudPhoneActivity.this, mFrameLayout, parasMap); -// mCloudPhoneScreenshot.startScreenshot(parasMap, new CloudAppScreenshotListener() { -// @Override -// public void onRecvPicture(byte[] picture) { -// File extDir = Environment.getExternalStorageDirectory(); -// File file = new File(extDir, "phone.jpg"); -// try (FileOutputStream fos = new FileOutputStream(file)) { -// fos.write(picture); -// } catch (IOException e) { -// } -// } -// }); } catch (IllegalArgumentException e) { showDialog(getResources().getString(R.string.cas_phone_input_param_invalid)); } catch (Exception e) { 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 1a55b90..6efc83a 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 7f31c16..6a3e46a 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/CasController.cpp b/cloudphone/src/main/cpp/CasController.cpp index 9cf3f7a..688baba 100644 --- a/cloudphone/src/main/cpp/CasController.cpp +++ b/cloudphone/src/main/cpp/CasController.cpp @@ -41,16 +41,17 @@ 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; CasController::CasController() { + INFO("constuctor."); m_isconnect = true; } CasController::~CasController() { + INFO("destuctor."); ReleaseResource(); } @@ -65,6 +66,11 @@ bool CasController::Start(ANativeWindow *nativeWindow, bool isHome) m_casMessages.push_back(CasMsgType::CmdControl); m_casMessages.push_back(CasMsgType::Orientation); m_casMessages.push_back(CasMsgType::Audio); + m_casMessages.push_back(CasMsgType::VirtualCamera); + m_casMessages.push_back(CasMsgType::VirtualMicrophone); + m_casMessages.push_back(CasMsgType::VirtualSensor); + m_casMessages.push_back(CasMsgType::VirtualLocation); + if (!ProcessStart()) { SetState(STOPPED); return false; @@ -87,9 +93,9 @@ bool CasController::Stop(bool isHome) ERR("Failed to send stop command."); } this->SetState(STOPPED); - m_casClient->stop(); - m_casClient->disconnect(); m_casHandlerManager->stop(); + m_casClient->stop(); + m_casClient->setListener(nullptr); ReleaseResource(); m_rotationDegrees = 0; m_orientation = 0; @@ -131,7 +137,7 @@ bool CasController::Resume(ANativeWindow *nativeWindow) bool CasController::Reconnect() { - std::lock_guard lockGuard(this->m_lock); + std::lock_guard lockGuad(m_lock); if (this->GetState() == STOPPED) { INFO("Reconnect failed because phone already stop."); return false; @@ -143,6 +149,7 @@ bool CasController::Reconnect() bool CasController::StartScreenshot() { + std::lock_guard lockGuard(m_lock); m_snapshotFlag = true; prepareParameters(); m_casMessages.clear(); @@ -150,7 +157,13 @@ bool CasController::StartScreenshot() m_casMessages.push_back(CasMsgType::CmdControl); m_casMessages.push_back(CasMsgType::Orientation); m_casMessages.push_back(CasMsgType::ScreenShot); - return ProcessStart(); + if (!ProcessStart()) { + SetState(STOPPED); + return false; + } else { + SetState(START_SUCCESS); + return true; + } } bool CasController::StopScreenshot() @@ -167,6 +180,7 @@ void CasController::SetJniConf(string key, string value) bool CasController::SetMediaConfig(map mediaConfig) { + std::lock_guard lockGuard(m_lock); if (!IsValidMediaConfig(mediaConfig)) { ERR("Media config is invalid"); return false; @@ -197,7 +211,6 @@ bool CasController::SendTouchEvent(int id, int action, int x, int y, int pressur INFO("Failed to send touch event because phone not start."); return false; } - INFO("SendTouchEvent ..."); return m_touch->SendTouchEvent(id, action, x, y, pressure, (int)time, orientation, height, width); } @@ -208,7 +221,6 @@ bool CasController::SendKeyEvent(uint16_t keycode, uint16_t action) INFO("Failed to send key event because phone already stop."); return false; } - INFO("SendKeyEvent ..."); return m_touch->SendKeyEvent(keycode, action); } @@ -220,7 +232,6 @@ bool CasController::SendMotionEvent(uint16_t masterAxis, int32_t masterValue, ui INFO("Failed to send motion event because phone not start."); return false; } - INFO("SendMotionEvent ..."); return m_touch->SendMotionEvent(masterAxis, masterValue, secondaryAxis, secondaryValue); } @@ -228,12 +239,19 @@ int CasController::JniSendData(CasMsgType type, uint8_t *data, int length) { std::lock_guard lockGuard(m_lock); if (this->GetState() != START_SUCCESS) { - INFO("Failed to send data because phone not start."); + return false; + } + if (m_streamBuildSender == nullptr || !m_casClient->isReady()) { return false; } return length == m_streamBuildSender->SendDataToServer(type, data, length); } +void CasController::SetLogPath(std::string &path) +{ + m_logPath = path; +} + JNIState CasController::GetState() { return this->m_state; @@ -270,18 +288,20 @@ bool CasController::IsValidMediaConfig(map mediaConfig) // 校验虚拟宽高,虚拟宽高需满足同时设置或同时未设置,且大于等于240,小于等于4096,与8对齐 bool containsStreamWidth = mediaConfig.find(KEY_STREAM_WIDTH) != mediaConfig.end(); bool containsStreamHeight = mediaConfig.find(KEY_STREAM_HEIGHT) != mediaConfig.end(); - if (!containsStreamWidth || !containsStreamHeight) { + + 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; } - 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; - } return true; } @@ -389,6 +409,8 @@ bool CasController::ForceIFrame() bool CasController::doForceIFrame() { + m_casHandlerManager->stopVideoHandler(); + m_casHandlerManager->startVideoHandler(); map parameters = { { KEY_COMMAND, CMD_REQ_IFRAME }, { KEY_SESSION_ID, m_sessionId } }; bool res = SendCommand(parameters); if (!res) { @@ -403,7 +425,7 @@ void CasController::NotifyCommand(int type, string msg) std::lock_guard lockGuard(this->m_callbackLock); if (m_casCmdCallbck != nullptr) { INFO("msg = %s", msg.c_str()); - m_casCmdCallbck(type, std::move(msg), m_callbackOpaque); + m_casCmdCallbck(type, msg, m_callbackOpaque); } } @@ -427,6 +449,7 @@ void CasController::onMessageRecv(stream_msg_head_t *header, uint8_t *body, int { 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) { @@ -436,8 +459,6 @@ void CasController::onMessageRecv(stream_msg_head_t *header, uint8_t *body, int } else { m_rotationDegrees = 0; } - m_casHandlerManager->stopVideoHandler(); - m_casHandlerManager->startVideoHandler(); ForceIFrame(); } if (m_casMessageCallback != nullptr) { @@ -493,6 +514,20 @@ std::string CasController::GetVideoRecvStats() { std::string statsString; std::stringstream stream; + + StreamRecvStats videoRecvStats; + if (m_casClient->getRecvStats(Video, &videoRecvStats) == 0) { + stream << "下行视频丢包 : " << videoRecvStats.lostRate << std::endl; + stream << "视频接收码率 : " << videoRecvStats.recvBitrate << std::endl; + } + StreamRecvStats audioRecvStats; + if (m_casClient->getRecvStats(Audio, &audioRecvStats) == 0) { + stream << "音频接受码率 : " << audioRecvStats.recvBitrate << std::endl; + } + StreamSendStats cmdSendStats; + if (m_casClient->getRecvStats(CmdControl, &cmdSendStats) == 0) { + stream << "指令发送码率(需操作) : " << cmdSendStats.encBitrate << std::endl; + } stream << "帧率 : " << GetCurrentFPS() << "fps" << std::endl; stream << "策略丢帧 : " << CasVideoUtil::GetInstance()->GetCurrentDropFPS() << "fps" << std::endl; @@ -500,31 +535,6 @@ std::string CasController::GetVideoRecvStats() return statsString; } -void CasController::CalculateFPS() -{ - uint64_t currentTime = CasVideoUtil::GetInstance()->GetNow(); - - m_videoDataCount++; - - if (m_lastTimeRefreshFPS == 0) { - m_lastTimeRefreshFPS = currentTime; - } - if (currentTime - m_lastTimeRefreshFPS > DURATION_USEC) { - m_currentFPS = m_videoDataCount; - m_videoDataCount = 0; - m_lastTimeRefreshFPS = currentTime; - } -} - -int CasController::GetCurrentFPS() const -{ - uint64_t currentTime = CasVideoUtil::GetInstance()->GetNow(); - if (currentTime - m_lastTimeRefreshFPS > 2 * DURATION_USEC) { - return 0; - } - return m_currentFPS; -} - bool CasController::prepareParameters() { m_conf.parseConf(m_jniConf); @@ -542,13 +552,12 @@ bool CasController::prepareParameters() if (m_mediaConfig.find(KEY_FRAME_TYPE) != m_mediaConfig.end()) { m_frameType = m_mediaConfig[KEY_FRAME_TYPE] == "h264" ? FrameType::H264 : FrameType::H265; } - m_frameType = FrameType::H264; return true; } bool CasController::ProcessStart() { - m_casClient = new (std::nothrow) CasClient(); + m_casClient = new (std::nothrow) CasClient(!m_snapshotFlag); if (m_casClient == nullptr) { ERR("Couldn't create CasClient."); return false; @@ -573,21 +582,23 @@ bool CasController::ProcessStart() ERR("Couldn't create CasTouch."); goto FAILED; } - m_casClient->init(m_ip, m_port); - m_casClient->setListner(m_casHandlerManager); - for (auto &it : m_casMessages) { m_casHandlerManager->createHandler(it); } + + m_casClient->init(m_ip, m_port, m_logPath); + m_casClient->setListener(m_casHandlerManager); + + m_casHandlerManager->init(); if (!BuildConnection()) { ERR("Connect failed."); goto FAILED; } - m_casHandlerManager->start(); if (m_casClient->start() != 0) { ERR("Failed to build connection"); goto FAILED; } + m_casHandlerManager->start(); if (!SendStartCmd()) { ERR("Send start cmd failed."); goto FAILED; @@ -625,7 +636,7 @@ bool CasController::ProcessReconnect() return false; } SetConnectStatus(true); - doForceIFrame(); + //doForceIFrame(); SetState(START_SUCCESS); return true; } @@ -637,6 +648,7 @@ void CasController::ReleaseResource() m_casClient = nullptr; } if (m_casHandlerManager != nullptr) { + m_casHandlerManager->stop(); m_casHandlerManager->clear(); delete m_casHandlerManager; m_casHandlerManager = nullptr; @@ -654,3 +666,28 @@ void CasController::ReleaseResource() m_touch = nullptr; } } + +void CasController::CalculateFPS() +{ + uint64_t currentTime = CasVideoUtil::GetInstance()->GetNow(); + + m_videoDataCount++; + + if (m_lastTimeRefreshFPS == 0) { + m_lastTimeRefreshFPS = currentTime; + } + if (currentTime - m_lastTimeRefreshFPS > DURATION_USEC) { + m_currentFPS = m_videoDataCount; + m_videoDataCount = 0; + m_lastTimeRefreshFPS = currentTime; + } +} + +int CasController::GetCurrentFPS() const +{ + uint64_t currentTime = CasVideoUtil::GetInstance()->GetNow(); + if (currentTime - m_lastTimeRefreshFPS > 2 * DURATION_USEC) { + return 0; + } + return m_currentFPS; +} diff --git a/cloudphone/src/main/cpp/CasController.h b/cloudphone/src/main/cpp/CasController.h index a02cb8e..6b327ba 100644 --- a/cloudphone/src/main/cpp/CasController.h +++ b/cloudphone/src/main/cpp/CasController.h @@ -38,7 +38,7 @@ class CasClient; class CasHandlerManager; -typedef void *(*CasCmdCallback)(int, std::string, void*); +typedef void *(*CasCmdCallback)(int, std::string &, void*); typedef void *(*CasMessageCallback)(stream_msg_head_t*, uint8_t*, int, void*); class CasController { @@ -80,6 +80,8 @@ public: int JniSendData(CasMsgType type, uint8_t *data, int length); + void SetLogPath(std::string &path); + uint64_t GetLag(); JNIState GetState(); @@ -126,10 +128,7 @@ private: CasCmdController *m_cmdController = nullptr; - CasSocket *m_casClientSocket = nullptr; CasStreamBuildSender *m_streamBuildSender = nullptr; - CasVideoHDecodeThread *m_videoDecodeThread = nullptr; - NetTrans *m_mtrans = nullptr; std::map m_jniConf; enum JNIState m_state = INIT; @@ -163,6 +162,7 @@ private: std::vector m_casMessages; bool m_isconnect; + std::string m_logPath{}; }; #endif // CLOUDAPPSDK_CASCONTROLLRT_H \ No newline at end of file diff --git a/cloudphone/src/main/cpp/CasJniBridge.cpp b/cloudphone/src/main/cpp/CasJniBridge.cpp index 9f2ab3f..a029150 100644 --- a/cloudphone/src/main/cpp/CasJniBridge.cpp +++ b/cloudphone/src/main/cpp/CasJniBridge.cpp @@ -30,7 +30,7 @@ using namespace std; ANativeWindow *gANativeWindow; // 用于jni回调java static JavaVM *g_JVM = nullptr; -void *invokeCmdCallBack(int type, string msg, void*); +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) @@ -50,9 +50,14 @@ static std::string jstring2string(JNIEnv *env, jstring jStr) return ret; } -JNIEXPORT jlong JNICALL JNI(init)(JNIEnv *env, jobject obj) +JNIEXPORT jlong JNICALL JNI(init)(JNIEnv *env, jobject obj, jstring logPath) { - return reinterpret_cast(new CasController()); + 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) @@ -114,25 +119,25 @@ JNIEXPORT void JNICALL JNI(stop)(JNIEnv *, jobject clazz, jlong nativeObject, jb } } -JNIEXPORT void JNICALL JNI(pause)(JNIEnv *, jobject clazz, jlong nativeObject) +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; + return false; } - casController->Pause(); + return casController->Pause(); } -JNIEXPORT void JNICALL JNI(resume)(JNIEnv *env, jobject clazz, jlong nativeObject, jobject surface) +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; + return false; } gANativeWindow = ANativeWindow_fromSurface(env, surface); - casController->Resume(gANativeWindow); + return casController->Resume(gANativeWindow); } JNIEXPORT jboolean JNICALL JNI(startScreenshot)(JNIEnv *env, jobject clazz, jlong nativeObject) @@ -370,12 +375,11 @@ extern "C" JNIEXPORT void JNICALL JNI(registerCasJNICallback)(JNIEnv *env, jobje casController->SetCmdCallBackMethod(invokeCmdCallBack, invokeMessageCallBack, (void*)jniCallbackObject); } -void *invokeCmdCallBack(int type, string msg, void *opaque) +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."); @@ -384,7 +388,6 @@ void *invokeCmdCallBack(int type, string msg, void *opaque) mNeedDetach = JNI_TRUE; } jobject jniCallbackObject = static_cast(opaque); - INFO("jniCallbackObject %p", jniCallbackObject); jclass javaClass = env->GetObjectClass(jniCallbackObject); if (javaClass == 0) { ERR("Unable to find class"); @@ -398,8 +401,6 @@ void *invokeCmdCallBack(int type, string msg, void *opaque) } jstring jmessage = env->NewStringUTF(msg.c_str()); env->CallVoidMethod(jniCallbackObject, javaCallbackId, type, jmessage); - // env->ReleaseStringUTFChars(jmessage, msg.c_str()); - INFO("Invoke cmd callback end."); env->DeleteLocalRef(javaClass); if (mNeedDetach) { g_JVM->DetachCurrentThread(); diff --git a/cloudphone/src/main/cpp/CasJniBridge.h b/cloudphone/src/main/cpp/CasJniBridge.h index 5530899..e5307e0 100644 --- a/cloudphone/src/main/cpp/CasJniBridge.h +++ b/cloudphone/src/main/cpp/CasJniBridge.h @@ -21,7 +21,7 @@ extern "C" { -JNIEXPORT long JNICALL JNI(init)(JNIEnv *env, jobject obj); +JNIEXPORT long JNICALL JNI(init)(JNIEnv *env, jobject obj, jstring logPath); JNIEXPORT void JNICALL JNI(deinit)(JNIEnv *env, jobject clazz, jlong nativeObject); @@ -33,9 +33,9 @@ JNIEXPORT jboolean JNICALL JNI(reconnect)(JNIEnv *env, jobject clazz, jlong nati JNIEXPORT void JNICALL JNI(stop)(JNIEnv *env, jobject clazz, jlong nativeObject, jboolean isHome); -JNIEXPORT void JNICALL JNI(pause)(JNIEnv *env, jobject clazz, jlong nativeObject); +JNIEXPORT jboolean JNICALL JNI(pause)(JNIEnv *env, jobject clazz, jlong nativeObject); -JNIEXPORT void JNICALL JNI(resume)(JNIEnv *env, jobject clazz, jlong nativeObject, jobject surface); +JNIEXPORT jboolean JNICALL JNI(resume)(JNIEnv *env, jobject clazz, jlong nativeObject, jobject surface); JNIEXPORT jboolean JNICALL JNI(startScreenshot)(JNIEnv *env, jobject clazz, jlong nativeObject); 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 0000000..867a4f4 --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasAudioHandler.cpp @@ -0,0 +1,104 @@ +// 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(CasMessageResult *result) + :CasMessageHandler(result) +{ + INFO("Construct."); + m_opusDecoder = nullptr; +} + +CasAudioHandler::~CasAudioHandler() +{ + INFO("Destruct."); +} + +int CasAudioHandler::init() +{ + INFO("Init."); + return 0; +} + +int CasAudioHandler::start() +{ + INFO("Start."); + std::lock_guard lock(m_lock); + int err; + m_opusDecoder = opus_decoder_create(SAMPLE_RATE, CHANNEL, &err); + if (m_opusDecoder == nullptr) { + ERR("Create opus decoder failed."); + return -1; + } + return 0; +} + +int CasAudioHandler::stop() +{ + INFO("Stop."); + std::lock_guard lock(m_lock); + if (m_opusDecoder != nullptr) { + opus_decoder_destroy(m_opusDecoder); + m_opusDecoder = nullptr; + } + return 0; +} + +void CasAudioHandler::handleMeassage(uint8_t *message, uint32_t length, int source) +{ + std::lock_guard lock(m_lock); + if (message == nullptr) { + return; + } + stream_msg_head_t *header = (stream_msg_head_t *) message; + uint8_t *body = message + sizeof(stream_msg_head_t); + if (source == 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 (source == SOURCE_MRIGHT) { + 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 0000000..5f7e80e --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasAudioHandler.h @@ -0,0 +1,39 @@ +// 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 "CasMessageHandler.h" + +struct OpusDecoder; + +class CasAudioHandler : public CasMessageHandler{ +public: + CasAudioHandler(CasMessageResult *result); + ~CasAudioHandler(); + + int init() override; + int start() override; + int stop() override; + void handleMeassage(uint8_t *message, uint32_t length, int dataSource) override; + +private: + std::mutex m_lock; + OpusDecoder *m_opusDecoder; +}; + +#endif // CLOUDAPPSDK_CASAUDIOHANDLER_H diff --git a/cloudphone/src/main/cpp/cas_controller/CasClient.cpp b/cloudphone/src/main/cpp/cas_controller/CasClient.cpp index 113101a..bf8d59e 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasClient.cpp +++ b/cloudphone/src/main/cpp/cas_controller/CasClient.cpp @@ -1,178 +1,208 @@ +// 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 "CasClient.h" #include "../cas_socket/CasTcpSocket.h" #include "../cas_common/CasLog.h" #include "../cas_common/CasBuffer.h" +#include "CasTcp.h" +#include "CasMright.h" +#include "ICasTransmission.h" -CasClient::CasClient() +CasClient::CasClient(bool isStream) { - INFO("CasClient constructure."); - m_ip = 0; - m_port = 0; - m_listener = nullptr; - m_casClientSocket = nullptr; -#if 1 - if (true) { - m_casMright = new CasMright(); - } + INFO("CasClient construct."); + m_stream = isStream; + m_casTcp = nullptr; +#if MTRANS_ENABLED + m_casMright = nullptr; #endif } CasClient::~CasClient() { - INFO("CasClient deconstructure."); + INFO("CasClient destruct."); } -int CasClient::init(uint32_t ip, uint16_t port) +int CasClient::init(uint32_t ip, uint16_t port, std::string &logPath) { INFO("Init, ip=%d, port=%d", ip, port); - std::lock_guard lock(m_lock); - m_ip = ip; - m_port = port; - if (m_casMright != nullptr) { - + std::lock_guard lock(m_mutex); + m_casTcp = new (std::nothrow) CasTcp(); + if (m_casTcp == nullptr) { + ERR("Could't create CasTcp."); + return -1; + } + m_casTcp->init(ip, port, logPath); +#if MTRANS_ENABLED + if (m_stream) { + m_casMright = new (std::nothrow) CasMright(); + if (m_casMright == nullptr) { + delete m_casTcp; + m_casTcp = nullptr; + return -1; + } + m_casMright->init(ip, port, logPath); } +#endif return 0; } int CasClient::deinit() { INFO("Deinit."); + std::lock_guard lock(m_mutex); + if (m_casTcp != nullptr) { + m_casTcp->deinit(); + delete m_casTcp; + m_casTcp = nullptr; + } + +#if MTRANS_ENABLED + if (m_casMright != nullptr) { + m_casMright->deinit(); + delete m_casMright; + m_casMright = nullptr; + } +#endif return 0; } int CasClient::connect() { INFO("Connect."); - std::lock_guard lock(m_lock); - m_casClientSocket = new (std::nothrow) CasTcpClientSocket(m_ip, m_port); - if (m_casClientSocket == nullptr) { - ERR("Create CasTcpClientSocket instance failed."); + std::lock_guard lock(m_mutex); + if (m_casTcp == nullptr) { + ERR("m_casTcp is null."); 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."); - return result; - } - INFO("Connect server success."); - return 0; -} - -int CasClient::disconnect() -{ - INFO("Disconnect."); - std::lock_guard lock(m_lock); - if (m_casClientSocket != nullptr) { - delete m_casClientSocket; - m_casClientSocket = nullptr; - } - return 0; + return m_casTcp->connect(); } int CasClient::start() { INFO("Start."); - std::lock_guard lock(m_lock); - m_isTaskRun = true; - m_task = std::thread(&CasClient::recvTask, this); + 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_casMright != nullptr) { + if (m_casMright->start() != 0) { + return -1; + } + } +#endif return 0; } int CasClient::stop() { INFO("Stop."); - std::lock_guard lock(m_lock); - if (m_isTaskRun) { - m_isTaskRun = false; - m_casClientSocket->Close(); - m_task.join(); + std::lock_guard lock(m_mutex); + if (m_casTcp == nullptr) { + ERR("m_casTcp is null."); + return -1; } - INFO("client stop success."); - return 0; + int ret = m_casTcp->stop(); +#if MTRANS_ENABLED + if (m_casMright != nullptr) { + m_casMright->stop(); + } +#endif + return ret; } int CasClient::reconnect() { INFO("Reconnect."); - stop(); - if (connect() != 0) { - ERR("Reconnect failed."); + std::lock_guard lock(m_mutex); + if (m_casTcp == nullptr) { + ERR("m_casTcp is null."); return -1; } - return start();; + if (m_casTcp->reconnect() != 0) { + return -1; + } +#if MTRANS_ENABLED + if (m_casMright != nullptr) { + m_casMright->reconnect(); + } +#endif + return 0; } int CasClient::send(uint8_t *buffer, uint32_t length) { - std::lock_guard lock(m_lock); - if (m_casClientSocket == nullptr) { - ERR("client socket is nullptr."); + std::lock_guard lock(m_mutex); + if (m_casTcp == nullptr) { + ERR("m_casTcp is null."); return -1; } - return m_casClientSocket->Send(buffer, length); -} - -void CasClient::setListner(CasClientListener *listener) -{ - m_listener = listener; +#if MTRANS_ENABLED + stream_msg_head_t *msgHead = (stream_msg_head_t *)buffer; + if (m_casMright != nullptr && m_casMright->isReady()) { + if (msgHead->type == VirtualMicrophone + || msgHead->type == VirtualCamera + || msgHead->type == TouchInput + || msgHead->type == KeyEventInput + || msgHead->type == MotionEventInput) { + return m_casMright->send(buffer, length); + } + } + if (m_casMright != nullptr && msgHead->type == CasMsgType::HeartBeat) { + m_casMright->send(buffer, length); + } +#endif + return m_casTcp->send(buffer, length); } -int CasClient::readN(uint8_t *buffer, uint32_t length) +void CasClient::setListener(CasClientListener *listener) { - uint32_t readLen = 0; - while (m_isTaskRun && readLen < length) { - int ret = m_casClientSocket->Recv(buffer + readLen, length - readLen); - if (ret > 0) { - readLen += ret; - } else { - if ((ret < 0) && ((0 == errno) || (EAGAIN == errno) || - (EWOULDBLOCK == errno) || (EINTR == errno) || (ETIMEDOUT == errno))) { - return 0; - } else { - return -1; - } - } + INFO("SetListener."); + std::lock_guard lock(m_mutex); + if (m_casTcp != nullptr) { + m_casTcp->setListener(listener); } - return readLen; +#if MTRANS_ENABLED + if (m_casMright != nullptr) { + m_casMright->setListener(listener); + } +#endif } -void CasClient::recvTask() +int CasClient::getRecvStats(int type, void *stats) { - 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) { - ERR("Read message header failed ret = %d headerLength = %d.", ret, headerLength); - usleep(1000); - continue; - } - uint32_t bodyLength = msgHead.GetPayloadSize(); - uint8_t *message = (uint8_t*)AllocBuffer(bodyLength + headerLength); - ret = readN(message + headerLength, bodyLength); - if (ret != bodyLength) { - ERR("Read message body failed ret = %d bodyLength %d type= %d.", ret, bodyLength, msgHead.type); - FreeBuffer(message); - continue; - } - (void)memcpy_s(message, headerLength, &msgHead, headerLength); - dispatchMessage(msgHead, message, bodyLength + headerLength); + std::lock_guard lock(m_mutex); +#if MTRANS_ENABLED + if (m_casMright != nullptr) { + return m_casMright->getRecvStats(type, stats); } +#endif + return -1; } -void CasClient::dispatchMessage(streamMsgHead &header, uint8_t *message, uint32_t length) +bool CasClient::isReady() { - if (m_listener != nullptr) { - m_listener->onNewPacket(header.type, message, length); + 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/CasClient.h b/cloudphone/src/main/cpp/cas_controller/CasClient.h index 895d5c3..771eb06 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasClient.h +++ b/cloudphone/src/main/cpp/cas_controller/CasClient.h @@ -1,53 +1,53 @@ +// 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 "../cas_socket/CasSocket.h" -#include "../cas_common/CasMsg.h" -#include "CasTcpSocket.h" -#include "CasMright.h" +#include "ICasTransmission.h" -class CasClientListener { -public: - CasClientListener() {}; - virtual ~CasClientListener() {}; - virtual void onNewPacket(int type, uint8_t *buffer, uint32_t length) = 0; -}; +class CasMright; +class CasTcp; +struct StreamRecvStats; class CasClient { public: - CasClient(); + CasClient(bool isStream = true); ~CasClient(); - int init(uint32_t ip, uint16_t port); + + int init(uint32_t ip, uint16_t port, std::string &path); int deinit(); int connect(); - int disconnect(); int start(); int stop(); int reconnect(); int send(uint8_t *buffer, uint32_t length); - void setListner(CasClientListener *listener); + void setListener(CasClientListener *listener); + int getRecvStats(int type, void *stats); + bool isReady(); 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::mutex m_lock; - std::thread m_task; - CasTcpClientSocket *m_casClientSocket; - CasClientListener *m_listener; + std::mutex m_mutex; + CasTcp *m_casTcp; + bool m_stream; #if MTRANS_ENABLED CasMright *m_casMright; #endif }; - -#endif //CLOUDAPPSDK_CASCLIENT_H +#endif // CLOUDAPPSDK_CASCLIENT_H diff --git a/cloudphone/src/main/cpp/cas_controller/CasCmdHandler.cpp b/cloudphone/src/main/cpp/cas_controller/CasCmdHandler.cpp index 2942f15..f391bc6 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasCmdHandler.cpp +++ b/cloudphone/src/main/cpp/cas_controller/CasCmdHandler.cpp @@ -1,9 +1,24 @@ +// 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(CasMessageResult *result) :CasMessageHandler(result) @@ -16,6 +31,12 @@ CasCmdHandler::~CasCmdHandler() INFO("cmd handler deconstructor."); } +int CasCmdHandler::init() +{ + INFO("init."); + return 0; +} + int CasCmdHandler::start() { INFO("cmd handler start."); @@ -28,10 +49,11 @@ int CasCmdHandler::stop() return 0; } -void CasCmdHandler::handleMeassage(uint8_t *message, uint32_t length) +void CasCmdHandler::handleMeassage(uint8_t *message, uint32_t length, int dataSource) { - INFO("cmd handler handle message."); - + if (message == nullptr) { + return; + } streamMsgHead *streamHead = (streamMsgHead *)message; const char *receivedMsgString = (char *)((uint8_t *)message + sizeof(streamMsgHead)); std::map parameters = @@ -57,4 +79,5 @@ void CasCmdHandler::handleMeassage(uint8_t *message, uint32_t length) 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 index a5c3562..03a00ae 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasCmdHandler.h +++ b/cloudphone/src/main/cpp/cas_controller/CasCmdHandler.h @@ -1,3 +1,17 @@ +// 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 @@ -11,11 +25,12 @@ public: CasCmdHandler(CasMessageResult *result); ~CasCmdHandler(); + int init() override; int start() override; int stop() override; - void handleMeassage(uint8_t *message, uint32_t length) override; + void handleMeassage(uint8_t *message, uint32_t length, int dataSource) override; private: }; -#endif //CLOUDAPPSDK_CASCMDHANDLER_H +#endif // CLOUDAPPSDK_CASCMDHANDLER_H diff --git a/cloudphone/src/main/cpp/cas_controller/CasCommonHandler.cpp b/cloudphone/src/main/cpp/cas_controller/CasCommonHandler.cpp index 64494c8..8ed2634 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasCommonHandler.cpp +++ b/cloudphone/src/main/cpp/cas_controller/CasCommonHandler.cpp @@ -1,3 +1,17 @@ +// 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" @@ -11,6 +25,11 @@ CasCommonHandler::~CasCommonHandler() { } +int CasCommonHandler::init() +{ + return 0; +} + int CasCommonHandler::start() { return 0; @@ -21,7 +40,7 @@ int CasCommonHandler::stop() return 0; } -void CasCommonHandler::handleMeassage(uint8_t *message, uint32_t length) +void CasCommonHandler::handleMeassage(uint8_t *message, uint32_t length, int dataSource) { if (message == nullptr) { return; diff --git a/cloudphone/src/main/cpp/cas_controller/CasCommonHandler.h b/cloudphone/src/main/cpp/cas_controller/CasCommonHandler.h index 14fb50b..f7271a2 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasCommonHandler.h +++ b/cloudphone/src/main/cpp/cas_controller/CasCommonHandler.h @@ -1,3 +1,17 @@ +// 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 @@ -10,12 +24,13 @@ public: CasCommonHandler(CasMessageResult *result); ~CasCommonHandler(); + int init() override; int start() override; int stop() override; - void handleMeassage(uint8_t *message, uint32_t length) override; + void handleMeassage(uint8_t *message, uint32_t length, int dataSource) override; private: }; -#endif //CLOUDAPPSDK_CASCOMMONHANDLER_H +#endif // CLOUDAPPSDK_CASCOMMONHANDLER_H diff --git a/cloudphone/src/main/cpp/cas_controller/CasHandlerManager.cpp b/cloudphone/src/main/cpp/cas_controller/CasHandlerManager.cpp index 2c61eb2..96534ff 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasHandlerManager.cpp +++ b/cloudphone/src/main/cpp/cas_controller/CasHandlerManager.cpp @@ -1,3 +1,17 @@ +// 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" @@ -6,26 +20,40 @@ #include "CasCommonHandler.h" #include "CasHearbeatHandler.h" #include "../cas_common/CasLog.h" +#include "CasBuffer.h" +#include "CasAudioHandler.h" CasHandlerManager::CasHandlerManager(CasController *casController) { - INFO("CasHandlerManager constructor."); + INFO("Constructor."); m_casController = casController; } CasHandlerManager::~CasHandlerManager() { - INFO("CasHandlerManager deconstructor."); + INFO("Destructor."); m_casController = nullptr; } +int CasHandlerManager::init() +{ + INFO("Init."); + for (auto &it : m_handlerMap) { + CasMessageHandler *handler = it.second; + if (handler->init() != 0) { + return -1; + } + } + return 0; +} + int CasHandlerManager::start() { - INFO("CasHandlerManager start."); + INFO("Start."); for (auto &it : m_handlerMap) { CasMessageHandler *handler = it.second; if (handler->start() != 0) { - break; + return -1; } } return 0; @@ -33,18 +61,17 @@ int CasHandlerManager::start() int CasHandlerManager::stop() { - INFO("CasHandlerManager stop."); + INFO("Stop."); for (auto &it : m_handlerMap) { CasMessageHandler *handler = it.second; handler->stop(); } - INFO("CasHandlerManager stop end."); return 0; } void CasHandlerManager::clear() { - INFO("CasHandlerManager clear."); + INFO("Clear."); for (auto &it : m_handlerMap) { CasMessageHandler *handler = it.second; } @@ -57,7 +84,6 @@ int CasHandlerManager::createHandler(CasMsgType type) CasVideoHandler *videoHandler; CasHearbeatHandler *hearbeatHandler; - int value = 0; switch (type) { case CasMsgType::Video: videoHandler = new (std::nothrow) CasVideoHandler(this); @@ -74,18 +100,25 @@ int CasHandlerManager::createHandler(CasMsgType type) case CasMsgType::CmdControl: handler = new (std::nothrow) CasCmdHandler(this); break; - case CasMsgType::Audio: case CasMsgType::Channel: case CasMsgType::Orientation: case CasMsgType::ImeData: - case CasMsgType::VirtualDevice: + case CasMsgType::VirtualCamera: + case CasMsgType::VirtualMicrophone: + case CasMsgType::VirtualSensor: + case CasMsgType::VirtualLocation: case CasMsgType::ScreenShot: handler = new (std::nothrow) CasCommonHandler(this); break; + case CasMsgType::Audio: + handler = new (std::nothrow) CasAudioHandler(this); + break; default: return -1; } - addHandler(type, handler); + if (handler != nullptr) { + addHandler(type, handler); + } return 0; } @@ -117,13 +150,20 @@ int CasHandlerManager::stopVideoHandler() return 0; } -void CasHandlerManager::onNewPacket(int type, uint8_t *buffer, uint32_t length) +void CasHandlerManager::onNewPacket(int type, uint8_t *buffer, uint32_t length, int source) { if (m_handlerMap.count(type)) { CasMessageHandler *handler = m_handlerMap[type]; - handler->handleMeassage(buffer, length); + handler->handleMeassage(buffer, length, source); } else { - delete[] buffer; + FreeBuffer(buffer); + } +} + +void CasHandlerManager::onNewCmd(int cmd, std::string detail) +{ + if (m_casController != nullptr) { + m_casController->NotifyCommand(cmd, detail); } } @@ -133,6 +173,7 @@ void CasHandlerManager::onCmdRecv(int code, std::string &message) m_casController->OnCmdRecv(code, message); } } + void CasHandlerManager::onMessageRecv(stream_msg_head_t *header, uint8_t *body) { if (m_casController != nullptr) { @@ -145,4 +186,11 @@ void CasHandlerManager::onConnectionStatus(bool status) if (m_casController != nullptr) { m_casController->SetConnectStatus(status); } +} + +void CasHandlerManager::onVideoPacketRecv() +{ + if (m_casController != nullptr) { + m_casController->CalculateFPS(); + } } \ No newline at end of file diff --git a/cloudphone/src/main/cpp/cas_controller/CasHandlerManager.h b/cloudphone/src/main/cpp/cas_controller/CasHandlerManager.h index ef87706..cd0e23c 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasHandlerManager.h +++ b/cloudphone/src/main/cpp/cas_controller/CasHandlerManager.h @@ -1,3 +1,17 @@ +// 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 @@ -13,25 +27,28 @@ public: CasHandlerManager(CasController *casController); ~CasHandlerManager(); + int init(); int start(); int stop(); void clear(); int createHandler(CasMsgType type); int startVideoHandler(); int stopVideoHandler(); - -public: void addHandler(CasMsgType type, CasMessageHandler *handler); -public: - void onNewPacket(int type, uint8_t *buffer, uint32_t length) override; +private: + void onNewPacket(int type, uint8_t *buffer, uint32_t length, int dataSource) override; + void onNewCmd(int cmd, std::string detail) override; + +private: void onCmdRecv(int code, std::string &msg) override; void onMessageRecv(stream_msg_head_t *header, uint8_t *body) override; void onConnectionStatus(bool status) override; + void onVideoPacketRecv() override; private: CasController *m_casController; std::map m_handlerMap; }; -#endif //CLOUDAPPSDK_CASHANDLERMANAGER_H +#endif // CLOUDAPPSDK_CASHANDLERMANAGER_H diff --git a/cloudphone/src/main/cpp/cas_controller/CasHearbeatHandler.cpp b/cloudphone/src/main/cpp/cas_controller/CasHearbeatHandler.cpp index 4060d85..ff3225f 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasHearbeatHandler.cpp +++ b/cloudphone/src/main/cpp/cas_controller/CasHearbeatHandler.cpp @@ -1,3 +1,17 @@ +// 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 "CasHearbeatHandler.h" #include "CasLog.h" @@ -19,13 +33,21 @@ CasHearbeatHandler::~CasHearbeatHandler() { } +int CasHearbeatHandler::init() +{ + INFO("init"); + return 0; +} + int CasHearbeatHandler::start() { + std::lock_guard lock(m_lock); m_heartbeatPktStream = new (std::nothrow) CasDataPipe(true); if (m_heartbeatPktStream == nullptr) { ERR("create heartbeat pipe failed."); return -1; } + m_conditionWait = false; m_heartbeatTaskRun = true; m_heartbeatThread = std::thread(&CasHearbeatHandler::heartbeatThread, this); return 0; @@ -33,6 +55,7 @@ int CasHearbeatHandler::start() int CasHearbeatHandler::stop() { + std::lock_guard lock(m_lock); if (m_heartbeatTaskRun) { m_heartbeatTaskRun = false; m_heartbeatPktStream->Exit(); @@ -44,8 +67,9 @@ int CasHearbeatHandler::stop() return 0; } -void CasHearbeatHandler::handleMeassage(uint8_t *message, uint32_t length) +void CasHearbeatHandler::handleMeassage(uint8_t *message, uint32_t length, int dataSource) { + std::lock_guard lock(m_lock); if (m_heartbeatPktStream != nullptr) { m_heartbeatPktStream->Handle(message); } @@ -53,6 +77,7 @@ void CasHearbeatHandler::handleMeassage(uint8_t *message, uint32_t length) void CasHearbeatHandler::setParameters(CasStreamBuildSender *streamBuildSender) { + std::lock_guard lock(m_lock); m_streamBuildSender = streamBuildSender; } @@ -83,7 +108,7 @@ void CasHearbeatHandler::heartbeatThread() int heatbeatFailedCount = 0; const int maxFailedTimes = 5; uint64_t lag; - bool isConnect = false; + bool isConnect = true; while (m_heartbeatTaskRun) { gettimeofday(&sendtime, nullptr); @@ -92,10 +117,10 @@ void CasHearbeatHandler::heartbeatThread() heatbeatFailedCount++; if (heatbeatFailedCount > maxFailedTimes && isConnect) { isConnect = false; + heatbeatFailedCount = 0; m_messageResult->onConnectionStatus(false); ERR("client is disconnect."); } - lag = MAX_LAG; } else { gettimeofday(&recvtime, nullptr); lag = (uint64_t)recvtime.tv_usec + (uint64_t)recvtime.tv_sec * 1000000 - @@ -105,9 +130,9 @@ void CasHearbeatHandler::heartbeatThread() isConnect = true; m_messageResult->onConnectionStatus(true); } + updateLag(lag); } - updateLag(lag); - sleepFor(200000); + sleepFor(200); // 200毫秒 } INFO("HeartbeatThread thread exit."); } @@ -131,7 +156,7 @@ void CasHearbeatHandler::updateLag(uint64_t lag) void CasHearbeatHandler::sleepFor(const int timeout) { std::unique_lock lock(m_mutex); - m_cv.wait_for(lock, std::chrono::microseconds(timeout), + m_cv.wait_for(lock, std::chrono::milliseconds(timeout), [this]() { return m_conditionWait; }); } diff --git a/cloudphone/src/main/cpp/cas_controller/CasHearbeatHandler.h b/cloudphone/src/main/cpp/cas_controller/CasHearbeatHandler.h index 30cf19b..8247296 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasHearbeatHandler.h +++ b/cloudphone/src/main/cpp/cas_controller/CasHearbeatHandler.h @@ -1,3 +1,17 @@ +// 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_CASHEARBEATHANDLER_H #define CLOUDAPPSDK_CASHEARBEATHANDLER_H @@ -15,9 +29,11 @@ class CasHearbeatHandler : public CasMessageHandler{ public: CasHearbeatHandler(CasMessageResult *result); ~CasHearbeatHandler(); + + int init() override; int start() override; int stop() override; - void handleMeassage(uint8_t *message, uint32_t length) override; + void handleMeassage(uint8_t *message, uint32_t length, int dataSource) override; void setParameters(CasStreamBuildSender *streamBuildSender); uint64_t getLag() { return m_lag; @@ -31,6 +47,7 @@ private: void updateLag(uint64_t lag); private: + std::mutex m_lock; uint64_t m_lag = 0; bool m_heartbeatTaskRun; std::thread m_heartbeatThread; diff --git a/cloudphone/src/main/cpp/cas_controller/CasMessageHandler.h b/cloudphone/src/main/cpp/cas_controller/CasMessageHandler.h index 1c0a4cd..e833b1d 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasMessageHandler.h +++ b/cloudphone/src/main/cpp/cas_controller/CasMessageHandler.h @@ -1,3 +1,17 @@ +// 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 @@ -12,6 +26,7 @@ public: virtual void onCmdRecv(int code, std::string &msg) = 0; virtual void onMessageRecv(stream_msg_head_t *header, uint8_t *body) = 0; virtual void onConnectionStatus(bool status) = 0; + virtual void onVideoPacketRecv() = 0; }; class CasMessageHandler { @@ -20,12 +35,13 @@ public: m_messageResult = result; } virtual ~CasMessageHandler() {} + virtual int init() = 0; virtual int start() = 0; virtual int stop() = 0; - virtual void handleMeassage(uint8_t *message, uint32_t length) = 0; + virtual void handleMeassage(uint8_t *message, uint32_t length, int source) = 0; protected: CasMessageResult *m_messageResult; }; -#endif //CLOUDAPPSDK_CASMESSAGEHANDLER_H +#endif // CLOUDAPPSDK_CASMESSAGEHANDLER_H diff --git a/cloudphone/src/main/cpp/cas_controller/CasMright.cpp b/cloudphone/src/main/cpp/cas_controller/CasMright.cpp index 5b2424d..d550d9a 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasMright.cpp +++ b/cloudphone/src/main/cpp/cas_controller/CasMright.cpp @@ -1,41 +1,84 @@ +// 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 "CasMright.h" #include "CasLog.h" +#include "opus.h" +#include "CasMsg.h" +#include "CasClient.h" +#include "net_trans.h" +#include "spdlog/sinks/rotating_file_sink.h" +#include "../CasCommon.h" -static int32_t OnRecvVideoStreamData(uint8_t*, uint32_t); -static int32_t OnRecvAudioStreamData(uint8_t*, uint32_t); -static int32_t OnRecvAudioDecodeCallback(int32_t, AudioJbDecode*); -static int32_t OnRecvCmdData(uint8_t*, uint32_t); -static void OnGotTransLog(const char*, uint32_t length); -static void OnNeedKeyFrameCallback(); +int32_t OnRecvVideoStreamData(uint8_t*, uint32_t); +int32_t OnRecvAudioStreamData(uint8_t*, uint32_t); +int32_t OnRecvAudioDecodeCallback(int32_t, AudioJbDecode*); +int32_t OnRecvCmdData(uint8_t*, uint32_t); +void OnGotTransLog(const char*, uint32_t length); +void OnNeedKeyFrameCallback(); +static CasClientListener *g_listener = nullptr; +static std::mutex g_mutex; +static int g_plcCount = 0; +static OpusDecoder *g_opusDecoder = nullptr; +static std::shared_ptr g_hrtpLogger = nullptr; +static bool g_isReady = false; CasMright::CasMright() { - INFO("CasMright constructor."); + INFO("CasMright construct."); m_mtrans = nullptr; - m_listener = nullptr; + g_listener = nullptr; + g_isReady = false; } CasMright::~CasMright() { - INFO("CasMright deconstructor."); + INFO("CasMright destruct."); m_mtrans = nullptr; } -bool CasMright::init(std::string &ip, uint16_t port) +int CasMright::init(uint32_t ip, uint16_t port, std::string &logPath) { - INFO("CasMright init ip = %s port = %d.", ip.c_str(), port); m_mtrans = new (std::nothrow) NetTrans(); if (m_mtrans == nullptr) { ERR("Create mtrans instance failed."); - return false; + 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("CasMright init ip = %s port = %d.", m_ip.c_str(), port); - m_mtrans->Init(PEER_CLIENT, ip.c_str(), port, param, + g_isReady = false; + return m_mtrans->Init(PEER_CLIENT, m_ip.c_str(), port, param, OnRecvVideoStreamData, OnRecvAudioStreamData, nullptr, @@ -43,25 +86,75 @@ bool CasMright::init(std::string &ip, uint16_t port) OnRecvCmdData, OnRecvAudioDecodeCallback, OnGotTransLog); - return true; } -bool CasMright::deinit() +int CasMright::deinit() { INFO("CasMright deinit."); if (m_mtrans != nullptr) { delete m_mtrans; m_mtrans = nullptr; } - return true; + return 0; +} + +int CasMright::connect() +{ + INFO("Connect"); + return 0; +} + +int CasMright::reconnect() +{ + INFO("Reonnect"); + stop(); + return start(); +} + +int CasMright::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: + m_mtrans->SendMotionEventData(buffer, length); + break; + case VirtualSensor: + m_mtrans->SendSensorData(buffer, length); + break; + } + return length; } -bool CasMright::start() +int CasMright::start() { - INFO("CasMright start."); + INFO("Start."); if (m_mtrans == nullptr) { - ERR("Mtrans instance is null."); - return false; + ERR("m_mtrans is null."); + return -1; } int ret = m_mtrans->Start(); if (ret < 0) { @@ -69,51 +162,140 @@ bool CasMright::start() } else { INFO("Mtrans start success."); } - return (ret >= 0); + int err = 0; + if (g_opusDecoder == nullptr) { + g_opusDecoder = opus_decoder_create(48000, 2, &err); + } + g_isReady = false; + return 0; } -bool CasMright::stop() +int CasMright::stop() { - INFO("CasMright stop."); + INFO("Stop."); if (m_mtrans == nullptr) { ERR("Mtrans instance is null."); return false; } - m_mtrans->Stop(); - return true; + g_isReady = false; + return m_mtrans->Stop();; } void CasMright::setListener(CasClientListener *listener) { - m_listener = listener; + g_listener = listener; +} + +bool CasMright::isReady() +{ + return g_isReady; } int32_t OnRecvVideoStreamData(uint8_t* data, uint32_t length) { + if (g_listener == nullptr) { + return -1; + } + uint8_t *videoData = new(std::nothrow) uint8_t[length]; + if (videoData != nullptr) { + memcpy_s(videoData, length, data, length); + g_listener->onNewPacket(CasMsgType::Video, videoData, length, SOURCE_MRIGHT); + } + g_isReady = 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); + + if (g_listener != nullptr) { + g_listener->onNewPacket(CasMsgType::Audio, buffer, dataLen, SOURCE_MRIGHT); + } 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() { - INFO("Need key frame callback.."); + if (g_listener != nullptr) { + g_listener->onNewCmd(CAS_REQUEST_CAMERA_KEY_FRAME, "camera need keyframe"); + } } int32_t OnRecvCmdData(uint8_t* data, uint32_t length) { - INFO("OnRecvCmdData"); + if (g_listener != nullptr) { + stream_msg_head_t *header = (stream_msg_head_t *)data; + g_listener->onNewPacket(header->type, data, length, SOURCE_MRIGHT); + } return 0; +} + +int CasMright::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/CasMright.h b/cloudphone/src/main/cpp/cas_controller/CasMright.h index a1f5450..90793bd 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasMright.h +++ b/cloudphone/src/main/cpp/cas_controller/CasMright.h @@ -1,26 +1,45 @@ +// 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_CASMRIGHT_H #define CLOUDAPPSDK_CASMRIGHT_H +#include #include -#include "net_trans.h" +#include "ICasTransmission.h" class CasClientListener; +class NetTrans; -class CasMright { +class CasMright : public ICasTransmission { public: CasMright(); ~CasMright(); - - bool init(std::string &ip, uint16_t port); - bool deinit(); - bool start(); - bool stop(); - bool isOk(); - void setListener(CasClientListener *listener); + 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(CasClientListener *listener) override; + bool isReady() override; + int getRecvStats(int type, void *value) override; private: + std::string m_ip; NetTrans *m_mtrans; - CasClientListener *m_listener; }; -#endif //CLOUDAPPSDK_CASMRIGHT_H +#endif // CLOUDAPPSDK_CASMRIGHT_H diff --git a/cloudphone/src/main/cpp/cas_controller/CasTcp.cpp b/cloudphone/src/main/cpp/cas_controller/CasTcp.cpp new file mode 100644 index 0000000..8558599 --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasTcp.cpp @@ -0,0 +1,188 @@ +// 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 "CasTcp.h" +#include "../cas_socket/CasTcpSocket.h" +#include "../cas_common/CasLog.h" +#include "../cas_common/CasBuffer.h" + +CasTcp::CasTcp() +{ + INFO("CasTcp construct."); + m_ip = 0; + m_port = 0; + m_listener = nullptr; + m_casClientSocket = nullptr; + m_isReady = false; +} + +CasTcp::~CasTcp() +{ + INFO("CasTcp destruct."); +} + +int CasTcp::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 CasTcp::deinit() +{ + INFO("Deinit."); + return 0; +} + +int CasTcp::connect() +{ + INFO("Connect."); + m_casClientSocket = new (std::nothrow) CasTcpClientSocket(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."); + return result; + } + m_isReady = true; + INFO("Connect server success."); + return 0; +} + +int CasTcp::start() +{ + INFO("Start."); + m_isTaskRun = true; + m_task = std::thread(&CasTcp::recvTask, this); + return 0; +} + +int CasTcp::stop() +{ + INFO("Stop."); + if (m_isTaskRun) { + m_isTaskRun = false; + m_task.join(); + delete m_casClientSocket; + m_casClientSocket = nullptr; + m_isReady = false; + } + return 0; +} + +int CasTcp::reconnect() +{ + INFO("Reconnect."); + stop(); + if (connect() != 0) { + ERR("Reconnect failed."); + return -1; + } + return start();; +} + +int CasTcp::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 CasTcp::setListener(CasClientListener *listener) +{ + m_listener = listener; +} + +bool CasTcp::isReady() +{ + return m_isReady; +} + +// private method +int CasTcp::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 CasTcp::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) { + // ERR("Read message header failed ret = %d headerLength = %d.", 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) { + // ERR("Read message body failed ret = %d bodyLength %d type= %d.", ret, bodyLength, msgHead.type); + FreeBuffer(message); + continue; + } + (void)memcpy_s(message, headerLength, &msgHead, headerLength); + dispatchMessage(msgHead, message, bodyLength + headerLength); + } +} + +void CasTcp::dispatchMessage(streamMsgHead &header, uint8_t *message, uint32_t length) +{ + if (m_listener != nullptr) { + m_listener->onNewPacket(header.type, message, length, SOURCE_TCP); + } +} + +int CasTcp::getRecvStats(int , void *) +{ + return 0; +} \ No newline at end of file diff --git a/cloudphone/src/main/cpp/cas_controller/CasTcp.h b/cloudphone/src/main/cpp/cas_controller/CasTcp.h new file mode 100644 index 0000000..4a49b00 --- /dev/null +++ b/cloudphone/src/main/cpp/cas_controller/CasTcp.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 CasTcp : public ICasTransmission { +public: + CasTcp(); + ~CasTcp(); + 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(CasClientListener *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; + CasTcpClientSocket *m_casClientSocket; + CasClientListener *m_listener; + bool m_isReady; +}; + +#endif // CLOUDAPPSDK_CASTCP_H diff --git a/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.cpp b/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.cpp index 83c86f1..116a16b 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.cpp +++ b/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.cpp @@ -1,10 +1,24 @@ +// 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" - -#define DIRECT_DECODE 0 +#include "CasMsgCode.h" +#include "../CasCommon.h" CasVideoHandler::CasVideoHandler(CasMessageResult *result) :CasMessageHandler(result) @@ -12,37 +26,47 @@ CasVideoHandler::CasVideoHandler(CasMessageResult *result) INFO("CasVideoHandler constructor."); m_videoPktStream = nullptr; m_nativeWindow = nullptr; + m_videoThread = nullptr; m_frameType = FrameType::H264; m_rotation = 0; } CasVideoHandler::~CasVideoHandler() { - INFO("CasVideoHandler deconstructor."); + 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 = - std::make_shared(m_nativeWindow, m_frameType, m_rotation); + 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_videoPktStream =std::make_shared(true); - if (m_videoThread == nullptr) { - ERR("Couldn't create CasDataPipe."); - m_videoThread = nullptr; - return -1; - } - m_videoThread->SetDecodePktHandle(m_videoPktStream.get()); + m_videoThread->SetDecodePktHandle(m_videoPktStream); if (m_videoThread->Start() != 0) { + delete m_videoThread; m_videoThread = nullptr; - m_videoPktStream = nullptr; + return -1; } + m_videoThread->GetVideoEngine()->SetFirstVidFrameListener(this); return 0; } @@ -51,21 +75,27 @@ int CasVideoHandler::stop() INFO("Stop."); std::lock_guard lockGuard(m_lock); if (m_videoThread == nullptr) { - ERR("Already stop."); + WARN("Already stop."); return -1; } - m_videoThread->Exit(); + if (m_videoThread->GetThreadStatus() == CAS_THREAD_RUNNING) { + m_videoThread->Exit(); + } + delete m_videoThread; m_videoThread = nullptr; - m_videoPktStream = nullptr; return 0; } -void CasVideoHandler::handleMeassage(uint8_t *message, uint32_t length) +void CasVideoHandler::handleMeassage(uint8_t *message, uint32_t length, int) { std::lock_guard lockGuard(m_lock); - if (m_videoPktStream != nullptr) { - m_videoPktStream->Handle(message); + 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) @@ -76,3 +106,11 @@ void CasVideoHandler::setParameters(ANativeWindow *nativeWindow, FrameType frame 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.cpp+++ b/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.cpp+++ deleted file mode 100644 index 5150e9d..0000000 --- a/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.cpp+++ +++ /dev/null @@ -1,129 +0,0 @@ -#include -#include "CasVideoHandler.h" -#include "../cas_decoder/CasVideoEngineCommon.h" -#include "../cas_common/CasMsg.h" -#include "../cas_common/CasBuffer.h" - -#define DIRECT_DECODE 0 - -CasVideoHandler::CasVideoHandler(CasMessageResult *result) - :CasMessageHandler(result) -{ - INFO("video handler constructor."); - m_decodeTaskRun = false; - m_rotation = 0; - m_videoPktStream = nullptr; - m_videoEngine = nullptr; - m_nativeWindow = nullptr; -} - -CasVideoHandler::~CasVideoHandler() -{ - INFO("video handler deconstructor."); - stop(); -} - -int CasVideoHandler::start() -{ - INFO("Strat video handler."); - m_videoEngine = new (std::nothrow) CasVideoEngine(); - m_videoPktStream = new (std::nothrow) CasDataPipe(true); - if (m_videoEngine == nullptr || m_videoPktStream == nullptr) { - ERR("create video instance failed, m_videoEngine = %p m_videoPktStream = %p", - m_videoEngine, m_videoPktStream); - goto FAILED; - } - m_videoEngine->InitDecoder(m_nativeWindow, DecoderType::DECODER_TYPE_HW, - m_frameType, m_rotation); - m_videoEngine->StartDecoder(); - m_decodeTaskRun = true; - m_decodeThread = std::thread(&CasVideoHandler::decodeThread, this); - return 0; - -FAILED: - if (m_videoEngine != nullptr) { - delete m_videoEngine; - m_videoEngine = nullptr; - } - if (m_videoPktStream == nullptr) { - delete m_videoPktStream; - m_videoPktStream = nullptr; - } - return -1; -} - -int CasVideoHandler::stop() -{ - INFO("Stop video handler."); - if (m_decodeTaskRun) { - m_decodeTaskRun = false; - m_videoPktStream->Exit(); - m_decodeThread.join(); - } - if (m_videoEngine != nullptr) { - m_videoEngine->StopDecoder(); - m_videoEngine->DestroyDecoder(); - delete m_videoEngine; - m_videoEngine = nullptr; - } - if (m_videoPktStream != nullptr) { - delete m_videoPktStream; - m_videoPktStream = nullptr; - } - return 0; -} - -void CasVideoHandler::handleMeassage(uint8_t *message, uint32_t length) -{ - if (DIRECT_DECODE) { - stream_msg_head_t *header = (stream_msg_head_t *) message; - uint32_t datasize = header->GetPayloadSize(); - uint8_t *videoData = message + sizeof(stream_msg_head_t); - int ret = m_videoEngine->DecodeFrame(videoData, datasize); - if (ret == VIDEO_ENGINE_CLIENT_DECODE_ERR) { - ERR("video engine decode error."); - } else if (ret == VIDEO_ENGINE_CLIENT_PARAM_INVALID) { - ERR("video engine params invalid."); - } - FreeBuffer(message); - } else { - if (m_videoPktStream != nullptr) { - m_videoPktStream->Handle(message); - } - } -} - -void CasVideoHandler::setParameters(ANativeWindow *nativeWindow, FrameType frameType, int rotation) -{ - m_nativeWindow = nativeWindow; - m_frameType = frameType; - m_rotation = rotation; -} - -void CasVideoHandler::decodeThread() -{ - const int timeout = 10; // 5毫秒 - uint8_t *onePkt; - while (m_decodeTaskRun) { - onePkt = (uint8_t*)m_videoPktStream->GetNextPktWaitFor(timeout); - if (onePkt == nullptr) { - //WARN("video package is empty"); - continue; - } - stream_msg_head_t *header = (stream_msg_head_t *) onePkt; - uint32_t datasize = header->GetPayloadSize(); - uint8_t *videoData = onePkt + sizeof(stream_msg_head_t); - - int ret = 0; - do { - ret = m_videoEngine->DecodeFrame(videoData, datasize); - if (ret == VIDEO_ENGINE_CLIENT_DECODE_ERR) { - ERR("video engine decode error."); - } else if (ret == VIDEO_ENGINE_CLIENT_PARAM_INVALID) { - ERR("video engine params invalid."); - } - } while(ret != SUCCESS && m_decodeTaskRun); - - FreeBuffer(onePkt); - } -} diff --git a/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.h b/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.h index c2ce50d..82a6c7d 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.h +++ b/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.h @@ -1,3 +1,17 @@ +// 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 @@ -11,26 +25,27 @@ #include "../cas_decoder/CasVideoEngine.h" #include "../cas_service/CasVideoHDecodeThread.h" -class CasVideoHandler : public CasMessageHandler{ +class CasVideoHandler :public CasMessageHandler, public CasFirstVideoFrameListener{ public: CasVideoHandler(CasMessageResult *result); ~CasVideoHandler(); + int init() override; int start() override; int stop() override; - void handleMeassage(uint8_t *message, uint32_t length) override; + void handleMeassage(uint8_t *message, uint32_t length, int source) override; void setParameters(ANativeWindow *nativeWindow, FrameType frameType, int rotation); -private: - void decodeThread(); +public: + void OnFirstFrame() override; private: - std::shared_ptr m_videoThread; - std::shared_ptr m_videoPktStream; + CasVideoHDecodeThread *m_videoThread; + CasDataPipe *m_videoPktStream; std::mutex m_lock; ANativeWindow *m_nativeWindow; FrameType m_frameType; int m_rotation; }; -#endif //CLOUDAPPSDK_CASVIDEOHANDLER_H +#endif // CLOUDAPPSDK_CASVIDEOHANDLER_H diff --git a/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.h+++ b/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.h+++ deleted file mode 100644 index f8720e8..0000000 --- a/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.h+++ +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef CLOUDAPPSDK_CASVIDEOHANDLER_H -#define CLOUDAPPSDK_CASVIDEOHANDLER_H - -#include -#include -#include -#include "CasMessageHandler.h" -#include "../cas_decoder/CasVideoEngineCommon.h" -#include "../cas_service/CasDataPipe.h" -#include "../cas_decoder/CasVideoEngine.h" - -class CasVideoHandler : public CasMessageHandler{ -public: - CasVideoHandler(CasMessageResult *result); - ~CasVideoHandler(); - - int start() override; - int stop() override; - void handleMeassage(uint8_t *message, uint32_t length) override; - void setParameters(ANativeWindow *nativeWindow, FrameType frameType, int rotation); - -private: - void decodeThread(); - -private: - int m_rotation; - CasDataPipe *m_videoPktStream; - CasVideoEngine *m_videoEngine; - ANativeWindow *m_nativeWindow; - FrameType m_frameType; - bool m_decodeTaskRun; - std::thread m_decodeThread; -}; - -#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 0000000..b4aa7e8 --- /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_MRIGHT = 0, + SOURCE_TCP +}; + +class CasClientListener { +public: + CasClientListener() {}; + virtual ~CasClientListener() {}; + 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(CasClientListener *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 66aea83..2484a35 100644 --- a/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.cpp +++ b/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.cpp @@ -12,6 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +#if __ANDROID_API__ >= 24 +#include +#include +#endif + #include "CasDecodeController.h" #include "CasVideoUtil.h" @@ -73,7 +78,6 @@ void FrameCallback(long frameTime, void* opaque) return; } - bool isDecodeFirstFrame = false; int iRet = 0; uint64_t startUs = 0; uint64_t nowUs = 0; @@ -139,8 +143,8 @@ void FrameCallback(long frameTime, void* opaque) uint64_t display2End = casVideoUtil->GetNow(); if (iRet == DECODER_SUCCESS) { - if (!isDecodeFirstFrame) { - isDecodeFirstFrame = true; + if (!controller->m_firstFrame) { + controller->m_firstFrame = true; if (firstVideoFrameListener != nullptr) { firstVideoFrameListener->OnFirstFrame(); } @@ -215,7 +219,7 @@ void OutputTaskEntry(CasDecodeController *controller) return; } -#if 0//__ANDROID_API__ >= 24 +#if __ANDROID_API__ >= 24 ALooper* looper = ALooper_forThread(); if (looper == nullptr) { ERR("Failed to get looper, preparing new one"); @@ -223,7 +227,7 @@ void OutputTaskEntry(CasDecodeController *controller) } controller->SetSubThreadStatus(true); controller->m_choreographer = AChoreographer_getInstance(); - ERR("cxchun controller = %p controller->m_choreographer %p", controller, controller->m_choreographer); + AChoreographer_postFrameCallback(controller->m_choreographer, FrameCallback, (void *)controller); while (controller->IsStatus(EngineStat::ENGINE_RUNNING)) { @@ -332,6 +336,7 @@ uint32_t CasDecodeController::Start() return VIDEO_ENGINE_CLIENT_START_ERR; } } + m_firstFrame = false; SetStatus(EngineStat::ENGINE_RUNNING); // to start sub-thread for the output of decoded frame std::thread outputTask(OutputTaskEntry, this); diff --git a/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.h b/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.h index 3552288..954a59b 100644 --- a/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.h +++ b/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.h @@ -184,6 +184,7 @@ public: uint32_t m_UpdatePatternOffset = 0; uint32_t m_HoldFrames = 0; + bool m_firstFrame = false; bool isStartDiscard = false; // 是否触发丢帧策略,进行日志打印 AChoreographer *m_choreographer = nullptr; diff --git a/cloudphone/src/main/cpp/cas_service/CasVideoHDecodeThread.cpp b/cloudphone/src/main/cpp/cas_service/CasVideoHDecodeThread.cpp index b4b77d4..11f193c 100644 --- a/cloudphone/src/main/cpp/cas_service/CasVideoHDecodeThread.cpp +++ b/cloudphone/src/main/cpp/cas_service/CasVideoHDecodeThread.cpp @@ -22,19 +22,6 @@ using namespace std; -class CasFirstVideoFrameImpl : public CasFirstVideoFrameListener { -public: - void OnFirstFrame() override - { -#if 0 // cxchun - if (CasController::GetInstance() != nullptr) { - CasController::GetInstance()->NotifyFirstVideoFrame(); - } -#endif - } - -}; - class CasVideoDecodeStatImpl : public CasVideoDecodeStatListener { public: void OnDecodeOneFrame(uint64_t decTime) override @@ -44,7 +31,8 @@ public: }; CasVideoHDecodeThread:: -CasVideoHDecodeThread(ANativeWindow *nativeWindow, FrameType frameType, int rotationDegrees) +CasVideoHDecodeThread(ANativeWindow *nativeWindow, FrameType frameType, + int rotationDegrees, CasFirstVideoFrameListener *listener) { this->m_nativeWindow = nativeWindow; this->m_videoEngine = nullptr; @@ -55,6 +43,7 @@ CasVideoHDecodeThread(ANativeWindow *nativeWindow, FrameType frameType, int rota this->m_threadStatus = CAS_THREAD_INIT; this->m_frameType = frameType; this->m_rotationDegrees = rotationDegrees; + this->m_firstVideoFrame = listener; } CasVideoHDecodeThread::~CasVideoHDecodeThread() @@ -64,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; @@ -183,19 +167,12 @@ 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) { @@ -274,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 be697bf..13299a0 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_socket/CasTcpClientSocket.cpp b/cloudphone/src/main/cpp/cas_socket/CasTcpClientSocket.cpp index 50ea560..6e20589 100644 --- a/cloudphone/src/main/cpp/cas_socket/CasTcpClientSocket.cpp +++ b/cloudphone/src/main/cpp/cas_socket/CasTcpClientSocket.cpp @@ -219,12 +219,3 @@ int CasTcpClientSocket::Reconnect() return this->Connect(); } - -void CasTcpClientSocket::Close() -{ - if (-1 != m_fd) { - shutdown(m_fd, SHUT_RDWR); - close(m_fd); - m_fd = -1; - } -} \ No newline at end of file diff --git a/cloudphone/src/main/cpp/cas_socket/CasTcpSocket.h b/cloudphone/src/main/cpp/cas_socket/CasTcpSocket.h index b09af3b..5096052 100644 --- a/cloudphone/src/main/cpp/cas_socket/CasTcpSocket.h +++ b/cloudphone/src/main/cpp/cas_socket/CasTcpSocket.h @@ -58,8 +58,6 @@ public: int CreateSocket(); - void Close(); - private: int CasCreateTcpClient(uint32_t socketOption, uint32_t remoteIp, uint16_t remotePort, uint32_t localIp, uint16_t localPort); diff --git a/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp b/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp index 84cf51b..d82ce95 100644 --- a/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp +++ b/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp @@ -119,13 +119,6 @@ int CasStreamBuildSender::SendDataToServer(CasMsgType type, const void *buf, siz for (size_t pos = 0; pos < dataLen;) { ssize_t stat = m_client->send((uint8_t*)outBuffer + pos, dataLen - pos); - -#if 0 //MTRANS_ENABLED - if (m_mtrans != nullptr && type == HeartBeat && m_client->GetStatus() == SOCKET_STATUS_RUNNING) { - m_mtrans->SendCmdData(reinterpret_cast(outBuffer + pos), dataLen - pos); - } -#endif - if (stat < 0) { ERR("Socket send failed: %s.", strerror(errno)); free(outBuffer); diff --git a/cloudphone/src/main/cpp/cas_stream/CasStreamRecvParser.cpp b/cloudphone/src/main/cpp/cas_stream/CasStreamRecvParser.cpp index 7de5128..85ac47f 100644 --- a/cloudphone/src/main/cpp/cas_stream/CasStreamRecvParser.cpp +++ b/cloudphone/src/main/cpp/cas_stream/CasStreamRecvParser.cpp @@ -69,28 +69,6 @@ CasStreamParseThread::~CasStreamParseThread() this->m_streamRecvParser = nullptr; } -#if 0 //cxchun -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); -} -#endif - void HandleCompletePktMsg(CasStreamParseThread *streamParseThread, streamMsgHead *msgHead, unsigned char *recvBuf, unsigned int pktStartPos) { diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/api/CloudAppScreenshotListener.java b/cloudphone/src/main/java/com/huawei/cloudphone/api/CloudAppScreenshotListener.java index 2361971..7a4acb3 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/api/CloudAppScreenshotListener.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/api/CloudAppScreenshotListener.java @@ -1,5 +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/ICloudPhone.java b/cloudphone/src/main/java/com/huawei/cloudphone/api/ICloudPhone.java index 9c301e2..016d07f 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/api/ICloudPhone.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/api/ICloudPhone.java @@ -62,16 +62,6 @@ public interface ICloudPhone { */ void exitCloudPhone() throws Exception; - /** - * 开始截图 - */ - void startScreenshot(final Map params, CloudAppScreenshotListener captureListener); - - /** - * 停止截图 - */ - void stopScreenshot(); - /** * 设置音视频参数 * 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 0000000..7723219 --- /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 64b2364..7b3167b 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; @@ -43,7 +44,6 @@ import android.widget.FrameLayout; import android.widget.Toast; import com.huawei.cloudphone.BuildConfig; -import com.huawei.cloudphone.api.CloudAppScreenshotListener; import com.huawei.cloudphone.api.CloudAppDataListener; import com.huawei.cloudphone.api.CloudPhoneClipboardListener; import com.huawei.cloudphone.api.CloudPhoneOrientationChangeListener; @@ -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; @@ -93,12 +95,10 @@ public class CloudPhoneImpl implements ICloudPhone { private static final int CMD_BACKGROUND_TIMEOUT = 15; private static final int CMD_RECONNECT = 16; private static final int CMD_SET_MEDIA_CONFIG = 17; - private static final byte CMD_START_SCREENSHOT = 18; - private static final byte CMD_STOP_SCREENSHOT = 19; 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(); @@ -143,7 +143,6 @@ public class CloudPhoneImpl implements ICloudPhone { //virtual devices private VirtualDeviceSession mVirtualDeviceSession = null; private RingBufferVirtualDeviceIO mRingBufferVirtualDeviceIO = null; - private CloudAppScreenshotListener mSnapshotListener = null; public CloudPhoneImpl() { mCurrentState = STATE_DEINIT; @@ -162,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(); @@ -190,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; @@ -270,23 +276,6 @@ public class CloudPhoneImpl implements ICloudPhone { } } - @Override - public void startScreenshot(final Map params, - CloudAppScreenshotListener captureListener) { - mSnapshotListener = captureListener; - Message msg = new Message(); - msg.what = CMD_START_SCREENSHOT; - msg.obj = params; - mCmdHandler.sendMessage(msg); - } - - @Override - public void stopScreenshot() { - Message msg = new Message(); - msg.what = CMD_STOP_SCREENSHOT; - mCmdHandler.sendMessage(msg); - } - @Override public void setMediaConfig(HashMap mediaConfig) { if (mCmdHandler == null) { @@ -408,6 +397,10 @@ public class CloudPhoneImpl implements ICloudPhone { } public void handleStartCmd(final Activity activity, final ViewGroup views) { + if (null == activity) { + CASLog.e(TAG, "handle start cmd fail, activity is null."); + return; + } if (mCurrentState != STATE_INIT) { CASLog.e(TAG, "mCurrentState = " + mCurrentState); return; @@ -475,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; @@ -813,25 +803,6 @@ public class CloudPhoneImpl implements ICloudPhone { } } - private void handleStartCapture(Message message) { - Map parameters = (Map)message.obj; - mStartParas = parameters; - mConnectorInfo = new CasConnectorInfo(); - if (!mConnectorInfo.initConnectorParams(mStartParas)) { - CASLog.e(TAG, "Init connector params failed."); - throw new IllegalArgumentException("Init connector params failed."); - } - mProccessor.setCasConnectorInfo(mConnectorInfo); - mProccessor.startJniRecv(); - mProccessor.startCapture(); - } - - private void handleStopCapture(Message message) { - mProccessor.stopCapture(); - mProccessor.stopJniRecv(); - mSnapshotListener = null; - } - private boolean sendTouchEvent(int id, int action, final int x1, final int y1, final int pressure, long time) { int orientation = 0; int newOrientation = 0; @@ -876,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); @@ -940,14 +911,6 @@ public class CloudPhoneImpl implements ICloudPhone { CASLog.i(TAG, "CMD_RECONNECT"); handleReconnectCmd(); break; - case CMD_START_SCREENSHOT: - CASLog.i(TAG, "CMD_STARTCAPTURE"); - handleStartCapture(msg); - break; - case CMD_STOP_SCREENSHOT: - CASLog.i(TAG, "CMD_STOPCAPTURE"); - handleStopCapture(msg); - break; default: break; } @@ -958,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) { @@ -967,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(); @@ -978,28 +943,29 @@ 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 { - if (mSnapshotListener != null) { - mSnapshotListener.onRecvPicture(data); - } } } @@ -1070,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 0000000..053c234 --- /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, orientation, displayWidth, displayHeight); + 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, orientation, displayWidth, displayHeight); + } + } + 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, orientation, displayWidth, displayHeight); + } + 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, displayWidth, displayHeight); + } + } + 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 2c0034e..2e50bd6 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/audio/AudioTrackerCallback.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/audio/AudioTrackerCallback.java @@ -219,6 +219,7 @@ public class AudioTrackerCallback { int userSize = msg.getSize(); byte[] buffer = msg.readBytes(userSize); +<<<<<<< HEAD if (userSize == USERSIZE_OPUS) { // decode and play short[] data = new short[bufferSizeInBytes]; @@ -234,6 +235,9 @@ public class AudioTrackerCallback { } else { CASLog.e(TAG, "audioTrackPlayer failed to write, invalid userSize."); } +======= + mTrackPlayer.write(buffer, 0, userSize, AudioTrack.WRITE_NON_BLOCKING); +>>>>>>> 32b51a0... AndroidSDK支持截图 } private void handleStop(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 new file mode 100644 index 0000000..1120b55 --- /dev/null +++ b/cloudphone/src/main/java/com/huawei/cloudphone/datacenter/CasRecvPktDispatcher.java @@ -0,0 +1,130 @@ +/* + * 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 0090f1f..c26eda2 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JNIWrapper.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JNIWrapper.java @@ -65,23 +65,18 @@ public class JNIWrapper { System.loadLibrary("cloudapp"); } - public JNIWrapper() { - mNativeObject = init(); - } - - public void finalize() { - deinit(mNativeObject); + public JNIWrapper(String logPath) { + mNativeObject = init(logPath); } public long getNativeObj() { return mNativeObject; } - public native long init(); + 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); @@ -96,9 +91,9 @@ public class JNIWrapper { public native void stop(long nativeObject, boolean isHome); - public native void pause(long nativeObject); + public native boolean pause(long nativeObject); - public native void resume(long nativeObject, Surface surface); + public native boolean resume(long nativeObject, Surface surface); public native boolean startScreenshot(long nativeObject); 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 3a54b85..22377f6 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JniBridge.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JniBridge.java @@ -23,94 +23,161 @@ import java.util.HashMap; public class JniBridge { private JNIWrapper mJniWrapper; private long mNativeObject; + private final String mLogPath; - public JniBridge() { - mJniWrapper = new JNIWrapper(); + public JniBridge(String logPath) { + mLogPath = logPath; + mJniWrapper = null; + mNativeObject = 0; + } + + public boolean init() { + mJniWrapper = new JNIWrapper(mLogPath); mNativeObject = mJniWrapper.getNativeObj(); + return true; + } + + public void deinit() { + if (mNativeObject != 0) { + mJniWrapper.deinit(mNativeObject); + mNativeObject = 0; + } } 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) { + 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) { + 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) { + if (mNativeObject == 0) { + return false; + } return mJniWrapper.sendMotionEvent(mNativeObject, masterAxis, masterValue, secondaryAxis, secondaryValue); } public void setJniConf(String key, String value) { - mJniWrapper.setJniConf(mNativeObject, key, value); + if (mNativeObject != 0) { + mJniWrapper.setJniConf(mNativeObject, key, value); + } } public boolean start(Surface surface, boolean isHome) { + if (mNativeObject == 0) { + return false; + } return mJniWrapper.start(mNativeObject, surface, isHome); } public void stop(boolean isHome) { - mJniWrapper.stop(mNativeObject, isHome); + if (mNativeObject != 0) { + mJniWrapper.stop(mNativeObject, isHome); + } } - public void pause() { - mJniWrapper.pause(mNativeObject); + public boolean pause() { + if (mNativeObject == 0) { + return false; + } + return mJniWrapper.pause(mNativeObject); } - public void resume(Surface surface) { - mJniWrapper.resume(mNativeObject, surface); + 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() { - mJniWrapper.stopScreenshot(mNativeObject); + if (mNativeObject != 0) { + mJniWrapper.stopScreenshot(mNativeObject); + } } public boolean reconnect() { - return mJniWrapper.reconnect(mNativeObject); + if (mNativeObject == 0) { + return false; + } + return mJniWrapper.reconnect(mNativeObject); } public int getJniStatus() { + if (mNativeObject == 0) { + return -1; + } return mJniWrapper.getJniStatus(mNativeObject); } public boolean getConnectStatus() { + if (mNativeObject == 0) { + return false; + } return mJniWrapper.getConnectStatus(mNativeObject); } public void registerCasJNICallback(Object callback) { - mJniWrapper.registerCasJNICallback(mNativeObject, callback); + if (mNativeObject != 0) { + mJniWrapper.registerCasJNICallback(mNativeObject, callback); + } } public int getLag() { + if (mNativeObject != 0) { + return -1; + } return mJniWrapper.getLag(mNativeObject); } public String getVideoStreamStats() { + if (mNativeObject == 0) { + return ""; + } return mJniWrapper.getVideoStreamStats(mNativeObject); } public String getSimpleStreamStats() { + if (mNativeObject == 0) { + return ""; + } return mJniWrapper.getSimpleStreamStats(mNativeObject); } public boolean setMediaConfig(HashMap mediaConfigMap) { - if (mediaConfigMap != null) { - return mJniWrapper.setMediaConfig(mNativeObject, mediaConfigMap); - } else { + if (mNativeObject == 0) { return false; } + return mJniWrapper.setMediaConfig(mNativeObject, mediaConfigMap); } public boolean setRotation(int rotation) { + if (mNativeObject == 0) { + return false; + } return mJniWrapper.setRotation(mNativeObject, rotation); } } 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 3e00f51..1d92ed0 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/service/CasProcessor.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/service/CasProcessor.java @@ -16,16 +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.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; @@ -36,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 */ @@ -73,17 +72,18 @@ public class CasProcessor { /** * listener */ - private CloudPhoneImpl.CASListener mListener = null; + private CasProcessorListener mListener = null; private boolean mJniCallback; private Object mJniCallbackLock = new Object(); private JniBridge mJniBridge; public CasProcessor() { - mJniBridge = new JniBridge(); mJniCallback = false; } - public void init() { + public void init(String logPath) { + mJniBridge = new JniBridge(logPath); + mJniBridge.init(); CASLog.i(TAG, "init...."); CasCallback casCallback = new CasCallback() { @Override @@ -117,12 +117,17 @@ public class CasProcessor { case JNIWrapper.CHANNEL: mListener.onChannelDataRecv(data); break; - case JNIWrapper.VIRTUAL_DEVICE_DATA: - mListener.onVirtualDevDataRecv(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; } } } @@ -130,6 +135,10 @@ public class CasProcessor { mJniBridge.registerCasJNICallback(casCallback); } + public void deinit() { + mJniBridge.deinit(); + } + public void setEncryptData(String encryptData) { CASLog.i(TAG, "setEncryptData...."); mJniBridge.setJniConf(KEY_ENCRYPTED_DATA, encryptData); @@ -188,7 +197,7 @@ public class CasProcessor { return ret; } - public void registerListener(CloudPhoneImpl.CASListener listener) { + public void registerListener(CasProcessorListener listener) { CASLog.i(TAG, "registerListener...."); mListener = listener; } @@ -235,6 +244,7 @@ public class CasProcessor { } public boolean startJniRecv() { + CASLog.i(TAG, "startJniRecv..... "); synchronized (mJniCallbackLock) { CASLog.i(TAG, "startJniRecv... "); mAudioTrackerCallback = new AudioTrackerCallback(); 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 0000000..a54d5d1 --- /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 4a8f7ed..7d9c093 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; } -- Gitee From abdcb741dd8ce44a21724c2e297afb83f34a4b46 Mon Sep 17 00:00:00 2001 From: CaiFeng <2397707574@qq.com> Date: Fri, 1 Nov 2024 00:07:49 +0800 Subject: [PATCH 3/3] =?UTF-8?q?AndroidSDK=E6=94=AF=E6=8C=81=E6=88=AA?= =?UTF-8?q?=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cloudphone/src/main/cpp/CasController.cpp | 126 ++++++++-------- cloudphone/src/main/cpp/CasController.h | 35 +++-- .../cpp/cas_controller/CasAudioHandler.cpp | 94 ++++++++---- .../main/cpp/cas_controller/CasAudioHandler.h | 14 +- .../main/cpp/cas_controller/CasCmdHandler.cpp | 14 +- .../main/cpp/cas_controller/CasCmdHandler.h | 6 +- .../cpp/cas_controller/CasCommonHandler.cpp | 7 +- .../cpp/cas_controller/CasCommonHandler.h | 7 +- .../cpp/cas_controller/CasHandlerManager.cpp | 139 +++++++++--------- .../cpp/cas_controller/CasHandlerManager.h | 29 ++-- .../CasHandlerResultProcessor.cpp | 62 ++++++++ .../CasHandlerResultProcessor.h | 38 +++++ ...eatHandler.cpp => CasHeartbeatHandler.cpp} | 57 ++++--- ...earbeatHandler.h => CasHeartbeatHandler.h} | 22 +-- .../cpp/cas_controller/CasMessageHandler.h | 19 ++- ...CasMright.cpp => CasMtansTransmission.cpp} | 99 +++++++------ .../{CasMright.h => CasMtansTransmission.h} | 17 ++- .../{CasTcp.cpp => CasTcpTransmission.cpp} | 58 ++++---- .../{CasTcp.h => CasTcpTransmission.h} | 12 +- .../{CasClient.cpp => CasTransmission.cpp} | 105 ++++++------- .../{CasClient.h => CasTransmission.h} | 21 +-- .../cpp/cas_controller/CasVideoHandler.cpp | 11 +- .../main/cpp/cas_controller/CasVideoHandler.h | 4 +- .../cpp/cas_controller/ICasTransmission.h | 10 +- .../cpp/cas_decoder/CasDecodeController.cpp | 17 ++- .../cpp/cas_decoder/CasDecodeController.h | 2 +- .../main/cpp/cas_service/CasCmdController.cpp | 2 +- .../main/cpp/cas_service/CasCmdController.h | 4 +- .../src/main/cpp/cas_service/CasDataPipe.cpp | 6 +- .../src/main/cpp/cas_service/CasTouch.cpp | 8 +- .../src/main/cpp/cas_service/CasTouch.h | 4 +- .../cpp/cas_service/CasVideoHDecodeThread.cpp | 4 +- .../cpp/cas_stream/CasStreamBuildSender.cpp | 16 +- .../cpp/cas_stream/CasStreamBuildSender.h | 7 +- .../apiimpl/CloudPhoneScreenshotImpl.java | 8 +- .../audio/AudioTrackerCallback.java | 4 - .../datacenter/CasRecvPktDispatcher.java | 130 ---------------- .../jniwrapper/JniMessageListener.java | 10 -- 38 files changed, 626 insertions(+), 602 deletions(-) create mode 100644 cloudphone/src/main/cpp/cas_controller/CasHandlerResultProcessor.cpp create mode 100644 cloudphone/src/main/cpp/cas_controller/CasHandlerResultProcessor.h rename cloudphone/src/main/cpp/cas_controller/{CasHearbeatHandler.cpp => CasHeartbeatHandler.cpp} (72%) rename cloudphone/src/main/cpp/cas_controller/{CasHearbeatHandler.h => CasHeartbeatHandler.h} (69%) rename cloudphone/src/main/cpp/cas_controller/{CasMright.cpp => CasMtansTransmission.cpp} (73%) rename cloudphone/src/main/cpp/cas_controller/{CasMright.h => CasMtansTransmission.h} (76%) rename cloudphone/src/main/cpp/cas_controller/{CasTcp.cpp => CasTcpTransmission.cpp} (72%) rename cloudphone/src/main/cpp/cas_controller/{CasTcp.h => CasTcpTransmission.h} (83%) rename cloudphone/src/main/cpp/cas_controller/{CasClient.cpp => CasTransmission.cpp} (61%) rename cloudphone/src/main/cpp/cas_controller/{CasClient.h => CasTransmission.h} (76%) delete mode 100644 cloudphone/src/main/java/com/huawei/cloudphone/datacenter/CasRecvPktDispatcher.java delete mode 100644 cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JniMessageListener.java diff --git a/cloudphone/src/main/cpp/CasController.cpp b/cloudphone/src/main/cpp/CasController.cpp index 688baba..83c15c0 100644 --- a/cloudphone/src/main/cpp/CasController.cpp +++ b/cloudphone/src/main/cpp/CasController.cpp @@ -26,7 +26,7 @@ #include "CasExtVideoDataPipe.h" #include "opus.h" #include "CasVideoUtil.h" -#include "cas_controller/CasClient.h" +#include "cas_controller/CasTransmission.h" #include "cas_controller/CasHandlerManager.h" using namespace std; @@ -45,13 +45,13 @@ const uint64_t DURATION_USEC = 1000000ULL; CasController::CasController() { - INFO("constuctor."); + INFO("Constuctor."); m_isconnect = true; } CasController::~CasController() { - INFO("destuctor."); + INFO("Destuctor."); ReleaseResource(); } @@ -60,16 +60,16 @@ bool CasController::Start(ANativeWindow *nativeWindow, bool isHome) std::lock_guard lockGuard(this->m_lock); prepareParameters(); m_nativeWindow = nativeWindow; - m_casMessages.clear(); - m_casMessages.push_back(CasMsgType::Video); - m_casMessages.push_back(CasMsgType::HeartBeat); - m_casMessages.push_back(CasMsgType::CmdControl); - m_casMessages.push_back(CasMsgType::Orientation); - m_casMessages.push_back(CasMsgType::Audio); - m_casMessages.push_back(CasMsgType::VirtualCamera); - m_casMessages.push_back(CasMsgType::VirtualMicrophone); - m_casMessages.push_back(CasMsgType::VirtualSensor); - m_casMessages.push_back(CasMsgType::VirtualLocation); + 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); @@ -83,8 +83,8 @@ bool CasController::Start(ANativeWindow *nativeWindow, bool isHome) bool CasController::Stop(bool isHome) { std::lock_guard lockGuard(this->m_lock); - if (this->GetState() == STOPPED) { - INFO("Current state is stopped."); + if (this->GetState() == STOPPED || this->GetState() == INIT) { + INFO("Already stop."); return false; } map parameters = { { KEY_COMMAND, CMD_STOP_APP } }; @@ -94,8 +94,7 @@ bool CasController::Stop(bool isHome) } this->SetState(STOPPED); m_casHandlerManager->stop(); - m_casClient->stop(); - m_casClient->setListener(nullptr); + m_casTransmission->stop(); ReleaseResource(); m_rotationDegrees = 0; m_orientation = 0; @@ -129,6 +128,7 @@ bool CasController::Resume(ANativeWindow *nativeWindow) return false; } SetState(START_SUCCESS); + m_casHandlerManager->setVideoParameters(m_nativeWindow, m_frameType, m_rotationDegrees); return m_casHandlerManager->startVideoHandler() == 0; } else { return ProcessEnterForeground(nativeWindow); @@ -142,8 +142,6 @@ bool CasController::Reconnect() INFO("Reconnect failed because phone already stop."); return false; } - m_casHandlerManager->stop(); - m_casHandlerManager->start(); return ProcessReconnect(); } @@ -152,11 +150,12 @@ bool CasController::StartScreenshot() std::lock_guard lockGuard(m_lock); m_snapshotFlag = true; prepareParameters(); - m_casMessages.clear(); - m_casMessages.push_back(CasMsgType::HeartBeat); - m_casMessages.push_back(CasMsgType::CmdControl); - m_casMessages.push_back(CasMsgType::Orientation); - m_casMessages.push_back(CasMsgType::ScreenShot); + + 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; @@ -241,7 +240,7 @@ int CasController::JniSendData(CasMsgType type, uint8_t *data, int length) if (this->GetState() != START_SUCCESS) { return false; } - if (m_streamBuildSender == nullptr || !m_casClient->isReady()) { + if (m_streamBuildSender == nullptr || !m_casTransmission->isReady()) { return false; } return length == m_streamBuildSender->SendDataToServer(type, data, length); @@ -318,7 +317,7 @@ string CasController::CalcMaxDisconnectDuration(string backgroundTimeout) bool CasController::BuildConnection() { NotifyCommand(CAS_CONNECTING, CasMsgCode::GetMsg(CAS_CONNECTING)); - int connectRes = m_casClient->connect(); + int connectRes = m_casTransmission->connect(); if (connectRes == -1) { SetState(CONNECTION_FAILURE); NotifyCommand(CAS_SERVER_UNREACHABLE, CasMsgCode::GetMsg(CAS_SERVER_UNREACHABLE)); @@ -350,7 +349,7 @@ bool CasController::SendCommand(map parameters) uint64_t CasController::GetLag() { - return 0; + return m_lag; } bool CasController::GetConnectStatus() @@ -393,10 +392,18 @@ bool CasController::ProcessEnterForeground(ANativeWindow *nativeWindow) return false; } SetState(START_SUCCESS); + m_casHandlerManager->setVideoParameters(m_nativeWindow, m_frameType, m_rotationDegrees); m_casHandlerManager->startVideoHandler(); return true; } +void CasController::ResetDecoder() +{ + m_casHandlerManager->stopVideoHandler(); + m_casHandlerManager->setVideoParameters(m_nativeWindow, m_frameType, m_rotationDegrees); + m_casHandlerManager->startVideoHandler(); +} + bool CasController::ForceIFrame() { std::lock_guard lockGuard(this->m_lock); @@ -409,8 +416,6 @@ bool CasController::ForceIFrame() bool CasController::doForceIFrame() { - m_casHandlerManager->stopVideoHandler(); - m_casHandlerManager->startVideoHandler(); map parameters = { { KEY_COMMAND, CMD_REQ_IFRAME }, { KEY_SESSION_ID, m_sessionId } }; bool res = SendCommand(parameters); if (!res) { @@ -459,7 +464,9 @@ void CasController::onMessageRecv(stream_msg_head_t *header, uint8_t *body, int } else { m_rotationDegrees = 0; } - ForceIFrame(); + ResetDecoder(); + doForceIFrame(); + m_orientation = orientation; } if (m_casMessageCallback != nullptr) { m_casMessageCallback(header, body, length, m_callbackOpaque); @@ -516,16 +523,16 @@ std::string CasController::GetVideoRecvStats() std::stringstream stream; StreamRecvStats videoRecvStats; - if (m_casClient->getRecvStats(Video, &videoRecvStats) == 0) { + if (m_casTransmission->getRecvStats(Video, &videoRecvStats) == 0) { stream << "下行视频丢包 : " << videoRecvStats.lostRate << std::endl; stream << "视频接收码率 : " << videoRecvStats.recvBitrate << std::endl; } StreamRecvStats audioRecvStats; - if (m_casClient->getRecvStats(Audio, &audioRecvStats) == 0) { + if (m_casTransmission->getRecvStats(Audio, &audioRecvStats) == 0) { stream << "音频接受码率 : " << audioRecvStats.recvBitrate << std::endl; } StreamSendStats cmdSendStats; - if (m_casClient->getRecvStats(CmdControl, &cmdSendStats) == 0) { + if (m_casTransmission->getRecvStats(CmdControl, &cmdSendStats) == 0) { stream << "指令发送码率(需操作) : " << cmdSendStats.encBitrate << std::endl; } stream << "帧率 : " << GetCurrentFPS() << "fps" << std::endl; @@ -557,47 +564,54 @@ bool CasController::prepareParameters() bool CasController::ProcessStart() { - m_casClient = new (std::nothrow) CasClient(!m_snapshotFlag); - if (m_casClient == nullptr) { - ERR("Couldn't create CasClient."); + m_casTransmission = std::make_shared(!m_snapshotFlag); + if (m_casTransmission == nullptr) { + ERR("Couldn't create CasTransmission."); return false; } - m_casHandlerManager = new (std::nothrow) CasHandlerManager(this); + 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 = new (std::nothrow) CasStreamBuildSender(m_casClient); + m_streamBuildSender = std::make_shared(m_casTransmission); if (m_streamBuildSender == nullptr) { ERR("Couldn't create CasStreamBuildSender."); goto FAILED; } - m_cmdController = new (std::nothrow) CasCmdController(m_streamBuildSender); + m_cmdController = std::make_shared(m_streamBuildSender); if (m_cmdController == nullptr) { ERR("Couldn't create CasCmdController."); goto FAILED; } - m_touch = new (std::nothrow) CasTouch(m_casClient); + m_touch = std::make_shared(m_casTransmission); if (m_touch == nullptr) { ERR("Couldn't create CasTouch."); goto FAILED; } - for (auto &it : m_casMessages) { + for (auto &it : m_casMessageTypes) { m_casHandlerManager->createHandler(it); } - m_casClient->init(m_ip, m_port, m_logPath); - m_casClient->setListener(m_casHandlerManager); + m_casTransmission->init(m_ip, m_port, m_logPath); + m_casTransmission->setListener(m_casHandlerManager); m_casHandlerManager->init(); if (!BuildConnection()) { ERR("Connect failed."); goto FAILED; } - if (m_casClient->start() != 0) { + 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."); @@ -612,8 +626,9 @@ FAILED: bool CasController::ProcessReconnect() { + m_casHandlerManager->stop(false); NotifyCommand(CAS_RECONNECTING, CasMsgCode::GetMsg(CAS_RECONNECTING)); - int connectRes = m_casClient->reconnect(); + int connectRes = m_casTransmission->reconnect(); if (connectRes != 0) { this->SetState(CONNECTION_FAILURE); return false; @@ -635,35 +650,16 @@ bool CasController::ProcessReconnect() ERR("Failed to send reconnect command"); return false; } + m_casHandlerManager->start(false); SetConnectStatus(true); - //doForceIFrame(); SetState(START_SUCCESS); return true; } void CasController::ReleaseResource() { - if (m_casClient != nullptr) { - delete m_casClient; - m_casClient = nullptr; - } if (m_casHandlerManager != nullptr) { m_casHandlerManager->stop(); - m_casHandlerManager->clear(); - delete m_casHandlerManager; - m_casHandlerManager = nullptr; - } - if (m_streamBuildSender != nullptr) { - delete m_streamBuildSender; - m_streamBuildSender = nullptr; - } - if (m_cmdController != nullptr) { - delete m_cmdController; - m_cmdController = nullptr; - } - if (m_touch != nullptr) { - delete m_touch; - m_touch = nullptr; } } diff --git a/cloudphone/src/main/cpp/CasController.h b/cloudphone/src/main/cpp/CasController.h index 6b327ba..95d1e22 100644 --- a/cloudphone/src/main/cpp/CasController.h +++ b/cloudphone/src/main/cpp/CasController.h @@ -12,13 +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" @@ -33,10 +34,12 @@ #include "CasVideoHDecodeThread.h" #include "CasStreamRecvParser.h" #include "CasStreamBuildSender.h" +#include "cas_controller/CasHandlerResultProcessor.h" #include "libs/mtrans/include/net_trans.h" -class CasClient; +class CasTransmission; class CasHandlerManager; +class CasHandlerResultProcessor; typedef void *(*CasCmdCallback)(int, std::string &, void*); typedef void *(*CasMessageCallback)(stream_msg_head_t*, uint8_t*, int, void*); @@ -84,6 +87,10 @@ public: uint64_t GetLag(); + void SetLag(uint64_t lag) { + m_lag = lag; + } + JNIState GetState(); void SetState(JNIState state); @@ -104,7 +111,7 @@ private: void onMessageRecv(stream_msg_head_t *header, uint8_t *body, int length); friend class CasHandlerManager; - + friend class CasHandlerResultProcessor; private: bool prepareParameters(); bool ProcessStart(); @@ -115,6 +122,7 @@ private: bool SendCommand(std::map parameters); void ProcessEnterBackground(); bool ProcessEnterForeground(ANativeWindow *nativeWindow); + void ResetDecoder(); bool ForceIFrame(); bool IsValidMediaConfig(std::map mediaConfig); std::string CalcMaxDisconnectDuration(std::string backgroundTimeout); @@ -126,13 +134,8 @@ private: CasCmdCallback m_casCmdCallbck = nullptr; CasMessageCallback m_casMessageCallback = nullptr; - CasCmdController *m_cmdController = nullptr; - - CasStreamBuildSender *m_streamBuildSender = 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; @@ -157,12 +160,16 @@ private: uint64_t m_lastTimeRefreshFPS = 0; void *m_callbackOpaque = nullptr; bool m_snapshotFlag = false; - CasClient *m_casClient = nullptr; - CasHandlerManager *m_casHandlerManager = nullptr; - - std::vector m_casMessages; + 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/cas_controller/CasAudioHandler.cpp b/cloudphone/src/main/cpp/cas_controller/CasAudioHandler.cpp index 867a4f4..b7f154a 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasAudioHandler.cpp +++ b/cloudphone/src/main/cpp/cas_controller/CasAudioHandler.cpp @@ -24,16 +24,18 @@ const int SAMPLE_RATE = 48000; const int CHANNEL = 2; const int OPUS_FRAME_SIZE = 1920; -CasAudioHandler::CasAudioHandler(CasMessageResult *result) +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() @@ -46,12 +48,20 @@ 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; } @@ -59,46 +69,66 @@ int CasAudioHandler::stop() { INFO("Stop."); std::lock_guard lock(m_lock); - if (m_opusDecoder != nullptr) { - opus_decoder_destroy(m_opusDecoder); - m_opusDecoder = nullptr; + 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::handleMeassage(uint8_t *message, uint32_t length, int source) +void CasAudioHandler::handleMessage(uint8_t *message, uint32_t length, int source) { std::lock_guard lock(m_lock); - if (message == nullptr) { + if (m_audioPktStream == nullptr) { + FreeBuffer(message); return; } - stream_msg_head_t *header = (stream_msg_head_t *) message; - uint8_t *body = message + sizeof(stream_msg_head_t); - if (source == 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; + 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; } - } else if (source == SOURCE_MRIGHT) { - if (m_messageResult != nullptr) { - m_messageResult->onMessageRecv(header, body); + 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); } - 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 index 5f7e80e..b6c264b 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasAudioHandler.h +++ b/cloudphone/src/main/cpp/cas_controller/CasAudioHandler.h @@ -17,23 +17,31 @@ #include #include +#include +#include #include "CasMessageHandler.h" +#include "CasDataPipe.h" struct OpusDecoder; class CasAudioHandler : public CasMessageHandler{ public: - CasAudioHandler(CasMessageResult *result); + CasAudioHandler(std::shared_ptr &result); ~CasAudioHandler(); int init() override; int start() override; int stop() override; - void handleMeassage(uint8_t *message, uint32_t length, int dataSource) override; + void handleMessage(uint8_t *message, uint32_t length, int dataSource) override; + void processThread(); -private: +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 index f391bc6..bb7140e 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasCmdHandler.cpp +++ b/cloudphone/src/main/cpp/cas_controller/CasCmdHandler.cpp @@ -20,36 +20,36 @@ #include "../cas_service/CasAppCtrlCmdUtils.h" #include "../cas_common/CasBuffer.h" -CasCmdHandler::CasCmdHandler(CasMessageResult *result) +CasCmdHandler::CasCmdHandler(std::shared_ptr &result) :CasMessageHandler(result) { - INFO("cmd handler constructor."); + INFO("Constructor."); } CasCmdHandler::~CasCmdHandler() { - INFO("cmd handler deconstructor."); + INFO("Destructor."); } int CasCmdHandler::init() { - INFO("init."); + INFO("Init."); return 0; } int CasCmdHandler::start() { - INFO("cmd handler start."); + INFO("Cmd handler start."); return 0; } int CasCmdHandler::stop() { - INFO("cmd handler stop."); + INFO("Cmd handler stop."); return 0; } -void CasCmdHandler::handleMeassage(uint8_t *message, uint32_t length, int dataSource) +void CasCmdHandler::handleMessage(uint8_t *message, uint32_t length, int source) { if (message == nullptr) { return; diff --git a/cloudphone/src/main/cpp/cas_controller/CasCmdHandler.h b/cloudphone/src/main/cpp/cas_controller/CasCmdHandler.h index 03a00ae..1c40989 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasCmdHandler.h +++ b/cloudphone/src/main/cpp/cas_controller/CasCmdHandler.h @@ -22,15 +22,13 @@ class CasCmdHandler : public CasMessageHandler { public: - CasCmdHandler(CasMessageResult *result); + CasCmdHandler(std::shared_ptr &result); ~CasCmdHandler(); int init() override; int start() override; int stop() override; - void handleMeassage(uint8_t *message, uint32_t length, int dataSource) override; - -private: + 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 index 8ed2634..a54b5a3 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasCommonHandler.cpp +++ b/cloudphone/src/main/cpp/cas_controller/CasCommonHandler.cpp @@ -16,7 +16,7 @@ #include "../cas_common/CasMsg.h" #include "../cas_common/CasBuffer.h" -CasCommonHandler::CasCommonHandler(CasMessageResult *result) +CasCommonHandler::CasCommonHandler(std::shared_ptr &result) :CasMessageHandler(result) { } @@ -40,11 +40,8 @@ int CasCommonHandler::stop() return 0; } -void CasCommonHandler::handleMeassage(uint8_t *message, uint32_t length, int dataSource) +void CasCommonHandler::handleMessage(uint8_t *message, uint32_t length, int source) { - if (message == nullptr) { - return; - } stream_msg_head_t *header = (stream_msg_head_t *) message; uint8_t *body = message + sizeof(stream_msg_head_t); diff --git a/cloudphone/src/main/cpp/cas_controller/CasCommonHandler.h b/cloudphone/src/main/cpp/cas_controller/CasCommonHandler.h index f7271a2..174ecb4 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasCommonHandler.h +++ b/cloudphone/src/main/cpp/cas_controller/CasCommonHandler.h @@ -21,16 +21,13 @@ class CasCommonHandler : public CasMessageHandler { public: - CasCommonHandler(CasMessageResult *result); + CasCommonHandler(std::shared_ptr &result); ~CasCommonHandler(); int init() override; int start() override; int stop() override; - void handleMeassage(uint8_t *message, uint32_t length, int dataSource) override; - -private: - + 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 index 96534ff..ebd1be2 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasHandlerManager.cpp +++ b/cloudphone/src/main/cpp/cas_controller/CasHandlerManager.cpp @@ -18,28 +18,33 @@ #include "CasHandlerManager.h" #include "CasCmdHandler.h" #include "CasCommonHandler.h" -#include "CasHearbeatHandler.h" +#include "CasHeartbeatHandler.h" #include "../cas_common/CasLog.h" #include "CasBuffer.h" #include "CasAudioHandler.h" -CasHandlerManager::CasHandlerManager(CasController *casController) +CasHandlerManager::CasHandlerManager(std::shared_ptr result) { INFO("Constructor."); - m_casController = casController; + m_hanlderResult = result; } CasHandlerManager::~CasHandlerManager() { INFO("Destructor."); - m_casController = nullptr; + 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) { - CasMessageHandler *handler = it.second; + auto &handler = it.second; if (handler->init() != 0) { return -1; } @@ -47,11 +52,14 @@ int CasHandlerManager::init() return 0; } -int CasHandlerManager::start() +int CasHandlerManager::start(bool isStartVideo) { INFO("Start."); for (auto &it : m_handlerMap) { - CasMessageHandler *handler = it.second; + auto &handler = it.second; + if (!isStartVideo && it.first == Video) { + continue; + } if (handler->start() != 0) { return -1; } @@ -59,46 +67,32 @@ int CasHandlerManager::start() return 0; } -int CasHandlerManager::stop() +int CasHandlerManager::stop(bool isStopVideo) { INFO("Stop."); for (auto &it : m_handlerMap) { - CasMessageHandler *handler = it.second; + if (!isStopVideo && it.first == Video) { + continue; + } + auto &handler = it.second; handler->stop(); } return 0; } -void CasHandlerManager::clear() -{ - INFO("Clear."); - for (auto &it : m_handlerMap) { - CasMessageHandler *handler = it.second; - } - m_handlerMap.clear(); -} - int CasHandlerManager::createHandler(CasMsgType type) { - CasMessageHandler *handler; - CasVideoHandler *videoHandler; - CasHearbeatHandler *hearbeatHandler; + std::shared_ptr handler = nullptr; switch (type) { case CasMsgType::Video: - videoHandler = new (std::nothrow) CasVideoHandler(this); - videoHandler->setParameters(m_casController->m_nativeWindow, - m_casController->m_frameType, - m_casController->m_rotationDegrees); - handler = static_cast(videoHandler); + handler = std::make_shared(m_hanlderResult); break; case CasMsgType::HeartBeat: - hearbeatHandler = new (std::nothrow) CasHearbeatHandler(this); - hearbeatHandler->setParameters(m_casController->m_streamBuildSender); - handler = static_cast(hearbeatHandler); + handler = std::make_shared(m_hanlderResult); break; case CasMsgType::CmdControl: - handler = new (std::nothrow) CasCmdHandler(this); + handler = std::make_shared(m_hanlderResult); break; case CasMsgType::Channel: case CasMsgType::Orientation: @@ -108,10 +102,10 @@ int CasHandlerManager::createHandler(CasMsgType type) case CasMsgType::VirtualSensor: case CasMsgType::VirtualLocation: case CasMsgType::ScreenShot: - handler = new (std::nothrow) CasCommonHandler(this); + handler = std::make_shared(m_hanlderResult); break; case CasMsgType::Audio: - handler = new (std::nothrow) CasAudioHandler(this); + handler = std::make_shared(m_hanlderResult); break; default: return -1; @@ -122,20 +116,42 @@ int CasHandlerManager::createHandler(CasMsgType type) return 0; } -void CasHandlerManager::addHandler(CasMsgType type, CasMessageHandler *handler) +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; } - CasVideoHandler *handler = (CasVideoHandler*)m_handlerMap[CasMsgType::Video]; - handler->setParameters(m_casController->m_nativeWindow, - m_casController->m_frameType, - m_casController->m_rotationDegrees); + std::shared_ptr handler = + std::static_pointer_cast(m_handlerMap[CasMsgType::Video]); + handler->setParameters(m_nativeWindow, m_frameType, m_rotation); handler->start(); return 0; } @@ -145,16 +161,27 @@ int CasHandlerManager::stopVideoHandler() if (m_handlerMap.count(CasMsgType::Video) == 0) { return -1; } - CasVideoHandler *handler = (CasVideoHandler*)m_handlerMap[CasMsgType::Video]; + 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)) { - CasMessageHandler *handler = m_handlerMap[type]; - handler->handleMeassage(buffer, length, source); + auto &handler = m_handlerMap[type]; + handler->handleMessage(buffer, length, source); } else { FreeBuffer(buffer); } @@ -162,35 +189,7 @@ void CasHandlerManager::onNewPacket(int type, uint8_t *buffer, uint32_t length, void CasHandlerManager::onNewCmd(int cmd, std::string detail) { - if (m_casController != nullptr) { - m_casController->NotifyCommand(cmd, detail); - } -} - -void CasHandlerManager::onCmdRecv(int code, std::string &message) -{ - if (m_casController != nullptr) { - m_casController->OnCmdRecv(code, message); + if (m_hanlderResult != nullptr) { + m_hanlderResult->onCmdRecv(cmd, detail); } } - -void CasHandlerManager::onMessageRecv(stream_msg_head_t *header, uint8_t *body) -{ - if (m_casController != nullptr) { - m_casController->onMessageRecv(header, body, header->GetPayloadSize()); - } -} - -void CasHandlerManager::onConnectionStatus(bool status) -{ - if (m_casController != nullptr) { - m_casController->SetConnectStatus(status); - } -} - -void CasHandlerManager::onVideoPacketRecv() -{ - if (m_casController != nullptr) { - m_casController->CalculateFPS(); - } -} \ No newline at end of file diff --git a/cloudphone/src/main/cpp/cas_controller/CasHandlerManager.h b/cloudphone/src/main/cpp/cas_controller/CasHandlerManager.h index cd0e23c..e0cdea9 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasHandlerManager.h +++ b/cloudphone/src/main/cpp/cas_controller/CasHandlerManager.h @@ -18,37 +18,40 @@ #include #include #include -#include "CasClient.h" +#include "CasTransmission.h" #include "CasMessageHandler.h" #include "../CasController.h" -class CasHandlerManager : public CasClientListener, public CasMessageResult { +class CasHandlerManager : public CasTransmissionListener { public: - CasHandlerManager(CasController *casController); + CasHandlerManager(std::shared_ptr result); ~CasHandlerManager(); int init(); - int start(); - int stop(); - void clear(); + int start(bool isStartVideo = true); + int stop(bool isStopVideo = true); int createHandler(CasMsgType type); int startVideoHandler(); int stopVideoHandler(); - void addHandler(CasMsgType type, CasMessageHandler *handler); + + 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 onCmdRecv(int code, std::string &msg) override; - void onMessageRecv(stream_msg_head_t *header, uint8_t *body) override; - void onConnectionStatus(bool status) override; - void onVideoPacketRecv() override; + void addHandler(CasMsgType type, std::shared_ptr &handler); private: - CasController *m_casController; - std::map m_handlerMap; + 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 0000000..fb02b00 --- /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 0000000..450c0c8 --- /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/CasHearbeatHandler.cpp b/cloudphone/src/main/cpp/cas_controller/CasHeartbeatHandler.cpp similarity index 72% rename from cloudphone/src/main/cpp/cas_controller/CasHearbeatHandler.cpp rename to cloudphone/src/main/cpp/cas_controller/CasHeartbeatHandler.cpp index ff3225f..4133659 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasHearbeatHandler.cpp +++ b/cloudphone/src/main/cpp/cas_controller/CasHeartbeatHandler.cpp @@ -13,75 +13,82 @@ // limitations under the License. #include -#include "CasHearbeatHandler.h" +#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; } -CasHearbeatHandler::CasHearbeatHandler(CasMessageResult *result) +CasHeartbeatHandler::CasHeartbeatHandler(std::shared_ptr &result) :CasMessageHandler(result) { + INFO("Costruct"); m_conditionWait = false; m_heartbeatTaskRun = false; } -CasHearbeatHandler::~CasHearbeatHandler() +CasHeartbeatHandler::~CasHeartbeatHandler() { + INFO("Destruct"); + m_heartbeatPktStream = nullptr; + m_streamBuildSender = nullptr; } -int CasHearbeatHandler::init() +int CasHeartbeatHandler::init() { - INFO("init"); + INFO("Init"); return 0; } -int CasHearbeatHandler::start() +int CasHeartbeatHandler::start() { std::lock_guard lock(m_lock); - m_heartbeatPktStream = new (std::nothrow) CasDataPipe(true); + m_heartbeatPktStream = std::make_shared(true); if (m_heartbeatPktStream == nullptr) { - ERR("create heartbeat pipe failed."); + ERR("Could't create m_heartbeatPktStream."); return -1; } m_conditionWait = false; m_heartbeatTaskRun = true; - m_heartbeatThread = std::thread(&CasHearbeatHandler::heartbeatThread, this); + m_heartbeatThread = std::thread(&CasHeartbeatHandler::heartbeatThread, this); return 0; } -int CasHearbeatHandler::stop() +int CasHeartbeatHandler::stop() { + INFO("Stop"); std::lock_guard lock(m_lock); if (m_heartbeatTaskRun) { m_heartbeatTaskRun = false; m_heartbeatPktStream->Exit(); interruptSleep(); m_heartbeatThread.join(); - delete m_heartbeatPktStream; m_heartbeatPktStream = nullptr; } return 0; } -void CasHearbeatHandler::handleMeassage(uint8_t *message, uint32_t length, int dataSource) +void CasHeartbeatHandler::handleMessage(uint8_t *message, uint32_t length, int dataSource) { std::lock_guard lock(m_lock); - if (m_heartbeatPktStream != nullptr) { - m_heartbeatPktStream->Handle(message); + if (m_heartbeatPktStream == nullptr) { + FreeBuffer(message); + return; } + m_heartbeatPktStream->Handle(message); } -void CasHearbeatHandler::setParameters(CasStreamBuildSender *streamBuildSender) +void CasHeartbeatHandler::setParameters(std::shared_ptr &streamBuildSender) { std::lock_guard lock(m_lock); m_streamBuildSender = streamBuildSender; } -bool CasHearbeatHandler::sendHeartbeat() +bool CasHeartbeatHandler::sendHeartbeat() { m_heartbeatPktStream->Clear(); std::map parameters = { { KEY_COMMAND, CMD_HEARTBEAT_REQUEST } }; @@ -98,10 +105,11 @@ bool CasHearbeatHandler::sendHeartbeat() ERR("Wait heartbeat response failed."); return false; } + FreeBuffer(onePkt); return true; } -void CasHearbeatHandler::heartbeatThread() +void CasHeartbeatHandler::heartbeatThread() { struct timeval sendtime; struct timeval recvtime; @@ -118,7 +126,7 @@ void CasHearbeatHandler::heartbeatThread() if (heatbeatFailedCount > maxFailedTimes && isConnect) { isConnect = false; heatbeatFailedCount = 0; - m_messageResult->onConnectionStatus(false); + m_messageResult->onConnectionStateChange(false); ERR("client is disconnect."); } } else { @@ -128,16 +136,16 @@ void CasHearbeatHandler::heartbeatThread() heatbeatFailedCount = 0; if (!isConnect) { isConnect = true; - m_messageResult->onConnectionStatus(true); + m_messageResult->onConnectionStateChange(true); } updateLag(lag); } sleepFor(200); // 200毫秒 } - INFO("HeartbeatThread thread exit."); + INFO("HeartbeatThread exit."); } -void CasHearbeatHandler::updateLag(uint64_t lag) +void CasHeartbeatHandler::updateLag(uint64_t lag) { if (m_lagDeque.size() >= MAX_LAGS_NUM) { m_lagDeque.pop_front(); @@ -151,16 +159,19 @@ void CasHearbeatHandler::updateLag(uint64_t lag) } } m_lag = maxLag; + if (m_messageResult != nullptr) { + m_messageResult->updateLag(m_lag); + } } -void CasHearbeatHandler::sleepFor(const int timeout) +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 CasHearbeatHandler::interruptSleep() +void CasHeartbeatHandler::interruptSleep() { { std::lock_guard lock(m_mutex); diff --git a/cloudphone/src/main/cpp/cas_controller/CasHearbeatHandler.h b/cloudphone/src/main/cpp/cas_controller/CasHeartbeatHandler.h similarity index 69% rename from cloudphone/src/main/cpp/cas_controller/CasHearbeatHandler.h rename to cloudphone/src/main/cpp/cas_controller/CasHeartbeatHandler.h index 8247296..e33d96b 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasHearbeatHandler.h +++ b/cloudphone/src/main/cpp/cas_controller/CasHeartbeatHandler.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef CLOUDAPPSDK_CASHEARBEATHANDLER_H -#define CLOUDAPPSDK_CASHEARBEATHANDLER_H +#ifndef CLOUDAPPSDK_CASHEARTBEATHANDLER_H +#define CLOUDAPPSDK_CASHEARTBEATHANDLER_H #include #include @@ -21,20 +21,20 @@ #include #include #include "CasMessageHandler.h" -#include "CasClient.h" +#include "CasTransmission.h" #include "CasStreamBuildSender.h" #include "../cas_service/CasDataPipe.h" -class CasHearbeatHandler : public CasMessageHandler{ +class CasHeartbeatHandler : public CasMessageHandler{ public: - CasHearbeatHandler(CasMessageResult *result); - ~CasHearbeatHandler(); + CasHeartbeatHandler(std::shared_ptr &result); + ~CasHeartbeatHandler(); int init() override; int start() override; int stop() override; - void handleMeassage(uint8_t *message, uint32_t length, int dataSource) override; - void setParameters(CasStreamBuildSender *streamBuildSender); + void handleMessage(uint8_t *message, uint32_t length, int dataSource) override; + void setParameters(std::shared_ptr &streamBuildSender); uint64_t getLag() { return m_lag; } @@ -51,12 +51,12 @@ private: uint64_t m_lag = 0; bool m_heartbeatTaskRun; std::thread m_heartbeatThread; - CasStreamBuildSender *m_streamBuildSender; + std::shared_ptr m_streamBuildSender; std::deque m_lagDeque; std::mutex m_mutex; std::condition_variable m_cv; bool m_conditionWait; - CasDataPipe *m_heartbeatPktStream; + std::shared_ptr m_heartbeatPktStream; }; -#endif //CLOUDAPPSDK_CASHEARBEATHANDLER_H +#endif // CLOUDAPPSDK_CASHEARTBEATHANDLER_H diff --git a/cloudphone/src/main/cpp/cas_controller/CasMessageHandler.h b/cloudphone/src/main/cpp/cas_controller/CasMessageHandler.h index e833b1d..efe99d8 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasMessageHandler.h +++ b/cloudphone/src/main/cpp/cas_controller/CasMessageHandler.h @@ -19,29 +19,32 @@ #include #include "CasMsg.h" -class CasMessageResult { +class CasHandlerResult { public: - CasMessageResult() = default; - virtual ~CasMessageResult() {} + 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 onConnectionStatus(bool status) = 0; + virtual void onConnectionStateChange(bool state) = 0; virtual void onVideoPacketRecv() = 0; + virtual void updateLag(uint64_t lag) = 0; }; class CasMessageHandler { public: - CasMessageHandler(CasMessageResult *result) { + CasMessageHandler(std::shared_ptr &result) { m_messageResult = result; } - virtual ~CasMessageHandler() {} + virtual ~CasMessageHandler() { + m_messageResult = nullptr; + } virtual int init() = 0; virtual int start() = 0; virtual int stop() = 0; - virtual void handleMeassage(uint8_t *message, uint32_t length, int source) = 0; + virtual void handleMessage(uint8_t *message, uint32_t length, int source) = 0; protected: - CasMessageResult *m_messageResult; + std::shared_ptr m_messageResult; }; #endif // CLOUDAPPSDK_CASMESSAGEHANDLER_H diff --git a/cloudphone/src/main/cpp/cas_controller/CasMright.cpp b/cloudphone/src/main/cpp/cas_controller/CasMtansTransmission.cpp similarity index 73% rename from cloudphone/src/main/cpp/cas_controller/CasMright.cpp rename to cloudphone/src/main/cpp/cas_controller/CasMtansTransmission.cpp index d550d9a..75cb3e9 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasMright.cpp +++ b/cloudphone/src/main/cpp/cas_controller/CasMtansTransmission.cpp @@ -13,44 +13,44 @@ // limitations under the License. #include -#include "CasMright.h" +#include "CasMtansTransmission.h" #include "CasLog.h" #include "opus.h" #include "CasMsg.h" -#include "CasClient.h" +#include "CasTransmission.h" #include "net_trans.h" #include "spdlog/sinks/rotating_file_sink.h" #include "../CasCommon.h" -int32_t OnRecvVideoStreamData(uint8_t*, uint32_t); -int32_t OnRecvAudioStreamData(uint8_t*, uint32_t); -int32_t OnRecvAudioDecodeCallback(int32_t, AudioJbDecode*); -int32_t OnRecvCmdData(uint8_t*, uint32_t); -void OnGotTransLog(const char*, uint32_t length); +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 CasClientListener *g_listener = nullptr; -static std::mutex g_mutex; +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_isReady = false; +static bool g_MtransIsvalid = false; -CasMright::CasMright() +CasMtansTransmission::CasMtansTransmission() { - INFO("CasMright construct."); + INFO("CasMtansTransmission construct."); m_mtrans = nullptr; - g_listener = nullptr; - g_isReady = false; + g_MtransIsvalid = false; + g_listener.reset(); } -CasMright::~CasMright() +CasMtansTransmission::~CasMtansTransmission() { - INFO("CasMright destruct."); + INFO("CasMtansTransmission destruct."); m_mtrans = nullptr; + g_listener.reset(); } -int CasMright::init(uint32_t ip, uint16_t port, std::string &logPath) +int CasMtansTransmission::init(uint32_t ip, uint16_t port, std::string &logPath) { m_mtrans = new (std::nothrow) NetTrans(); if (m_mtrans == nullptr) { @@ -75,9 +75,9 @@ int CasMright::init(uint32_t ip, uint16_t port, std::string &logPath) TransConfigParam param; param.minVideoSendBitrate = 500; param.maxVideoSendBitrate = 10000; - INFO("CasMright init ip = %s port = %d.", m_ip.c_str(), port); + INFO("CasMtansTransmission init ip = %s port = %d.", m_ip.c_str(), port); - g_isReady = false; + g_MtransIsvalid = false; return m_mtrans->Init(PEER_CLIENT, m_ip.c_str(), port, param, OnRecvVideoStreamData, OnRecvAudioStreamData, @@ -86,11 +86,12 @@ int CasMright::init(uint32_t ip, uint16_t port, std::string &logPath) OnRecvCmdData, OnRecvAudioDecodeCallback, OnGotTransLog); + m_isStart = false; } -int CasMright::deinit() +int CasMtansTransmission::deinit() { - INFO("CasMright deinit."); + INFO("CasMtansTransmission deinit."); if (m_mtrans != nullptr) { delete m_mtrans; m_mtrans = nullptr; @@ -98,20 +99,20 @@ int CasMright::deinit() return 0; } -int CasMright::connect() +int CasMtansTransmission::connect() { INFO("Connect"); return 0; } -int CasMright::reconnect() +int CasMtansTransmission::reconnect() { INFO("Reonnect"); stop(); return start(); } -int CasMright::send(uint8_t *buffer, uint32_t length) +int CasMtansTransmission::send(uint8_t *buffer, uint32_t length) { if (m_mtrans == nullptr) { ERR("m_mtrans is null."); @@ -140,18 +141,24 @@ int CasMright::send(uint8_t *buffer, uint32_t length) 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 CasMright::start() +int CasMtansTransmission::start() { INFO("Start."); + if (m_isStart) { + return 0; + } if (m_mtrans == nullptr) { ERR("m_mtrans is null."); return -1; @@ -166,42 +173,48 @@ int CasMright::start() if (g_opusDecoder == nullptr) { g_opusDecoder = opus_decoder_create(48000, 2, &err); } - g_isReady = false; + g_MtransIsvalid = false; + m_isStart = true; return 0; } -int CasMright::stop() +int CasMtansTransmission::stop() { + if (!m_isStart) { + return 0; + } INFO("Stop."); if (m_mtrans == nullptr) { ERR("Mtrans instance is null."); return false; } - g_isReady = false; + g_MtransIsvalid = false; + m_isStart = false; return m_mtrans->Stop();; } -void CasMright::setListener(CasClientListener *listener) +void CasMtansTransmission::setListener(std::weak_ptr &listener) { g_listener = listener; } -bool CasMright::isReady() +bool CasMtansTransmission::isReady() { - return g_isReady; + return g_MtransIsvalid; } int32_t OnRecvVideoStreamData(uint8_t* data, uint32_t length) { - if (g_listener == nullptr) { + 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); - g_listener->onNewPacket(CasMsgType::Video, videoData, length, SOURCE_MRIGHT); + listener->onNewPacket(CasMsgType::Video, videoData, length, SOURCE_MTRANS); } - g_isReady = true; + g_MtransIsvalid = true; return 0; } @@ -217,9 +230,9 @@ int32_t OnRecvAudioStreamData(uint8_t* data, uint32_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); - - if (g_listener != nullptr) { - g_listener->onNewPacket(CasMsgType::Audio, buffer, dataLen, SOURCE_MRIGHT); + std::shared_ptr listener = g_listener.lock(); + if (listener != nullptr) { + listener->onNewPacket(CasMsgType::Audio, buffer, dataLen, SOURCE_MTRANS); } return 0; } @@ -269,21 +282,23 @@ void OnGotTransLog(const char* str, uint32_t length) void OnNeedKeyFrameCallback() { - if (g_listener != nullptr) { - g_listener->onNewCmd(CAS_REQUEST_CAMERA_KEY_FRAME, "camera need keyframe"); + 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) { - if (g_listener != nullptr) { + std::shared_ptr listener = g_listener.lock(); + if (listener != nullptr) { stream_msg_head_t *header = (stream_msg_head_t *)data; - g_listener->onNewPacket(header->type, data, length, SOURCE_MRIGHT); + listener->onNewPacket(header->type, data, length, SOURCE_MTRANS); } return 0; } -int CasMright::getRecvStats(int type, void *value) +int CasMtansTransmission::getRecvStats(int type, void *value) { if (m_mtrans == nullptr) { return -1; diff --git a/cloudphone/src/main/cpp/cas_controller/CasMright.h b/cloudphone/src/main/cpp/cas_controller/CasMtansTransmission.h similarity index 76% rename from cloudphone/src/main/cpp/cas_controller/CasMright.h rename to cloudphone/src/main/cpp/cas_controller/CasMtansTransmission.h index 90793bd..dc9d805 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasMright.h +++ b/cloudphone/src/main/cpp/cas_controller/CasMtansTransmission.h @@ -12,20 +12,20 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef CLOUDAPPSDK_CASMRIGHT_H -#define CLOUDAPPSDK_CASMRIGHT_H +#ifndef CLOUDAPPSDK_CASMTRANS_H +#define CLOUDAPPSDK_CASMTRANS_H #include #include #include "ICasTransmission.h" -class CasClientListener; +class CasTransmissionListener; class NetTrans; -class CasMright : public ICasTransmission { +class CasMtansTransmission : public ICasTransmission { public: - CasMright(); - ~CasMright(); + CasMtansTransmission(); + ~CasMtansTransmission(); int init(uint32_t ip, uint16_t port, std::string &logPath) override; int deinit() override; int connect() override; @@ -33,13 +33,14 @@ public: int stop() override; int reconnect() override; int send(uint8_t *buffer, uint32_t length) override; - void setListener(CasClientListener *listener) 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_CASMRIGHT_H +#endif // CLOUDAPPSDK_CASMTRANS_H diff --git a/cloudphone/src/main/cpp/cas_controller/CasTcp.cpp b/cloudphone/src/main/cpp/cas_controller/CasTcpTransmission.cpp similarity index 72% rename from cloudphone/src/main/cpp/cas_controller/CasTcp.cpp rename to cloudphone/src/main/cpp/cas_controller/CasTcpTransmission.cpp index 8558599..865465d 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasTcp.cpp +++ b/cloudphone/src/main/cpp/cas_controller/CasTcpTransmission.cpp @@ -12,27 +12,30 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "CasTcp.h" +#include "CasTcpTransmission.h" #include "../cas_socket/CasTcpSocket.h" #include "../cas_common/CasLog.h" #include "../cas_common/CasBuffer.h" -CasTcp::CasTcp() +CasTcpTransmission::CasTcpTransmission() { - INFO("CasTcp construct."); + INFO("Construct."); m_ip = 0; m_port = 0; - m_listener = nullptr; + m_isTaskRun = false; m_casClientSocket = nullptr; + m_listener.reset(); m_isReady = false; } -CasTcp::~CasTcp() +CasTcpTransmission::~CasTcpTransmission() { - INFO("CasTcp destruct."); + INFO("Destruct."); + m_casClientSocket = nullptr; + m_listener.reset(); } -int CasTcp::init(uint32_t ip, uint16_t port, std::string &logPath) +int CasTcpTransmission::init(uint32_t ip, uint16_t port, std::string &logPath) { INFO("Init, ip=%d, port=%d", ip, port); m_ip = ip; @@ -40,16 +43,16 @@ int CasTcp::init(uint32_t ip, uint16_t port, std::string &logPath) return 0; } -int CasTcp::deinit() +int CasTcpTransmission::deinit() { INFO("Deinit."); return 0; } -int CasTcp::connect() +int CasTcpTransmission::connect() { INFO("Connect."); - m_casClientSocket = new (std::nothrow) CasTcpClientSocket(m_ip, m_port); + m_casClientSocket = std::make_shared(m_ip, m_port); if (m_casClientSocket == nullptr) { ERR("Create CasTcpClientSocket instance failed."); return -1; @@ -67,6 +70,7 @@ int CasTcp::connect() } if (result < 0) { ERR("Connect to server failed."); + m_casClientSocket = nullptr; return result; } m_isReady = true; @@ -74,28 +78,27 @@ int CasTcp::connect() return 0; } -int CasTcp::start() +int CasTcpTransmission::start() { INFO("Start."); m_isTaskRun = true; - m_task = std::thread(&CasTcp::recvTask, this); + m_task = std::thread(&CasTcpTransmission::recvTask, this); return 0; } -int CasTcp::stop() +int CasTcpTransmission::stop() { INFO("Stop."); if (m_isTaskRun) { m_isTaskRun = false; m_task.join(); - delete m_casClientSocket; - m_casClientSocket = nullptr; + m_casClientSocket.reset(); m_isReady = false; } return 0; } -int CasTcp::reconnect() +int CasTcpTransmission::reconnect() { INFO("Reconnect."); stop(); @@ -106,7 +109,7 @@ int CasTcp::reconnect() return start();; } -int CasTcp::send(uint8_t *buffer, uint32_t length) +int CasTcpTransmission::send(uint8_t *buffer, uint32_t length) { if (m_casClientSocket == nullptr) { ERR("client socket is nullptr."); @@ -119,18 +122,18 @@ int CasTcp::send(uint8_t *buffer, uint32_t length) return m_casClientSocket->Send(buffer, length); } -void CasTcp::setListener(CasClientListener *listener) +void CasTcpTransmission::setListener(std::weak_ptr &listener) { - m_listener = listener; + m_listener = listener; } -bool CasTcp::isReady() +bool CasTcpTransmission::isReady() { return m_isReady; } // private method -int CasTcp::readN(uint8_t *buffer, uint32_t length) +int CasTcpTransmission::readN(uint8_t *buffer, uint32_t length) { uint32_t readLen = 0; while (m_isTaskRun && readLen < length) { @@ -151,14 +154,13 @@ int CasTcp::readN(uint8_t *buffer, uint32_t length) return readLen; } -void CasTcp::recvTask() +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) { - // ERR("Read message header failed ret = %d headerLength = %d.", ret, headerLength); usleep(100000); continue; } @@ -166,7 +168,6 @@ void CasTcp::recvTask() uint8_t *message = (uint8_t*)AllocBuffer(bodyLength + headerLength); ret = readN(message + headerLength, bodyLength); if (ret != bodyLength) { - // ERR("Read message body failed ret = %d bodyLength %d type= %d.", ret, bodyLength, msgHead.type); FreeBuffer(message); continue; } @@ -175,14 +176,15 @@ void CasTcp::recvTask() } } -void CasTcp::dispatchMessage(streamMsgHead &header, uint8_t *message, uint32_t length) +void CasTcpTransmission::dispatchMessage(streamMsgHead &header, uint8_t *message, uint32_t length) { - if (m_listener != nullptr) { - m_listener->onNewPacket(header.type, message, length, SOURCE_TCP); + std::shared_ptr listener = m_listener.lock(); + if (listener != nullptr) { + listener->onNewPacket(header.type, message, length, SOURCE_TCP); } } -int CasTcp::getRecvStats(int , void *) +int CasTcpTransmission::getRecvStats(int , void *) { return 0; } \ No newline at end of file diff --git a/cloudphone/src/main/cpp/cas_controller/CasTcp.h b/cloudphone/src/main/cpp/cas_controller/CasTcpTransmission.h similarity index 83% rename from cloudphone/src/main/cpp/cas_controller/CasTcp.h rename to cloudphone/src/main/cpp/cas_controller/CasTcpTransmission.h index 4a49b00..91bdb10 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasTcp.h +++ b/cloudphone/src/main/cpp/cas_controller/CasTcpTransmission.h @@ -23,10 +23,10 @@ class CasTcpClientSocket; -class CasTcp : public ICasTransmission { +class CasTcpTransmission : public ICasTransmission { public: - CasTcp(); - ~CasTcp(); + CasTcpTransmission(); + ~CasTcpTransmission(); int init(uint32_t ip, uint16_t port, std::string &logPath) override; int deinit() override; int connect() override; @@ -34,7 +34,7 @@ public: int stop() override; int reconnect() override; int send(uint8_t *buffer, uint32_t length) override; - void setListener(CasClientListener *listener) override; + void setListener(std::weak_ptr &listener) override; bool isReady() override; int getRecvStats(int type, void *value) override; @@ -48,8 +48,8 @@ private: uint16_t m_port; bool m_isTaskRun; std::thread m_task; - CasTcpClientSocket *m_casClientSocket; - CasClientListener *m_listener; + std::shared_ptr m_casClientSocket; + std::weak_ptr m_listener; bool m_isReady; }; diff --git a/cloudphone/src/main/cpp/cas_controller/CasClient.cpp b/cloudphone/src/main/cpp/cas_controller/CasTransmission.cpp similarity index 61% rename from cloudphone/src/main/cpp/cas_controller/CasClient.cpp rename to cloudphone/src/main/cpp/cas_controller/CasTransmission.cpp index bf8d59e..4b6fe0c 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasClient.cpp +++ b/cloudphone/src/main/cpp/cas_controller/CasTransmission.cpp @@ -12,74 +12,67 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "CasClient.h" +#include "CasTransmission.h" #include "../cas_socket/CasTcpSocket.h" #include "../cas_common/CasLog.h" #include "../cas_common/CasBuffer.h" -#include "CasTcp.h" -#include "CasMright.h" +#include "CasTcpTransmission.h" +#include "CasMtansTransmission.h" #include "ICasTransmission.h" -CasClient::CasClient(bool isStream) +CasTransmission::CasTransmission(bool isStream) { - INFO("CasClient construct."); + INFO("CasTransmission construct."); m_stream = isStream; m_casTcp = nullptr; #if MTRANS_ENABLED - m_casMright = nullptr; + m_casMtrans = nullptr; #endif } -CasClient::~CasClient() +CasTransmission::~CasTransmission() { - INFO("CasClient destruct."); + INFO("CasTransmission destruct."); } -int CasClient::init(uint32_t ip, uint16_t port, std::string &logPath) +int CasTransmission::init(uint32_t ip, uint16_t port, std::string &logPath) { - INFO("Init, ip=%d, port=%d", ip, port); + INFO("Init"); std::lock_guard lock(m_mutex); - m_casTcp = new (std::nothrow) CasTcp(); + 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 CasTcp."); + ERR("Could't create CasTcpTransmission."); return -1; } m_casTcp->init(ip, port, logPath); #if MTRANS_ENABLED if (m_stream) { - m_casMright = new (std::nothrow) CasMright(); - if (m_casMright == nullptr) { - delete m_casTcp; + m_casMtrans = std::make_shared(); + if (m_casMtrans == nullptr) { m_casTcp = nullptr; return -1; } - m_casMright->init(ip, port, logPath); + m_casMtrans->init(ip, port, logPath); } #endif return 0; } -int CasClient::deinit() +int CasTransmission::deinit() { INFO("Deinit."); std::lock_guard lock(m_mutex); - if (m_casTcp != nullptr) { - m_casTcp->deinit(); - delete m_casTcp; - m_casTcp = nullptr; - } - + m_casTcp = nullptr; #if MTRANS_ENABLED - if (m_casMright != nullptr) { - m_casMright->deinit(); - delete m_casMright; - m_casMright = nullptr; - } + m_casMtrans = nullptr; #endif return 0; } -int CasClient::connect() +int CasTransmission::connect() { INFO("Connect."); std::lock_guard lock(m_mutex); @@ -87,10 +80,20 @@ int CasClient::connect() ERR("m_casTcp is null."); return -1; } - return m_casTcp->connect(); + 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 CasClient::start() +int CasTransmission::start() { INFO("Start."); std::lock_guard lock(m_mutex); @@ -103,8 +106,8 @@ int CasClient::start() } #if MTRANS_ENABLED - if (m_casMright != nullptr) { - if (m_casMright->start() != 0) { + if (m_casMtrans != nullptr) { + if (m_casMtrans->start() != 0) { return -1; } } @@ -112,7 +115,7 @@ int CasClient::start() return 0; } -int CasClient::stop() +int CasTransmission::stop() { INFO("Stop."); std::lock_guard lock(m_mutex); @@ -122,14 +125,14 @@ int CasClient::stop() } int ret = m_casTcp->stop(); #if MTRANS_ENABLED - if (m_casMright != nullptr) { - m_casMright->stop(); + if (m_casMtrans != nullptr) { + m_casMtrans->stop(); } #endif return ret; } -int CasClient::reconnect() +int CasTransmission::reconnect() { INFO("Reconnect."); std::lock_guard lock(m_mutex); @@ -141,14 +144,14 @@ int CasClient::reconnect() return -1; } #if MTRANS_ENABLED - if (m_casMright != nullptr) { - m_casMright->reconnect(); + if (m_casMtrans != nullptr) { + m_casMtrans->reconnect(); } #endif return 0; } -int CasClient::send(uint8_t *buffer, uint32_t length) +int CasTransmission::send(uint8_t *buffer, uint32_t length) { std::lock_guard lock(m_mutex); if (m_casTcp == nullptr) { @@ -157,23 +160,23 @@ int CasClient::send(uint8_t *buffer, uint32_t length) } #if MTRANS_ENABLED stream_msg_head_t *msgHead = (stream_msg_head_t *)buffer; - if (m_casMright != nullptr && m_casMright->isReady()) { + 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_casMright->send(buffer, length); + return m_casMtrans->send(buffer, length); } } - if (m_casMright != nullptr && msgHead->type == CasMsgType::HeartBeat) { - m_casMright->send(buffer, length); + if (m_casMtrans != nullptr && msgHead->type == CasMsgType::HeartBeat) { + m_casMtrans->send(buffer, length); } #endif return m_casTcp->send(buffer, length); } -void CasClient::setListener(CasClientListener *listener) +void CasTransmission::setListener(std::weak_ptr listener) { INFO("SetListener."); std::lock_guard lock(m_mutex); @@ -181,24 +184,24 @@ void CasClient::setListener(CasClientListener *listener) m_casTcp->setListener(listener); } #if MTRANS_ENABLED - if (m_casMright != nullptr) { - m_casMright->setListener(listener); + if (m_casMtrans != nullptr) { + m_casMtrans->setListener(listener); } #endif } -int CasClient::getRecvStats(int type, void *stats) +int CasTransmission::getRecvStats(int type, void *stats) { std::lock_guard lock(m_mutex); #if MTRANS_ENABLED - if (m_casMright != nullptr) { - return m_casMright->getRecvStats(type, stats); + if (m_casMtrans != nullptr) { + return m_casMtrans->getRecvStats(type, stats); } #endif return -1; } -bool CasClient::isReady() +bool CasTransmission::isReady() { std::lock_guard lock(m_mutex); if (m_casTcp != nullptr) { diff --git a/cloudphone/src/main/cpp/cas_controller/CasClient.h b/cloudphone/src/main/cpp/cas_controller/CasTransmission.h similarity index 76% rename from cloudphone/src/main/cpp/cas_controller/CasClient.h rename to cloudphone/src/main/cpp/cas_controller/CasTransmission.h index 771eb06..78b67cf 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasClient.h +++ b/cloudphone/src/main/cpp/cas_controller/CasTransmission.h @@ -15,19 +15,20 @@ #ifndef CLOUDAPPSDK_CASCLIENT_H #define CLOUDAPPSDK_CASCLIENT_H -#include +#include #include #include +#include #include "ICasTransmission.h" -class CasMright; -class CasTcp; +class CasMtansTransmission; +class CasTcpTransmission; struct StreamRecvStats; -class CasClient { +class CasTransmission { public: - CasClient(bool isStream = true); - ~CasClient(); + CasTransmission(bool isStream = true); + ~CasTransmission(); int init(uint32_t ip, uint16_t port, std::string &path); int deinit(); @@ -36,17 +37,17 @@ public: int stop(); int reconnect(); int send(uint8_t *buffer, uint32_t length); - void setListener(CasClientListener *listener); + void setListener(std::weak_ptr listener); int getRecvStats(int type, void *stats); bool isReady(); private: - std::mutex m_mutex; - CasTcp *m_casTcp; bool m_stream; + std::mutex m_mutex; + std::shared_ptr m_casTcp; #if MTRANS_ENABLED - CasMright *m_casMright; + std::shared_ptr m_casMtrans; #endif }; diff --git a/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.cpp b/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.cpp index 116a16b..a51a980 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.cpp +++ b/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.cpp @@ -20,7 +20,7 @@ #include "CasMsgCode.h" #include "../CasCommon.h" -CasVideoHandler::CasVideoHandler(CasMessageResult *result) +CasVideoHandler::CasVideoHandler(std::shared_ptr &result) :CasMessageHandler(result) { INFO("CasVideoHandler constructor."); @@ -66,7 +66,6 @@ int CasVideoHandler::start() m_videoThread = nullptr; return -1; } - m_videoThread->GetVideoEngine()->SetFirstVidFrameListener(this); return 0; } @@ -78,15 +77,15 @@ int CasVideoHandler::stop() WARN("Already stop."); return -1; } - if (m_videoThread->GetThreadStatus() == CAS_THREAD_RUNNING) { - m_videoThread->Exit(); - } + m_videoThread->Stop(); + m_videoThread->Exit(); delete m_videoThread; m_videoThread = nullptr; + m_videoPktStream->Clear(); return 0; } -void CasVideoHandler::handleMeassage(uint8_t *message, uint32_t length, int) +void CasVideoHandler::handleMessage(uint8_t *message, uint32_t length, int source) { std::lock_guard lockGuard(m_lock); if (m_videoPktStream == nullptr) { diff --git a/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.h b/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.h index 82a6c7d..3a423b9 100644 --- a/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.h +++ b/cloudphone/src/main/cpp/cas_controller/CasVideoHandler.h @@ -27,13 +27,13 @@ class CasVideoHandler :public CasMessageHandler, public CasFirstVideoFrameListener{ public: - CasVideoHandler(CasMessageResult *result); + CasVideoHandler(std::shared_ptr &result); ~CasVideoHandler(); int init() override; int start() override; int stop() override; - void handleMeassage(uint8_t *message, uint32_t length, int source) override; + void handleMessage(uint8_t *message, uint32_t length, int source) override; void setParameters(ANativeWindow *nativeWindow, FrameType frameType, int rotation); public: diff --git a/cloudphone/src/main/cpp/cas_controller/ICasTransmission.h b/cloudphone/src/main/cpp/cas_controller/ICasTransmission.h index b4aa7e8..2a1978e 100644 --- a/cloudphone/src/main/cpp/cas_controller/ICasTransmission.h +++ b/cloudphone/src/main/cpp/cas_controller/ICasTransmission.h @@ -18,14 +18,14 @@ #include "stdint.h" enum DataSource { - SOURCE_MRIGHT = 0, + SOURCE_MTRANS = 0, SOURCE_TCP }; -class CasClientListener { +class CasTransmissionListener { public: - CasClientListener() {}; - virtual ~CasClientListener() {}; + 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; }; @@ -42,7 +42,7 @@ public: virtual int stop() = 0; virtual int reconnect() = 0; virtual int send(uint8_t *buffer, uint32_t length) = 0; - virtual void setListener(CasClientListener *listener) = 0; + virtual void setListener(std::weak_ptr &listener) = 0; virtual bool isReady() = 0; virtual int getRecvStats(int type, void *value) = 0; }; diff --git a/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.cpp b/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.cpp index 2484a35..69ddc87 100644 --- a/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.cpp +++ b/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.cpp @@ -71,9 +71,9 @@ void CasDecodeController::ClearDiscardFrameCache() m_HoldFrames = 0; } -void FrameCallback(long frameTime, void* opaque) +void FrameCallback(long frameTime, void* parameters) { - CasDecodeController *controller = (CasDecodeController *)opaque; + CasDecodeController *controller = (CasDecodeController *)parameters; if (!controller->IsStatus(EngineStat::ENGINE_RUNNING)) { return; } @@ -143,8 +143,8 @@ void FrameCallback(long frameTime, void* opaque) uint64_t display2End = casVideoUtil->GetNow(); if (iRet == DECODER_SUCCESS) { - if (!controller->m_firstFrame) { - controller->m_firstFrame = true; + if (!controller->m_isFirstFrame) { + controller->m_isFirstFrame = true; if (firstVideoFrameListener != nullptr) { firstVideoFrameListener->OnFirstFrame(); } @@ -215,6 +215,7 @@ CasVideoDecodeStatListener* CasDecodeController::GetCasVideoDecodeStatListener() */ void OutputTaskEntry(CasDecodeController *controller) { + INFO("begin"); if (controller == nullptr) { return; } @@ -234,7 +235,8 @@ void OutputTaskEntry(CasDecodeController *controller) 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; @@ -275,6 +277,7 @@ void OutputTaskEntry(CasDecodeController *controller) } controller->SetSubThreadStatus(false); #endif + INFO("end"); } /* @@ -336,10 +339,12 @@ uint32_t CasDecodeController::Start() return VIDEO_ENGINE_CLIENT_START_ERR; } } - m_firstFrame = false; + 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 954a59b..83c8ce7 100644 --- a/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.h +++ b/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.h @@ -184,7 +184,7 @@ public: uint32_t m_UpdatePatternOffset = 0; uint32_t m_HoldFrames = 0; - bool m_firstFrame = false; + bool m_isFirstFrame = false; bool isStartDiscard = false; // 是否触发丢帧策略,进行日志打印 AChoreographer *m_choreographer = nullptr; diff --git a/cloudphone/src/main/cpp/cas_service/CasCmdController.cpp b/cloudphone/src/main/cpp/cas_service/CasCmdController.cpp index 66eab1c..5215641 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 ac0d4b8..28cf962 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 deb34c1..b42decc 100644 --- a/cloudphone/src/main/cpp/cas_service/CasDataPipe.cpp +++ b/cloudphone/src/main/cpp/cas_service/CasDataPipe.cpp @@ -48,10 +48,8 @@ void CasDataPipe::Clear() noexcept void CasDataPipe::Exit() { - { - lock_guard lck(this->m_lock); - this->m_status = false; - } + lock_guard lck(this->m_lock); + this->m_status = false; 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 7b670ae..469902f 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(CasClient *client) +CasTouch::CasTouch(std::shared_ptr casTransmission) { - Init(client); + Init(casTransmission); } CasTouch::~CasTouch() @@ -26,9 +26,9 @@ CasTouch::~CasTouch() delete m_streamBuildSender; } -void CasTouch::Init(CasClient *client) +void CasTouch::Init(std::shared_ptr &casTransmission) { - m_streamBuildSender = new CasStreamBuildSender(client); + 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 b89a3d0..12c8fe3 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(CasClient *client); + explicit CasTouch(std::shared_ptr casTransmission); ~CasTouch(); - void Init(CasClient *client); + 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 11f193c..239088e 100644 --- a/cloudphone/src/main/cpp/cas_service/CasVideoHDecodeThread.cpp +++ b/cloudphone/src/main/cpp/cas_service/CasVideoHDecodeThread.cpp @@ -173,16 +173,16 @@ int CasVideoHDecodeThread::Start() this->m_videoDecodeStat = nullptr; } - 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; diff --git a/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp b/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp index d82ce95..61b3726 100644 --- a/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp +++ b/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp @@ -19,24 +19,20 @@ #include "CasMsg.h" #include "CasStreamBuildSender.h" -CasStreamBuildSender::CasStreamBuildSender(CasClient *client) +CasStreamBuildSender::CasStreamBuildSender(std::shared_ptr casTransmission) { - this->m_client = client; + this->m_casTransmission = casTransmission; } CasStreamBuildSender::~CasStreamBuildSender() { - this->m_client = 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_client == nullptr) { + if (m_casTransmission == nullptr) { ERR("Failed to send data, client is null."); return -1; } @@ -118,7 +114,7 @@ int CasStreamBuildSender::SendDataToServer(CasMsgType type, const void *buf, siz } for (size_t pos = 0; pos < dataLen;) { - ssize_t stat = m_client->send((uint8_t*)outBuffer + pos, dataLen - pos); + 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 ecf5bd9..f0be7aa 100644 --- a/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.h +++ b/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.h @@ -18,11 +18,11 @@ #include "CasMsg.h" #include "CasSocket.h" #include "../libs/mtrans/include/net_trans.h" -#include "../cas_controller/CasClient.h" +#include "../cas_controller/CasTransmission.h" class CasStreamBuildSender { public: - explicit CasStreamBuildSender(CasClient *client); + explicit CasStreamBuildSender(std::shared_ptr casTransmission); ~CasStreamBuildSender(); @@ -31,8 +31,7 @@ public: int SendDataToServer(CasMsgType type, const void *buf, size_t len); private: - CasClient *m_client = nullptr; - NetTrans *m_mtrans = nullptr; + std::shared_ptr m_casTransmission = nullptr; }; #endif // CLOUDAPPSDK_CASSTREAMBUILDSENDER_H diff --git a/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneScreenshotImpl.java b/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneScreenshotImpl.java index 053c234..8021b23 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneScreenshotImpl.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/apiimpl/CloudPhoneScreenshotImpl.java @@ -107,7 +107,7 @@ public class CloudPhoneScreenshotImpl implements ICloudPhoneScreenshot { id = event.getPointerId(index); rawX = (int) event.getX(index); rawY = (int) event.getY(index); - result = sendTouchEvent(id, action, rawX, rawY, orientation, displayWidth, displayHeight); + result = sendTouchEvent(id, action, rawX, rawY, displayWidth, displayHeight, orientation); break; case MotionEvent.ACTION_MOVE: final int historySize = event.getHistorySize(); @@ -117,14 +117,14 @@ public class CloudPhoneScreenshotImpl implements ICloudPhoneScreenshot { id = event.getPointerId(j); rawX = (int) event.getHistoricalX(j, i); rawY = (int) event.getHistoricalY(j, i); - result = sendTouchEvent(id, action, rawX, rawY, orientation, displayWidth, displayHeight); + 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, orientation, displayWidth, displayHeight); + result = sendTouchEvent(id, action, rawX, rawY, displayWidth, displayHeight, orientation); } break; default: @@ -165,7 +165,7 @@ public class CloudPhoneScreenshotImpl implements ICloudPhoneScreenshot { synchronized (mLock) { if (mProccessor != null) { return mProccessor.sendTouchEvent(id, action, x, y, 0, -1, - newOrientation, displayWidth, displayHeight); + newOrientation, displayHeight, displayWidth); } } return false; 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 2e50bd6..2c0034e 100644 --- a/cloudphone/src/main/java/com/huawei/cloudphone/audio/AudioTrackerCallback.java +++ b/cloudphone/src/main/java/com/huawei/cloudphone/audio/AudioTrackerCallback.java @@ -219,7 +219,6 @@ public class AudioTrackerCallback { int userSize = msg.getSize(); byte[] buffer = msg.readBytes(userSize); -<<<<<<< HEAD if (userSize == USERSIZE_OPUS) { // decode and play short[] data = new short[bufferSizeInBytes]; @@ -235,9 +234,6 @@ public class AudioTrackerCallback { } else { CASLog.e(TAG, "audioTrackPlayer failed to write, invalid userSize."); } -======= - mTrackPlayer.write(buffer, 0, userSize, AudioTrack.WRITE_NON_BLOCKING); ->>>>>>> 32b51a0... AndroidSDK支持截图 } private void handleStop(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 1120b55..0000000 --- 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/JniMessageListener.java b/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JniMessageListener.java deleted file mode 100644 index 1a97996..0000000 --- a/cloudphone/src/main/java/com/huawei/cloudphone/jniwrapper/JniMessageListener.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.huawei.cloudphone.jniwrapper; - -public interface JniMessageListener { - void onGetAudioMessage(byte[] data, int length); - void onGetMessage(byte[] data, int length); - void onGetImeDataMessage(byte[] data, int length); - void onGetDataChannelMessage(byte[] data, int length); - void onGetVirtualMessage(byte[] data, int length); - void onGetCmdListener(byte[] data, int length); -} -- Gitee