diff --git a/app/build.gradle b/app/build.gradle
index 4dcd9d140ff1defa9ebf99157e6f6112c2ac370e..f5c2cf1bbb35716859c5ca12fed86b64b5f9fd3f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -5,6 +5,7 @@ android {
compileSdkVersion versions.compileSdkVersion
buildToolsVersion versions.buildToolsVersion
defaultConfig {
+ applicationId "com.huawei.cloudapp.b004"
minSdkVersion versions.minSdkVersion
targetSdkVersion versions.targetSdkVersion
versionCode versions.versionCode
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 3d7b485b9bd1a13e131853c8cbb7d93dfd38b5b6..4f5d659c86ea9209c2c675bc1a7a757dd86973b9 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -4,6 +4,7 @@
+
@@ -19,6 +20,7 @@
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:supportsRtl="true"
+ android:requestLegacyExternalStorage="true"
android:theme="@style/AppTheme">
{
private static final String TAG = "CasCloudPhoneActivity";
@@ -164,9 +182,20 @@ public class CasCloudPhoneActivity extends FragmentActivity implements IHandleDa
private boolean mIsStopCloudPhoneCalled = false;
private ICloudPhone mCloudPhone;
private TextView mNavigationTextView;
+ private TextView mStatisticsTextView;
+
+ private TextView mMiniStatisticsTextView;
+ private CheckBox mStatisticsCheckBox;
+
+ private TextView mSensorInfoTextView;
+
+ private CheckBox mSensorInfoCheckBox;
+ private Timer mTimer = new Timer();
private boolean isReadyToExit;
private Toast mToast;
+ private int storagePermission = 1;
+
private Handler handler = new Handler() {
@SuppressLint("RestrictedApi")
@Override
@@ -187,6 +216,11 @@ public class CasCloudPhoneActivity extends FragmentActivity implements IHandleDa
processStateChangeMsg(msg.arg1, (String) msg.obj);
break;
case MSG_RECONNECT_FAILED:
+ try {
+ mCloudPhone.exitCloudPhone();
+ } catch (Exception e) {
+ CASLog.e(TAG, "exitCloudPhone failed");
+ }
showDialog(getResources().getString(R.string.cas_phone_reconnect_server_fail_tip_message));
break;
case MSG_CANCLE_EXIT:
@@ -243,6 +277,54 @@ public class CasCloudPhoneActivity extends FragmentActivity implements IHandleDa
mProgressBar = findViewById(R.id.loading_progress_bar);
mFrameLayout = (FrameLayout) findViewById(R.id.frame_layout);
+ mMiniStatisticsTextView = findViewById(R.id.cas_mini_statistic_view);
+ mMiniStatisticsTextView.setTextColor(Color.argb(255, 216, 234, 196));
+
+ mStatisticsTextView = findViewById(R.id.cas_statistic_view);
+ mStatisticsTextView.setTextColor(Color.GREEN);
+
+ mStatisticsCheckBox = findViewById(R.id.cas_stats_checkBox);
+ mStatisticsCheckBox.setTextColor(Color.GREEN);
+ mStatisticsCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ if (isChecked) {
+ mStatisticsTextView.setVisibility(View.VISIBLE);
+ float density = getResources().getDisplayMetrics().density;
+ mStatisticsTextView.setHeight((int) (50 * density));
+ } else {
+ mStatisticsTextView.setVisibility(View.INVISIBLE);
+ }
+ });
+ mTimer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ runOnUiThread(() -> {
+ onUpdateSensorInfo();
+ onUpdateStatisticInfo();
+ });
+ }
+ }, 1000, 1000);
+
+ mSensorInfoTextView = findViewById(R.id.cas_sensor_view);
+ mSensorInfoTextView.setTextColor(Color.GREEN);
+
+ mSensorInfoCheckBox = findViewById(R.id.cas_sensor_checkBox);
+ mSensorInfoCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ if (isChecked) {
+ mSensorInfoTextView.setVisibility(View.VISIBLE);
+ float density = getResources().getDisplayMetrics().density;
+ mSensorInfoTextView.setHeight((int) (50 * density));
+ } else {
+ mSensorInfoTextView.setVisibility(View.INVISIBLE);
+ }
+ });
+
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
+ ActivityCompat.requestPermissions(this, new String[]{
+ Manifest.permission.READ_EXTERNAL_STORAGE,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE
+ }, storagePermission);
+ }
+
mNavigationTextView = findViewById(R.id.cas_navigaiton);
mNavigationTextView.setOnTouchListener(new View.OnTouchListener() {
@SuppressLint({"RestrictedApi", "ClickableViewAccessibility"})
@@ -309,6 +391,36 @@ public class CasCloudPhoneActivity extends FragmentActivity implements IHandleDa
}
}
+ public void onUpdateStatisticInfo() {
+ LocalDateTime dateTime = LocalDateTime.now();
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");
+ String timeStr = dateTime.format(formatter);
+ String simpleStr = timeStr + " ";
+ simpleStr += mCloudPhone.getSimpleStatisticInfo();
+ mMiniStatisticsTextView.setText(simpleStr);
+
+ if (mStatisticsTextView.getVisibility() != View.VISIBLE) {
+ return;
+ }
+
+ String str = timeStr + "\n";
+ str += "网络数据>>\n";
+ str += mCloudPhone.getVideoStatisticInfo();
+ str += "\n";
+ str += getTowerInfo();
+ mStatisticsTextView.setText(str);
+ }
+
+ public void onUpdateSensorInfo() {
+ if (mSensorInfoTextView.getVisibility() != View.VISIBLE) {
+ return;
+ }
+
+ String str = "传感器启停状况>>";
+ str += mCloudPhone.getSensorStatusInfo();
+ mSensorInfoTextView.setText(str);
+ }
+
/**
* Activity:onStart
*/
@@ -400,6 +512,12 @@ public class CasCloudPhoneActivity extends FragmentActivity implements IHandleDa
if (requestCode == DEV_TYPE_CAMERA || requestCode == DEV_TYPE_MICROPHONE || requestCode == DEV_TYPE_LOCATION) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
mCloudPhone.sendPermissionResult(requestCode, PackageManager.PERMISSION_GRANTED);
+ return;
+ }
+ }
+ if (requestCode == storagePermission) {
+ if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ CASLog.i(TAG, "storagePermission");
}
}
}
@@ -444,6 +562,11 @@ public class CasCloudPhoneActivity extends FragmentActivity implements IHandleDa
showDialog(getResources().getString(R.string.cas_phone_resource_inusing_tip_message));
break;
case CasState.CAS_INVALID_CMD:
+ try {
+ mCloudPhone.exitCloudPhone();
+ } catch (Exception e) {
+ CASLog.e(TAG, "exitCloudPhone failed");
+ }
showDialog(getResources().getString(R.string.cas_phone_invalid_cmd_tip_message));
break;
case CasState.CAS_VERIFY_PARAMETER_MISSING:
@@ -478,6 +601,11 @@ public class CasCloudPhoneActivity extends FragmentActivity implements IHandleDa
setStartSuccessFlag();
break;
case CasState.CAS_CONNECT_LOST:
+ try {
+ mCloudPhone.exitCloudPhone();
+ } catch (Exception e) {
+ CASLog.e(TAG, "exitCloudPhone failed");
+ }
showDialog(getResources().getString(R.string.cas_phone_reconnect_server_fail_tip_message));
break;
case CasState.CAS_NOTOUCH_TIMEOUT:
@@ -599,6 +727,67 @@ public class CasCloudPhoneActivity extends FragmentActivity implements IHandleDa
return false;
}
+ private String getTowerInfo() {
+ int rssi = -1;
+ int subscriberId;
+ StringBuilder tower = new StringBuilder("基站数据>>");
+
+ SubscriptionManager subscriptionManager = (SubscriptionManager) this.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+ if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
+ ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.READ_PHONE_STATE }, 101);
+ tower.append("\n未授权");
+ return tower.toString();
+ }
+ TelephonyManager tm = (TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE);
+ List subInfos = subscriptionManager.getActiveSubscriptionInfoList();
+ if (subInfos.isEmpty()) {
+ tower.append("\n无sim卡");
+ return tower.toString();
+ }
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+ tower.append("\n当前手机版本过低,无法获取数据");
+ return tower.toString();
+ }
+ subscriberId = SubscriptionManager.getActiveDataSubscriptionId();
+ for (SubscriptionInfo subInfo:subInfos) {
+ if (subscriberId != subInfo.getSubscriptionId()) {
+ continue;
+ }
+ TelephonyManager tm2 = tm.createForSubscriptionId(subInfo.getSubscriptionId());
+
+ tower.append("\nmcc : ").append(subInfo.getMccString());
+ tower.append("\nmnc : ").append(subInfo.getMncString());
+ String dataType = "";
+
+ List cellSignalStrengths = tm2.getSignalStrength().getCellSignalStrengths();
+ for (CellSignalStrength cellSignalStrength:cellSignalStrengths) {
+ if (cellSignalStrength instanceof CellSignalStrengthCdma) {
+ dataType = "CDMA";
+ rssi = ((CellSignalStrengthCdma) cellSignalStrength).getCdmaDbm();
+ } else if (cellSignalStrength instanceof CellSignalStrengthGsm) {
+ dataType = "GSM";
+ rssi = ((CellSignalStrengthGsm) cellSignalStrength).getDbm();
+ } else if (cellSignalStrength instanceof CellSignalStrengthLte) {
+ dataType = "LTE";
+ rssi = ((CellSignalStrengthLte) cellSignalStrength).getDbm();
+ } else if (cellSignalStrength instanceof CellSignalStrengthNr) {
+ dataType = "NR";
+ rssi = ((CellSignalStrengthNr) cellSignalStrength).getDbm();
+ } else if (cellSignalStrength instanceof CellSignalStrengthTdscdma) {
+ dataType = "TDSCDMA";
+ rssi = ((CellSignalStrengthTdscdma) cellSignalStrength).getDbm();
+ } else if (cellSignalStrength instanceof CellSignalStrengthWcdma) {
+ dataType = "WCDMA";
+ rssi = ((CellSignalStrengthWcdma) cellSignalStrength).getDbm();
+ }
+ }
+ tower.append("\nnetType : ").append(dataType);
+ tower.append("\nrssi : ").append(rssi);
+ tower.append("\noperator : ").append(subInfo.getDisplayName());
+ }
+ return tower.toString();
+ }
+
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK && bIsStart)) {
@@ -620,6 +809,10 @@ public class CasCloudPhoneActivity extends FragmentActivity implements IHandleDa
return;
}
CASLog.d(TAG, "stop cloud phone");
+ if (mTimer != null) {
+ mTimer.cancel();
+ mTimer = null;
+ }
handler.removeCallbacksAndMessages(null);
mIsStopCloudPhoneCalled = true;
mProgressBar.setVisibility(View.VISIBLE);
diff --git a/app/src/main/res/drawable/rounded_corner.xml b/app/src/main/res/drawable/rounded_corner.xml
new file mode 100644
index 0000000000000000000000000000000000000000..57eef4f29c874a82d2e38d6d9cd59c3bb334271b
--- /dev/null
+++ b/app/src/main/res/drawable/rounded_corner.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/cas_activity_fullscreen.xml b/app/src/main/res/layout/cas_activity_fullscreen.xml
index 50d47614d940212ac1ff458378fdaad3cd6ff891..9684316e8ee7d02cccd2e7325ec2d6ca030709e7 100644
--- a/app/src/main/res/layout/cas_activity_fullscreen.xml
+++ b/app/src/main/res/layout/cas_activity_fullscreen.xml
@@ -19,6 +19,48 @@
android:layout_width="match_parent"
android:layout_height="match_parent"/>
+
+
+
+
+
+
+
+
+
+
- 华为云CPH
+ CPH-24.7.0
CasCloudAppMainActivity
GameListActivity
GameListActivity2
diff --git a/cloudphone/src/main/cpp/CMakeLists.txt b/cloudphone/src/main/cpp/CMakeLists.txt
index 25c8b5d0440702b164e4b43167cc791c90233b59..b831d160beb2dc4e510b2cf520a06d39a73fed68 100644
--- a/cloudphone/src/main/cpp/CMakeLists.txt
+++ b/cloudphone/src/main/cpp/CMakeLists.txt
@@ -46,6 +46,7 @@ SET(CPP_ROOT src/main/cpp)
include_directories(libs/libopus/include)
include_directories(libs/openssl/include/)
include_directories(libs/mtrans/include/)
+include_directories(libs)
include_directories(cas_common)
include_directories(cas_stream)
diff --git a/cloudphone/src/main/cpp/CasController.cpp b/cloudphone/src/main/cpp/CasController.cpp
index 55332ee365c41591906470592e2af58e9d1a1b8d..5086f472c3631462ba8d272d7ee0f3a505444ff3 100644
--- a/cloudphone/src/main/cpp/CasController.cpp
+++ b/cloudphone/src/main/cpp/CasController.cpp
@@ -15,12 +15,17 @@
#include
#include
#include
+#include
+#include
+#include "spdlog/sinks/rotating_file_sink.h"
#include "CasJniBridge.h"
#include "CasController.h"
#include "CasConf.h"
#include "CasTcpSocket.h"
#include "CasMsgCode.h"
#include "CasExtVideoDataPipe.h"
+#include "opus.h"
+#include "CasVideoUtil.h"
using namespace std;
@@ -34,11 +39,18 @@ const int TIMES = 8;
const int FRAME_RATE_MIN = 10;
const int FRAME_RATE_MAX = 60;
const std::string CLIENT_TYPE = "1";
+const std::string HRTP_LOG_PATH = "/sdcard/Android/media/com.huawei.cloudapp.b004/";
+const uint64_t DURATION_USEC = 1000000ULL;
int32_t OnRecvVideoStreamData(uint8_t* data, uint32_t length);
int32_t OnRecvAudioStreamData(uint8_t* data, uint32_t length);
+int32_t OnRecvAudioDecodeCallback(int32_t streamId, AudioJbDecode* audioJbDecode);
int32_t OnRecvCmdData(uint8_t* data, uint32_t length);
+void OnGotTransLog(const char* str, uint32_t length);
void OnNeedKeyFrameCallback();
+OpusDecoder *g_opusDecoder = nullptr;
+shared_ptr hrtpLogger = nullptr;
+int g_plcCount = 0;
CasController *CasController::g_instance = nullptr;
CasController *CasController::GetInstance()
@@ -429,18 +441,33 @@ bool CasController::CreateWorkers()
#if MTRANS_ENABLED
if (m_mtrans == nullptr) {
TransConfigParam param;
- param.minVideoSendBitrate = 2000;
- param.maxVideoSendBitrate = 2000;
+ 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,
- nullptr);
+ OnRecvCmdData,
+ OnRecvAudioDecodeCallback,
+ OnGotTransLog);
}
#endif
+ int err = 0;
+ g_opusDecoder = opus_decoder_create(48000, 2, &err);
+
return true;
}
@@ -773,6 +800,48 @@ bool CasController::SendMotionEvent(uint16_t masterAxis, int32_t masterValue, ui
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) {
return false;
}
@@ -785,10 +854,13 @@ 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) {
- if (type == VirtualCamera) {
- return length == m_mtrans->SendVideoData(data, length, "h264");
- } else if (type == VirtualMicrophone) {
- return length == m_mtrans->SendAudioData(data, length);
+ switch (type) {
+ case (VirtualCamera):
+ return length == m_mtrans->SendVideoData(data, length, "h264");
+ case (VirtualMicrophone):
+ return length == m_mtrans->SendAudioData(data, length);
+ default:
+ break;
}
}
#endif
@@ -852,8 +924,14 @@ int CasController::JniRecvData(CasMsgType type, uint8_t *data, int length)
return 0;
}
- if (EOK != memcpy_s(data, dataLen, pPayload, dataLen)) {
- ERR("Failed to copy.");
+ 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;
+ }
+ return 0;
}
if (isGetOrientation) {
@@ -870,8 +948,8 @@ int CasController::JniRecvData(CasMsgType type, uint8_t *data, int length)
void CasController::IsNeedRotation(int orientation)
{
-
INFO("ORIENTATION is %d, old ORIENTATION is %d.", orientation, m_orientation);
+
if (orientation == 8) {
m_rotationDegrees = 270;
} else if (orientation == 24) {
@@ -882,7 +960,6 @@ void CasController::IsNeedRotation(int orientation)
m_rotationDegrees = 0;
}
- std::lock_guard lockGuard(this->m_decoderLock);
ResetDecoder(m_isNotifyFirstFrame);
ForceIFrame();
m_orientation = orientation;
@@ -941,6 +1018,7 @@ bool CasController::ProcessEnterForeground(ANativeWindow *nativeWindow)
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) {
@@ -954,6 +1032,7 @@ bool CasController::CreateDecWorker(ANativeWindow *nativeWindow, bool needVideoD
void CasController::StartDecWorker(bool retainVideoDecode)
{
+ std::lock_guard lockGuard(this->m_decoderLock);
if (retainVideoDecode) {
if (m_videoDecodeThread != nullptr) {
m_videoDecodeThread->Restart();
@@ -965,6 +1044,7 @@ void CasController::StartDecWorker(bool retainVideoDecode)
void CasController::StopDecWorker(bool retainVideoDecode)
{
+ std::lock_guard lockGuard(this->m_decoderLock);
if (m_videoDecodeThread == nullptr) {
return;
}
@@ -1075,6 +1155,7 @@ void CasController::RecvdVideoData(uint8_t *data, int length)
uint8_t *videoData = new uint8_t[length];
memcpy(videoData, data, length);
m_videoPacketStream->Handle(videoData);
+ CalculateFPS();
}
m_isMTransValid = true;
}
@@ -1097,12 +1178,194 @@ int32_t OnRecvVideoStreamData(uint8_t* data, uint32_t length)
int32_t OnRecvAudioStreamData(uint8_t* data, uint32_t length)
{
int headerLen = 36;
- CasController::GetInstance()->RecvdAudioData(data + headerLen, length - headerLen);
+
+ 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;
+ }
+ 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;
+}
+
+void CasController::HandleCmdData(uint8_t *data, int length)
+{
+ streamMsgHead *msgHead = (streamMsgHead *)data;
+ INFO("Cmd callback size = %d, type = %d", length, msgHead->type);
+ if (msgHead->type <= Invalid || msgHead->type >= End) {
+ ERR("msgHead type is invalid : %d.", msgHead->type);
+ return;
+ }
+ 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;
+
+ uint64_t lag = GetLag() / 1000;
+ if (lag >= 600) {
+ stream << "600+ms";
+ } else {
+ 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 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;
+
+ statsString = stream.str();
+ 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;
}
\ No newline at end of file
diff --git a/cloudphone/src/main/cpp/CasController.h b/cloudphone/src/main/cpp/CasController.h
index 400c0e12468392d4fb2b73a09368304ad6564b97..9064f6fec56b4d935045417a1a3f7903fee60e3b 100644
--- a/cloudphone/src/main/cpp/CasController.h
+++ b/cloudphone/src/main/cpp/CasController.h
@@ -65,6 +65,11 @@ public:
cmdCallBack = method;
}
+ CasVideoHDecodeThread* GetVideoDecodeThread()
+ {
+ return m_videoDecodeThread;
+ }
+
int JniSendData(CasMsgType type, uint8_t *data, int length);
int JniRecvData(CasMsgType type, uint8_t *data, int length);
@@ -85,6 +90,12 @@ public:
void RecvdAudioData(uint8_t *data, int length);
+ void HandleCmdData(uint8_t *data, int length);
+
+ std::string GetVideoRecvStats();
+
+ std::string GetSimpleRecvStats();
+
private:
bool Release();
@@ -128,6 +139,9 @@ private:
std::string CalcMaxDisconnectDuration(std::string backgroundTimeout);
+ void CalculateFPS();
+ int GetCurrentFPS() const;
+
void *(*cmdCallBack)(int type, std::string msg) = nullptr;
static CasController *g_instance;
@@ -177,6 +191,10 @@ private:
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};
};
#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 939c953e78119bf7ee5ad4d0566acaa51b9bcfb8..b4b470e2783ca5bfff157a910cfcaf84a649e448 100644
--- a/cloudphone/src/main/cpp/CasJniBridge.cpp
+++ b/cloudphone/src/main/cpp/CasJniBridge.cpp
@@ -214,6 +214,18 @@ extern "C" JNIEXPORT jint JNICALL JNI(getLag)(JNIEnv *env, jclass)
return lag;
}
+extern "C" JNIEXPORT jstring JNICALL JNI(getVideoStreamStats)(JNIEnv *env, jclass)
+{
+ string statsString = gJniApiCtrl->GetVideoRecvStats();
+ return env->NewStringUTF(statsString.c_str());
+}
+
+extern "C" JNIEXPORT jstring JNICALL JNI(getSimpleStreamStats)(JNIEnv *env, jclass)
+{
+ string statsString = gJniApiCtrl->GetSimpleRecvStats();
+ return env->NewStringUTF(statsString.c_str());
+}
+
extern "C" JNIEXPORT void JNICALL JNI(setAssetsData)(JNIEnv *env, jclass, jstring jFilename, jbyteArray jData,
jint length)
{
diff --git a/cloudphone/src/main/cpp/CasJniBridge.h b/cloudphone/src/main/cpp/CasJniBridge.h
index 47a4c8d78b5e081bef95cf070e2e09c6e05125f7..1c385b7a0e5756f4d67a60dbd741e76033a0ad70 100644
--- a/cloudphone/src/main/cpp/CasJniBridge.h
+++ b/cloudphone/src/main/cpp/CasJniBridge.h
@@ -34,6 +34,10 @@ JNIEXPORT int JNICALL JNI(getJniStatus)(JNIEnv *env, jclass);
JNIEXPORT int JNICALL JNI(getLag)(JNIEnv *env, jclass);
+JNIEXPORT jstring JNICALL JNI(getVideoStreamStats)(JNIEnv *env, jclass);
+
+JNIEXPORT jstring JNICALL JNI(getSimpleStreamStats)(JNIEnv *env, jclass);
+
JNIEXPORT jboolean JNICALL JNI(setMediaConfig)(JNIEnv *env, jclass, jobject mediaConfig);
JNIEXPORT jint JNICALL JNI(recvData)(JNIEnv *env, jclass, jbyte type, jbyteArray data, int length);
diff --git a/cloudphone/src/main/cpp/cas_common/CMakeLists.txt b/cloudphone/src/main/cpp/cas_common/CMakeLists.txt
index c664df17d9688d0a906c133e56b02569d738d184..b3657ed4feb023917068230983de36bd422b34a7 100644
--- a/cloudphone/src/main/cpp/cas_common/CMakeLists.txt
+++ b/cloudphone/src/main/cpp/cas_common/CMakeLists.txt
@@ -1,2 +1,3 @@
+include_directories(../libs)
aux_source_directory(. CAS_COMMON_SRC_LIST)
add_library(cas_common STATIC ${CAS_COMMON_SRC_LIST})
\ No newline at end of file
diff --git a/cloudphone/src/main/cpp/cas_common/CasLog.cpp b/cloudphone/src/main/cpp/cas_common/CasLog.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..899a3f9e70fb33167b09f897e8d348e6b9535f4e
--- /dev/null
+++ b/cloudphone/src/main/cpp/cas_common/CasLog.cpp
@@ -0,0 +1,43 @@
+// Copyright 2024 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 "CasLog.h"
+#include "spdlog/sinks/rotating_file_sink.h"
+
+// 最大10M ,最多转储3个文件
+static auto max_size = 1048576 * 10;
+static auto max_files = 3;
+std::shared_ptr sdkLogger = nullptr;
+
+void LogToFile(int level, const char *filename, int line, const char *function, const char* msg)
+{
+ if (sdkLogger == nullptr) {
+ sdkLogger = spdlog::rotating_logger_mt("sdk_logger", "/sdcard/Android/media/com.huawei.cloudapp.b004/sdk_log.txt", max_size, max_files);
+ }
+ switch (level) {
+ case ANDROID_LOG_DEBUG:
+ sdkLogger->debug("[{}:{}]:{}:{}", filename, line, function, msg);
+ break;
+ case ANDROID_LOG_INFO:
+ sdkLogger->info("[{}:{}]:{}:{}", filename, line, function, msg);
+ break;
+ case ANDROID_LOG_WARN:
+ sdkLogger->warn("[{}:{}]:{}:{}", filename, line, function, msg);
+ break;
+ case ANDROID_LOG_ERROR:
+ sdkLogger->error("[{}:{}]:{}:{}", filename, line, function, msg);
+ break;
+ default:
+ break;
+ }
+}
\ No newline at end of file
diff --git a/cloudphone/src/main/cpp/cas_common/CasLog.h b/cloudphone/src/main/cpp/cas_common/CasLog.h
index 6f955306079419bcd292def33fe81b7c7436ce8f..be74980e39932c3c25992993243f9bd1ba21a43c 100644
--- a/cloudphone/src/main/cpp/cas_common/CasLog.h
+++ b/cloudphone/src/main/cpp/cas_common/CasLog.h
@@ -34,6 +34,8 @@ inline char *CasStrrchr(const char *str, int c);
const int LOG_BUF_SIZE = 512;
+void LogToFile(int level, const char *filename, int line, const char *function, const char* msg);
+
inline void LogPrint(int level, const char *filename, int line, const char *function, const char *fmt, ...)
{
if (level < ANDROID_LOG_INFO) {
@@ -45,6 +47,7 @@ inline void LogPrint(int level, const char *filename, int line, const char *func
int ret = vsnprintf_s(msg, LOG_BUF_SIZE, LOG_BUF_SIZE - 1, fmt, args);
if (ret != -1) {
__android_log_print(level, "CloudAppJni", "[%s:%d]%s():%s", filename, line, function, msg);
+ LogToFile(level, filename, line, function, msg);
}
va_end(args);
}
diff --git a/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.cpp b/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.cpp
index 27ab0cfd3d4d0130c11739f25f1a751d7f84cc29..9167fa7795dd7a6b698c24c270e1addfdd65e98c 100644
--- a/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.cpp
+++ b/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.cpp
@@ -30,6 +30,50 @@ CasDecodeController *CasDecodeController::g_instance = nullptr;
AChoreographer *choreographer;
CasDecodeController *g_controller = nullptr;
+uint32_t CasDecodeController::ReduceFps(uint64_t frameBuffer)
+{
+ const uint64_t maxBuffCount15 = 12;
+ const uint64_t maxBuffCount30 = 8;
+ const uint64_t maxBuffCount48 = 4;
+ uint32_t updateIdx = FPSPATTERNS60;
+ if (frameBuffer >= maxBuffCount15) {
+ updateIdx = FPSPATTERNS15;
+ } else if (frameBuffer >= maxBuffCount30) {
+ updateIdx = FPSPATTERNS30;
+ } else if (frameBuffer >= maxBuffCount48) {
+ updateIdx = FPSPATTERNS48;
+ }
+ return updateIdx;
+}
+
+bool CasDecodeController::IsDiscardFrame(uint32_t updatePatternIdx)
+{
+ if (updatePatternIdx >= FPSPATTERNS_MAX) {
+ INFO("IsDiscardFrame index over limit, index = %u", updatePatternIdx);
+ updatePatternIdx = FPSPATTERNS60;
+ }
+ // 丢帧策略变化
+ if (updatePatternIdx != m_UpdatePatternIdx) {
+ ClearDiscardFrameCache();
+ m_UpdatePatternIdx = updatePatternIdx;
+ }
+ if (m_HoldFrames > 1) {
+ m_HoldFrames--;
+ } else {
+ m_UpdatePatternOffset = (m_UpdatePatternOffset + 1) % strlen(UPDATE_FPSPATTERNS[m_UpdatePatternIdx]);
+ char ch = UPDATE_FPSPATTERNS[m_UpdatePatternIdx][m_UpdatePatternOffset];
+ m_HoldFrames = ch - '0';
+ }
+ return !(m_HoldFrames == 1);
+}
+
+void CasDecodeController::ClearDiscardFrameCache()
+{
+ m_UpdatePatternIdx = FPSPATTERNS60;
+ m_UpdatePatternOffset = 0;
+ m_HoldFrames = 0;
+}
+
void FrameCallback(long frameTime, void* data)
{
if (!g_controller->IsStatus(EngineStat::ENGINE_RUNNING)) {
@@ -50,12 +94,58 @@ void FrameCallback(long frameTime, void* data)
CasFirstVideoFrameListener* firstVideoFrameListener = g_controller->GetCasFirstVideoFrameListener();
CasVideoDecodeStatListener* videoDecodeStatListener = g_controller->GetCasVideoDecodeStatListener();
- iRet = static_cast(decoder->OutputAndDisplay());
- if (iRet == DECODER_OUTPUT_ERR) {
- ERR("Sub-Thread exited.");
- } else if (iRet == DECODER_OUTPUT_RETRY) {
- decoder->OutputAndDisplay();
- } else if (iRet == DECODER_SUCCESS) {
+ // 支持网络缓存堆积时按照策略丢帧
+ 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;
+ }
+ // 缓存堆积时,根据策略丢缓存后马上出下一帧,一次处理2帧
+ uint64_t reduceBegin = 0;
+ uint64_t reduceEnd = 0;
+ uint64_t display1Begin = 0;
+ 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;
+ }
+ isDiscard = g_controller->IsDiscardFrame(updatePatternIdx);
+ // 是否丢帧
+ if (isDiscard) {
+ g_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);
+ }
+ reduceEnd = casVideoUtil->GetNow();
+ display1Begin = casVideoUtil->GetNow();
+ iRet = static_cast(decoder->OutputAndDisplay(!isDiscard));
+ display1End = casVideoUtil->GetNow();
+ if (iRet != DECODER_OUTPUT_RETRY) {
+ g_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();
+ }
+ }
+ uint64_t display2Begin = casVideoUtil->GetNow();
+ if (isDiscard) {
+ iRet = static_cast(decoder->OutputAndDisplay(true));
+ if (iRet != DECODER_OUTPUT_RETRY) {
+ g_controller->m_DisplayFrameNum++;
+ }
+ }
+ uint64_t display2End = casVideoUtil->GetNow();
+
+ if (iRet == DECODER_SUCCESS) {
if (!isDecodeFirstFrame) {
isDecodeFirstFrame = true;
if (firstVideoFrameListener != nullptr) {
@@ -70,7 +160,23 @@ void FrameCallback(long frameTime, void* data)
casVideoUtil->SetTimestamp(nowUs);
videoDecodeStatListener->OnDecodeOneFrame(latency);
}
+ } else if (iRet == DECODER_OUTPUT_ERR) {
+ ERR("FrameCallback OutputAndDisplay error, ret=%d", iRet);
+ }
+ if (g_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);
+ }
+ uint64_t vsyncEnd = casVideoUtil->GetNow();
+ uint64_t duration = vsyncEnd - startUs;
+ uint64_t reduceTime = reduceEnd - reduceBegin;
+ uint64_t display1Time = display1End - display1Begin;
+ uint64_t display2Time = display2End - display2Begin;
+ if (duration > 15000) {
+ ERR("FrameCallback cost time duration:%lu, reduceTime:%lu, displayTime1:%lu, displayTime2:%lu",
+ duration, reduceTime, display1Time, display2Time);
}
+
AChoreographer_postFrameCallback(choreographer, FrameCallback, nullptr);
}
#endif
@@ -238,6 +344,7 @@ uint32_t CasDecodeController::Init(ANativeWindow *nativeWindow, FrameType frameT
return VIDEO_ENGINE_CLIENT_INIT_FAIL;
}
SetStatus(EngineStat::ENGINE_INIT);
+ m_InputFrameNum = 0;
INFO("Init success.");
return SUCCESS;
}
@@ -271,6 +378,12 @@ uint32_t CasDecodeController::Start()
if (outputTask.joinable()) {
outputTask.detach();
}
+
+ m_DisplayFrameNum = 0;
+ m_DiscardFrameNum = 0;
+ isStartDiscard = false;
+ ClearDiscardFrameCache();
+
INFO("Start success.");
return SUCCESS;
}
@@ -298,6 +411,7 @@ uint32_t CasDecodeController::Decode(const uint8_t *buf, size_t length)
ERR("Failed to process input data.");
return VIDEO_ENGINE_CLIENT_DECODE_ERR;
}
+ m_InputFrameNum++;
return SUCCESS;
}
diff --git a/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.h b/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.h
index 71ba810a04eba82ab2cd1c0a802740fca48c57b9..a93fc76eeeaf77d19212c4c4c0b341f12ac5671f 100644
--- a/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.h
+++ b/cloudphone/src/main/cpp/cas_decoder/CasDecodeController.h
@@ -23,6 +23,25 @@
#include "CasVideoEngineCommon.h"
#include "CasDecoder.h"
+enum FramePatterns {
+ FPSPATTERNS15 = 0, // 0
+ FPSPATTERNS24, // 1
+ FPSPATTERNS25, // 2
+ FPSPATTERNS30, // 3
+ FPSPATTERNS48, // 4
+ FPSPATTERNS60, // 5
+ FPSPATTERNS_MAX,
+};
+
+const char UPDATE_FPSPATTERNS[FPSPATTERNS_MAX][8] = {
+ "4", // 15 fps 丢3保1
+ "32", // 24 fps 丢1保1,丢2保1
+ "32322", // 25 fps 丢1保1,丢1保1,丢2保1,丢1保1,丢2保1
+ "2", // 30 fps 丢1保1
+ "2111", // 48 fps 保3,丢1保1
+ "1", // 60 fps 不丢
+};
+
class CasDecodeController {
public:
/*
@@ -149,6 +168,19 @@ public:
CasVideoDecodeStatListener* GetCasVideoDecodeStatListener();
+ bool IsDiscardFrame(uint32_t updatePatternIdx);
+ void ClearDiscardFrameCache();
+ uint32_t ReduceFps(uint64_t frameBuffer);
+
+ uint64_t m_InputFrameNum = 0;
+ uint64_t m_DisplayFrameNum = 0;
+ uint64_t m_DiscardFrameNum = 0;
+ uint32_t m_UpdatePatternIdx = FPSPATTERNS60;
+ uint32_t m_UpdatePatternOffset = 0;
+ uint32_t m_HoldFrames = 0;
+
+ bool isStartDiscard = false; // 是否触发丢帧策略,进行日志打印
+
private:
/*
* @fn CasDecodeController
diff --git a/cloudphone/src/main/cpp/cas_decoder/CasDecoder.cpp b/cloudphone/src/main/cpp/cas_decoder/CasDecoder.cpp
index 2259a62831e3362f18b5b8e229547db22dcc3229..9fe7a6506c31c805304e42e895b1f24cec2c4eef 100644
--- a/cloudphone/src/main/cpp/cas_decoder/CasDecoder.cpp
+++ b/cloudphone/src/main/cpp/cas_decoder/CasDecoder.cpp
@@ -222,7 +222,7 @@ uint32_t CasDecoder::Input(const uint8_t *buf, const size_t len)
* DECODER_OUTPUT_RETRY
* DECODER_OUTPUT_ERR
*/
-uint32_t CasDecoder::OutputAndDisplay()
+uint32_t CasDecoder::OutputAndDisplay(bool isDisplay)
{
AMediaCodecBufferInfo info;
// Get the index of the next available buffer of processed data.
@@ -245,14 +245,13 @@ uint32_t CasDecoder::OutputAndDisplay()
m_outputFrameCount++;
DBG("DecodeLog: dequeue output buffer success: bufId = %d, m_outputFrameCount = %d", bufId, m_outputFrameCount);
- if (m_inputFrameCount - m_outputFrameCount > 1 && !m_isDropFrame) {
+ if (!isDisplay) {
DBG("DecodeLog: frame count %d output buffer latency exceed %d frames, release one frame. ", m_outputFrameCount, m_inputFrameCount - m_outputFrameCount);
AMediaCodec_releaseOutputBuffer(m_mediaCodec, bufId, false);
- m_isDropFrame = true;
} else {
AMediaCodec_releaseOutputBuffer(m_mediaCodec, bufId, true);
- m_isDropFrame = false;
}
+
return DECODER_SUCCESS;
}
switch (bufId) {
diff --git a/cloudphone/src/main/cpp/cas_decoder/CasDecoder.h b/cloudphone/src/main/cpp/cas_decoder/CasDecoder.h
index d3868b11727cd45d45deddc3702ae76e6490a83d..fa2dc1939c0d674766a42e0ff657c4dab95368b1 100644
--- a/cloudphone/src/main/cpp/cas_decoder/CasDecoder.h
+++ b/cloudphone/src/main/cpp/cas_decoder/CasDecoder.h
@@ -79,7 +79,7 @@ public:
* DECODER_OUTPUT_RETRY
* DECODER_OUTPUT_ERR
*/
- uint32_t OutputAndDisplay();
+ uint32_t OutputAndDisplay(bool idDisplay);
/*
* @fn GetFps
diff --git a/cloudphone/src/main/cpp/cas_decoder/CasVideoUtil.cpp b/cloudphone/src/main/cpp/cas_decoder/CasVideoUtil.cpp
index dd4c27cd731d2183d74307d7bae13d54866a110f..f56b7ead3e24673c5b135d8ee1b6d229f82294b0 100644
--- a/cloudphone/src/main/cpp/cas_decoder/CasVideoUtil.cpp
+++ b/cloudphone/src/main/cpp/cas_decoder/CasVideoUtil.cpp
@@ -153,4 +153,27 @@ void CasVideoUtil::Release() noexcept
delete m_frameQueue;
m_frameQueue = nullptr;
}
+}
+
+void CasVideoUtil::DropOneFrame() {
+ uint64_t currentTime = GetNow();
+
+ m_VideoDropCount++;
+
+ if (m_lastTimeRefreshDropFPS == 0) {
+ m_lastTimeRefreshDropFPS = currentTime;
+ }
+ if (currentTime - m_lastTimeRefreshDropFPS > DURATION_USEC) {
+ m_VideoDropFPS = m_VideoDropCount;
+ m_VideoDropCount = 0;
+ m_lastTimeRefreshDropFPS = currentTime;
+ }
+}
+
+int CasVideoUtil::GetCurrentDropFPS() {
+ uint64_t currentTime = GetNow();
+ if (currentTime - m_lastTimeRefreshDropFPS > 2 * DURATION_USEC) {
+ return 0;
+ }
+ return m_VideoDropFPS;
}
\ No newline at end of file
diff --git a/cloudphone/src/main/cpp/cas_decoder/CasVideoUtil.h b/cloudphone/src/main/cpp/cas_decoder/CasVideoUtil.h
index 7784afe82ea89d5e2f29615a92425bb7b1989c7b..f31d48a4c65a71173e16e086f363638e86427321 100644
--- a/cloudphone/src/main/cpp/cas_decoder/CasVideoUtil.h
+++ b/cloudphone/src/main/cpp/cas_decoder/CasVideoUtil.h
@@ -60,6 +60,18 @@ public:
*/
uint32_t GetFps();
+ /*
+ * @fn DropOneFrame
+ * @brief count when drop one frame
+ */
+ void DropOneFrame();
+
+ /*
+ * @fn GetCurrentDropFPS
+ * @brief get drop FPS
+ */
+ int GetCurrentDropFPS();
+
private:
/*
* @brief: construct
@@ -81,5 +93,8 @@ private:
static CasVideoUtil *g_instance;
static std::mutex g_instanceLock;
CasItemQueue *m_frameQueue = nullptr;
+ int m_VideoDropCount = 0;
+ int m_VideoDropFPS = 0;
+ uint64_t m_lastTimeRefreshDropFPS = 0;
};
#endif // CLOUDAPPSDK_CASVIDEOUTIL_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 a7acdc0eb87d1e90aea605012289326cabb63c2e..d3091a23e1fd2e3fccdd3415b313310ba45c54c8 100644
--- a/cloudphone/src/main/cpp/cas_service/CasDataPipe.cpp
+++ b/cloudphone/src/main/cpp/cas_service/CasDataPipe.cpp
@@ -59,6 +59,7 @@ void CasDataPipe::Handle(void *pPkt)
if (this->m_status) {
stream_msg_head_t *head = (stream_msg_head_t *)pPkt;
this->m_totalSize += head->GetPayloadSize();
+ this->m_unprocessedSize = m_deque.size() + 1;
this->m_deque.push_back(pPkt);
if (this->m_block) {
@@ -121,4 +122,9 @@ void *CasDataPipe::GetNextPktWaitFor(int timeout)
int CasDataPipe::GetNumItems()
{
return this->m_deque.size();
+}
+
+void CasDataPipe::UpdateUnprocessedSize()
+{
+ this->m_unprocessedSize = GetNumItems();
}
\ No newline at end of file
diff --git a/cloudphone/src/main/cpp/cas_service/CasDataPipe.h b/cloudphone/src/main/cpp/cas_service/CasDataPipe.h
index 9b4efa26f075f4deb8b8009d68a3c802013e3bb3..a336553ce2a6c843d31cb446797e3ebb4f58aa28 100644
--- a/cloudphone/src/main/cpp/cas_service/CasDataPipe.h
+++ b/cloudphone/src/main/cpp/cas_service/CasDataPipe.h
@@ -38,6 +38,12 @@ public:
void Clear() noexcept;
+ /**
+ * 当使用数据的地方处理完数据时调用,更新未处理数据数量
+ */
+ void UpdateUnprocessedSize();
+
+ std::atomic_uint64_t m_unprocessedSize{0};
protected:
uint32_t m_totalSize;
std::deque m_deque;
diff --git a/cloudphone/src/main/cpp/cas_service/CasHeartbeatThread.cpp b/cloudphone/src/main/cpp/cas_service/CasHeartbeatThread.cpp
index f7458820c82aae3ae815cbb9c82f80db96be56bb..aafb7c29245342b1978d899beb0cf77aec05f07e 100644
--- a/cloudphone/src/main/cpp/cas_service/CasHeartbeatThread.cpp
+++ b/cloudphone/src/main/cpp/cas_service/CasHeartbeatThread.cpp
@@ -26,6 +26,7 @@ const uint64_t MAX_LAG = 10 * 1000000;
CasHeartbeatThread::CasHeartbeatThread(CasHeartbeatController *heartbeatController, CasSocket *sock)
{
+ m_threadStatus = CAS_THREAD_INIT;
m_casSocket = sock;
m_heartbeatController = heartbeatController;
}
diff --git a/cloudphone/src/main/cpp/cas_service/CasVideoHDecodeThread.cpp b/cloudphone/src/main/cpp/cas_service/CasVideoHDecodeThread.cpp
index 130bd448a81628e934b17806cf11b25c80ad4a59..ab3045189b32b02430f25d77e4287099377d9dee 100644
--- a/cloudphone/src/main/cpp/cas_service/CasVideoHDecodeThread.cpp
+++ b/cloudphone/src/main/cpp/cas_service/CasVideoHDecodeThread.cpp
@@ -146,6 +146,7 @@ void DecodeTaskEntry(CasVideoHDecodeThread *decodeThreadObj)
uint32_t ret = 0;
ret = videoEngine->DecodeFrame((uint8_t *)onePkt + sizeof(stream_msg_head_t), dataSize);
+ videoPktStream->UpdateUnprocessedSize();
if (ret == SUCCESS) {
lastFramePending = false;
diff --git a/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp b/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp
index 9c1036145c4af54cc16efc5c63dbea003b688556..cb201a2c50b245ff963c051755a28e74c6f78a48 100644
--- a/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp
+++ b/cloudphone/src/main/cpp/cas_stream/CasStreamBuildSender.cpp
@@ -121,7 +121,7 @@ int CasStreamBuildSender::SendDataToServer(CasMsgType type, const void *buf, siz
ssize_t stat = m_socket->Send(outBuffer + pos, dataLen - pos);
#if MTRANS_ENABLED
- if (m_mtrans != nullptr && type == HeartBeat) {
+ if (m_mtrans != nullptr && type == HeartBeat && m_socket->GetStatus() == SOCKET_STATUS_RUNNING) {
m_mtrans->SendCmdData(reinterpret_cast(outBuffer + pos), dataLen - pos);
}
#endif
diff --git a/cloudphone/src/main/cpp/cas_stream/CasStreamRecvParser.cpp b/cloudphone/src/main/cpp/cas_stream/CasStreamRecvParser.cpp
index 09746b90d9944d7f2f12a1a03801807f5d773186..20a6fcf74529be62e14416ad02f36bd189c6f635 100644
--- a/cloudphone/src/main/cpp/cas_stream/CasStreamRecvParser.cpp
+++ b/cloudphone/src/main/cpp/cas_stream/CasStreamRecvParser.cpp
@@ -91,6 +91,26 @@ CasStreamParseThread::~CasStreamParseThread()
this->m_streamRecvParser = nullptr;
}
+int FastDecodeFrame(uint8_t *buf, size_t length)
+{
+ CasController *casController = CasController::GetInstance();
+ if (casController == nullptr) {
+ ERR("Get CasController failed.");
+ return VIDEO_ENGINE_CLIENT_DESTROY_ERR;
+ }
+ CasVideoHDecodeThread *thread = casController->GetVideoDecodeThread();
+ if (thread == nullptr) {
+ ERR("Get CasVideoHDecodeThread failed.");
+ return VIDEO_ENGINE_CLIENT_DESTROY_ERR;
+ }
+ CasVideoEngine *engine = thread->GetVideoEngine();
+ if (engine == nullptr) {
+ ERR("Get CasVideoEngine failed.");
+ return VIDEO_ENGINE_CLIENT_DESTROY_ERR;
+ }
+ return engine->DecodeFrame(buf, length);
+}
+
void HandleCompletePktMsg(CasStreamParseThread *streamParseThread, streamMsgHead *msgHead, unsigned char *recvBuf,
unsigned int pktStartPos)
{
@@ -100,6 +120,19 @@ 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 (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/libs/Arm32/libmtrans.a b/cloudphone/src/main/cpp/libs/Arm32/libmtrans.a
index 5140bfd878c72f956a55cbf4d5fd38a447277698..c5ea687fc5b3b4d8e3f0d0ae71b159764423fe2a 100644
Binary files a/cloudphone/src/main/cpp/libs/Arm32/libmtrans.a and b/cloudphone/src/main/cpp/libs/Arm32/libmtrans.a differ
diff --git a/cloudphone/src/main/cpp/libs/Arm64/libmtrans.a b/cloudphone/src/main/cpp/libs/Arm64/libmtrans.a
index 4410512a4914f657f2fa4035c68fc872cd122a3f..5cc7394353ed5939ec60b3dc2ef982209d5b98e5 100644
Binary files a/cloudphone/src/main/cpp/libs/Arm64/libmtrans.a and b/cloudphone/src/main/cpp/libs/Arm64/libmtrans.a differ
diff --git a/cloudphone/src/main/cpp/libs/mtrans/include/net_trans.h b/cloudphone/src/main/cpp/libs/mtrans/include/net_trans.h
index 5b3c0b6bf20b2accafc6a5f0e4d4c29d637e4555..4f696811f8d4beae506dcb0a20853936ac52df63 100644
--- a/cloudphone/src/main/cpp/libs/mtrans/include/net_trans.h
+++ b/cloudphone/src/main/cpp/libs/mtrans/include/net_trans.h
@@ -25,6 +25,8 @@ public:
virtual ~NetTrans();
+ void EnableLog(const std::string& logPath, NET_TRANS_LOG_LEVLE logLevel);
+
int Init(NET_TRANS_PEER_TYPE peerType,
char const *ip, int port,
TransConfigParam param,
@@ -32,7 +34,9 @@ public:
RecvAudioDataCallback audioCallback,
UpdataBitrateCallback bitrateCallback,
RequestKeyFrameCallback keyFrameCallback,
- RecvCmdDataCallback cmdCallback);
+ RecvCmdDataCallback cmdCallback,
+ RecvAudioDecodeCallback audioDecodeCallback,
+ TransLogCallback transLogCallback);
int Start();
int Stop();
@@ -41,6 +45,20 @@ public:
int SendAudioData(uint8_t *data, int len);
int SendCmdData(uint8_t *data, int len);
+ int SendSensorData(uint8_t *data, int len);
+
+ int SendLocationData(uint8_t *data, int len);
+
+ int SendTouchEventData(uint8_t *data, int len);
+
+ int SendKeyEventData(uint8_t *data, int len);
+
+ int SendMotionEventData(uint8_t *data, int len);
+ int GetVideoRecvStats(StreamRecvStats* stats);
+ int GetAudioRecvStats(StreamRecvStats* stats);
+ int GetRtt();
+ int GetCmdSendStats(StreamSendStats* stats);
+
private:
class NetTransPri;
class NetTransPri *m_netTransPri;
diff --git a/cloudphone/src/main/cpp/libs/mtrans/include/net_trans_def.h b/cloudphone/src/main/cpp/libs/mtrans/include/net_trans_def.h
index dc8903ba5ef9a5f0fe6a75e2bb9debc3949a45e0..bd581580e7d57ac5b23aee5135d969721380adc2 100644
--- a/cloudphone/src/main/cpp/libs/mtrans/include/net_trans_def.h
+++ b/cloudphone/src/main/cpp/libs/mtrans/include/net_trans_def.h
@@ -27,10 +27,127 @@ struct TransConfigParam {
uint32_t maxVideoSendBitrate;
};
+enum NET_TRANS_LOG_LEVLE {
+ TRANS_LOG_LEVEL_OFF, // 关闭日志
+ TRANS_LOG_LEVEL_ERROR, // error级别
+ TRANS_LOG_LEVEL_WARN, // warn级别
+ TRANS_LOG_LEVEL_INFO, // info级别
+ TRANS_LOG_LEVEL_DEBUG // debug级别
+};
+
+struct AudioJbDecode {
+ int32_t bfi;
+ uint8_t * payload;
+ uint32_t inputLength;
+ int16_t* outputData;
+ uint32_t outputLength;
+ uint32_t outputBufSize;
+ int32_t frameType;
+ uint32_t metaDataOffset;
+};
+
+struct TouchEventParam {
+ uint8_t id;
+ uint8_t action;
+ uint16_t x;
+ uint16_t y;
+ uint16_t pressure;
+ int32_t time;
+ uint8_t orientation;
+ uint16_t height;
+ uint16_t width;
+} __attribute__((packed));
+
+struct KeyEventParam {
+ uint16_t keycode;
+ uint16_t action;
+} __attribute__((packed));
+
+struct MotionEventParam {
+ uint16_t masterAxis;
+ int32_t masterValue;
+ uint16_t secondaryAxis;
+ int32_t secondaryValue;
+} __attribute__((packed));
+
+struct VideoRecvStats {
+ uint32_t recvFrameRate;
+ uint32_t redRate;
+ uint32_t recoverRate;
+ uint32_t jbListpacketNum;
+ uint32_t sendKeyRequestCnt;
+ int32_t jbJitter;
+ uint32_t refFrameErrorCnt;
+ uint64_t aveJbDelayAfterBuild;
+ uint64_t recvAveDelay;
+ uint64_t recvMaxDelay;
+};
+
+struct AudioRecvStats {
+ uint32_t receiveTotalTime;
+ uint32_t plcCount;
+ uint32_t tsmCompressCount;
+ uint32_t tsmStretchCount;
+ int32_t totalDelay;
+ int32_t estTotalDelay;
+ uint64_t noVoiceFrameCnt;
+ uint32_t getFrameCount;
+};
+
+struct StreamRecvStats {
+ uint32_t recvBitrate;
+ uint32_t lostRate;
+ uint32_t jitter;
+ uint32_t lostPktCnt;
+ uint32_t maxContLostPktCnt;
+ uint32_t recvTotalFrameCnt;
+ uint32_t jbDepth;
+ uint32_t jbTotalFrameCnt;
+ uint32_t packetRate;
+ uint32_t periodNotRecvPktCnt;
+ uint32_t periodNotRecvPktTime;
+ uint32_t periodFreezeFrameCnt;
+ uint32_t periodFreezeTime;
+ uint32_t sendNackRequestCnt;
+ union {
+ VideoRecvStats videoStats;
+ AudioRecvStats audioStats;
+ };
+};
+
+struct VideoSendStats {
+ uint32_t sendFrameRate;
+ uint32_t availableEncBitrate;
+ uint32_t inputFrameCnt;
+ uint32_t sendFrameCnt;
+ uint32_t redRate;
+ uint32_t recvKeyRequestCnt;
+ uint32_t recvNackRequestCnt;
+ uint32_t nackResponsePktCnt;
+ uint32_t nackNotRspCnt;
+};
+
+struct StreamSendStats {
+ uint32_t encBitrate;
+ uint32_t fecBitrate;
+ uint32_t arqBitrate;
+ uint32_t packetRate;
+ uint32_t rtt;
+ uint32_t remoteJitter;
+ uint32_t remoteLostRate;
+ uint32_t pacingSendMaxDelay;
+ uint32_t pacingSendAveDelay;
+ uint32_t periodNotSendPktCnt;
+ uint64_t periodNotSendPktTime;
+ VideoSendStats videoStats;
+};
+
typedef int32_t (*RecvVideoDataCallback)(uint8_t* data, uint32_t length);
typedef int32_t (*RecvAudioDataCallback)(uint8_t* data, uint32_t length);
+typedef int32_t (*RecvAudioDecodeCallback)(int32_t streamId, AudioJbDecode* audioJbDecode);
typedef int32_t (*RecvCmdDataCallback)(uint8_t* data, uint32_t length);
typedef void (*UpdataBitrateCallback)(uint32_t encBitrate, uint32_t totalBitrate);
typedef void (*RequestKeyFrameCallback)();
+typedef void (*TransLogCallback)(const char* str, uint32_t length);
#endif //HUAWEICLOUD_CLOUDPHONEACCESS_ENGINE_NET_TRANS_DEF_H
diff --git a/cloudphone/src/main/cpp/libs/spdlog/async.h b/cloudphone/src/main/cpp/libs/spdlog/async.h
new file mode 100644
index 0000000000000000000000000000000000000000..e96abd199b4978f8265b0a30c5b4e05111918bff
--- /dev/null
+++ b/cloudphone/src/main/cpp/libs/spdlog/async.h
@@ -0,0 +1,100 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+//
+// Async logging using global thread pool
+// All loggers created here share same global thread pool.
+// Each log message is pushed to a queue along with a shared pointer to the
+// logger.
+// If a logger deleted while having pending messages in the queue, it's actual
+// destruction will defer
+// until all its messages are processed by the thread pool.
+// This is because each message in the queue holds a shared_ptr to the
+// originating logger.
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+namespace spdlog {
+
+namespace details {
+static const size_t default_async_q_size = 8192;
+}
+
+// async logger factory - creates async loggers backed with thread pool.
+// if a global thread pool doesn't already exist, create it with default queue
+// size of 8192 items and single thread.
+template
+struct async_factory_impl {
+ template
+ static std::shared_ptr create(std::string logger_name, SinkArgs &&...args) {
+ auto ®istry_inst = details::registry::instance();
+
+ // create global thread pool if not already exists..
+
+ auto &mutex = registry_inst.tp_mutex();
+ std::lock_guard tp_lock(mutex);
+ auto tp = registry_inst.get_tp();
+ if (tp == nullptr) {
+ tp = std::make_shared(details::default_async_q_size, 1U);
+ registry_inst.set_tp(tp);
+ }
+
+ auto sink = std::make_shared(std::forward(args)...);
+ auto new_logger = std::make_shared(std::move(logger_name), std::move(sink),
+ std::move(tp), OverflowPolicy);
+ registry_inst.initialize_logger(new_logger);
+ return new_logger;
+ }
+};
+
+using async_factory = async_factory_impl;
+using async_factory_nonblock = async_factory_impl;
+
+template
+inline std::shared_ptr create_async(std::string logger_name,
+ SinkArgs &&...sink_args) {
+ return async_factory::create(std::move(logger_name),
+ std::forward(sink_args)...);
+}
+
+template
+inline std::shared_ptr create_async_nb(std::string logger_name,
+ SinkArgs &&...sink_args) {
+ return async_factory_nonblock::create(std::move(logger_name),
+ std::forward(sink_args)...);
+}
+
+// set global thread pool.
+inline void init_thread_pool(size_t q_size,
+ size_t thread_count,
+ std::function on_thread_start,
+ std::function on_thread_stop) {
+ auto tp = std::make_shared(q_size, thread_count, on_thread_start,
+ on_thread_stop);
+ details::registry::instance().set_tp(std::move(tp));
+}
+
+inline void init_thread_pool(size_t q_size,
+ size_t thread_count,
+ std::function on_thread_start) {
+ init_thread_pool(q_size, thread_count, on_thread_start, [] {});
+}
+
+inline void init_thread_pool(size_t q_size, size_t thread_count) {
+ init_thread_pool(
+ q_size, thread_count, [] {}, [] {});
+}
+
+// get the global thread pool.
+inline std::shared_ptr thread_pool() {
+ return details::registry::instance().get_tp();
+}
+} // namespace spdlog
diff --git a/cloudphone/src/main/cpp/libs/spdlog/async_logger-inl.h b/cloudphone/src/main/cpp/libs/spdlog/async_logger-inl.h
new file mode 100644
index 0000000000000000000000000000000000000000..499800d88063ca2c105d1b7c8ea9691e60ee86e2
--- /dev/null
+++ b/cloudphone/src/main/cpp/libs/spdlog/async_logger-inl.h
@@ -0,0 +1,86 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+ #include
+#endif
+
+#include
+#include
+
+#include
+#include
+
+SPDLOG_INLINE spdlog::async_logger::async_logger(std::string logger_name,
+ sinks_init_list sinks_list,
+ std::weak_ptr tp,
+ async_overflow_policy overflow_policy)
+ : async_logger(std::move(logger_name),
+ sinks_list.begin(),
+ sinks_list.end(),
+ std::move(tp),
+ overflow_policy) {}
+
+SPDLOG_INLINE spdlog::async_logger::async_logger(std::string logger_name,
+ sink_ptr single_sink,
+ std::weak_ptr tp,
+ async_overflow_policy overflow_policy)
+ : async_logger(
+ std::move(logger_name), {std::move(single_sink)}, std::move(tp), overflow_policy) {}
+
+// send the log message to the thread pool
+SPDLOG_INLINE void spdlog::async_logger::sink_it_(const details::log_msg &msg){
+ SPDLOG_TRY{if (auto pool_ptr = thread_pool_.lock()){
+ pool_ptr->post_log(shared_from_this(), msg, overflow_policy_);
+}
+else {
+ throw_spdlog_ex("async log: thread pool doesn't exist anymore");
+}
+}
+SPDLOG_LOGGER_CATCH(msg.source)
+}
+
+// send flush request to the thread pool
+SPDLOG_INLINE void spdlog::async_logger::flush_(){SPDLOG_TRY{auto pool_ptr = thread_pool_.lock();
+if (!pool_ptr) {
+ throw_spdlog_ex("async flush: thread pool doesn't exist anymore");
+}
+
+std::future future = pool_ptr->post_flush(shared_from_this(), overflow_policy_);
+// Wait for the flush operation to complete.
+// This might throw exception if the flush message get dropped because of overflow.
+future.get();
+}
+SPDLOG_LOGGER_CATCH(source_loc())
+}
+
+//
+// backend functions - called from the thread pool to do the actual job
+//
+SPDLOG_INLINE void spdlog::async_logger::backend_sink_it_(const details::log_msg &msg) {
+ for (auto &sink : sinks_) {
+ if (sink->should_log(msg.level)) {
+ SPDLOG_TRY { sink->log(msg); }
+ SPDLOG_LOGGER_CATCH(msg.source)
+ }
+ }
+
+ if (should_flush_(msg)) {
+ backend_flush_();
+ }
+}
+
+SPDLOG_INLINE void spdlog::async_logger::backend_flush_() {
+ for (auto &sink : sinks_) {
+ SPDLOG_TRY { sink->flush(); }
+ SPDLOG_LOGGER_CATCH(source_loc())
+ }
+}
+
+SPDLOG_INLINE std::shared_ptr spdlog::async_logger::clone(std::string new_name) {
+ auto cloned = std::make_shared(*this);
+ cloned->name_ = std::move(new_name);
+ return cloned;
+}
diff --git a/cloudphone/src/main/cpp/libs/spdlog/async_logger.h b/cloudphone/src/main/cpp/libs/spdlog/async_logger.h
new file mode 100644
index 0000000000000000000000000000000000000000..846c4c6f080290f3022fecede2c85dfedaafa618
--- /dev/null
+++ b/cloudphone/src/main/cpp/libs/spdlog/async_logger.h
@@ -0,0 +1,74 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+// Fast asynchronous logger.
+// Uses pre allocated queue.
+// Creates a single back thread to pop messages from the queue and log them.
+//
+// Upon each log write the logger:
+// 1. Checks if its log level is enough to log the message
+// 2. Push a new copy of the message to a queue (or block the caller until
+// space is available in the queue)
+// Upon destruction, logs all remaining messages in the queue before
+// destructing..
+
+#include
+
+namespace spdlog {
+
+// Async overflow policy - block by default.
+enum class async_overflow_policy {
+ block, // Block until message can be enqueued
+ overrun_oldest, // Discard oldest message in the queue if full when trying to
+ // add new item.
+ discard_new // Discard new message if the queue is full when trying to add new item.
+};
+
+namespace details {
+class thread_pool;
+}
+
+class SPDLOG_API async_logger final : public std::enable_shared_from_this,
+ public logger {
+ friend class details::thread_pool;
+
+public:
+ template
+ async_logger(std::string logger_name,
+ It begin,
+ It end,
+ std::weak_ptr tp,
+ async_overflow_policy overflow_policy = async_overflow_policy::block)
+ : logger(std::move(logger_name), begin, end),
+ thread_pool_(std::move(tp)),
+ overflow_policy_(overflow_policy) {}
+
+ async_logger(std::string logger_name,
+ sinks_init_list sinks_list,
+ std::weak_ptr tp,
+ async_overflow_policy overflow_policy = async_overflow_policy::block);
+
+ async_logger(std::string logger_name,
+ sink_ptr single_sink,
+ std::weak_ptr tp,
+ async_overflow_policy overflow_policy = async_overflow_policy::block);
+
+ std::shared_ptr clone(std::string new_name) override;
+
+protected:
+ void sink_it_(const details::log_msg &msg) override;
+ void flush_() override;
+ void backend_sink_it_(const details::log_msg &incoming_log_msg);
+ void backend_flush_();
+
+private:
+ std::weak_ptr thread_pool_;
+ async_overflow_policy overflow_policy_;
+};
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+ #include "async_logger-inl.h"
+#endif
diff --git a/cloudphone/src/main/cpp/libs/spdlog/cfg/argv.h b/cloudphone/src/main/cpp/libs/spdlog/cfg/argv.h
new file mode 100644
index 0000000000000000000000000000000000000000..7de2f83e7d2a5fa9dec8a90e8e0d4ec79c9620ed
--- /dev/null
+++ b/cloudphone/src/main/cpp/libs/spdlog/cfg/argv.h
@@ -0,0 +1,40 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+#include
+#include
+
+//
+// Init log levels using each argv entry that starts with "SPDLOG_LEVEL="
+//
+// set all loggers to debug level:
+// example.exe "SPDLOG_LEVEL=debug"
+
+// set logger1 to trace level
+// example.exe "SPDLOG_LEVEL=logger1=trace"
+
+// turn off all logging except for logger1 and logger2:
+// example.exe "SPDLOG_LEVEL=off,logger1=debug,logger2=info"
+
+namespace spdlog {
+namespace cfg {
+
+// search for SPDLOG_LEVEL= in the args and use it to init the levels
+inline void load_argv_levels(int argc, const char **argv) {
+ const std::string spdlog_level_prefix = "SPDLOG_LEVEL=";
+ for (int i = 1; i < argc; i++) {
+ std::string arg = argv[i];
+ if (arg.find(spdlog_level_prefix) == 0) {
+ auto levels_string = arg.substr(spdlog_level_prefix.size());
+ helpers::load_levels(levels_string);
+ }
+ }
+}
+
+inline void load_argv_levels(int argc, char **argv) {
+ load_argv_levels(argc, const_cast(argv));
+}
+
+} // namespace cfg
+} // namespace spdlog
diff --git a/cloudphone/src/main/cpp/libs/spdlog/cfg/env.h b/cloudphone/src/main/cpp/libs/spdlog/cfg/env.h
new file mode 100644
index 0000000000000000000000000000000000000000..6e554145445ae42b71d5fb8bf33ddc89ce8e46fb
--- /dev/null
+++ b/cloudphone/src/main/cpp/libs/spdlog/cfg/env.h
@@ -0,0 +1,36 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+#include
+#include
+#include
+
+//
+// Init levels and patterns from env variables SPDLOG_LEVEL
+// Inspired from Rust's "env_logger" crate (https://crates.io/crates/env_logger).
+// Note - fallback to "info" level on unrecognized levels
+//
+// Examples:
+//
+// set global level to debug:
+// export SPDLOG_LEVEL=debug
+//
+// turn off all logging except for logger1:
+// export SPDLOG_LEVEL="*=off,logger1=debug"
+//
+
+// turn off all logging except for logger1 and logger2:
+// export SPDLOG_LEVEL="off,logger1=debug,logger2=info"
+
+namespace spdlog {
+namespace cfg {
+inline void load_env_levels() {
+ auto env_val = details::os::getenv("SPDLOG_LEVEL");
+ if (!env_val.empty()) {
+ helpers::load_levels(env_val);
+ }
+}
+
+} // namespace cfg
+} // namespace spdlog
diff --git a/cloudphone/src/main/cpp/libs/spdlog/cfg/helpers-inl.h b/cloudphone/src/main/cpp/libs/spdlog/cfg/helpers-inl.h
new file mode 100644
index 0000000000000000000000000000000000000000..93650a2e843f2e118a9e458218f9ec1183c7e541
--- /dev/null
+++ b/cloudphone/src/main/cpp/libs/spdlog/cfg/helpers-inl.h
@@ -0,0 +1,107 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+ #include
+#endif
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+namespace spdlog {
+namespace cfg {
+namespace helpers {
+
+// inplace convert to lowercase
+inline std::string &to_lower_(std::string &str) {
+ std::transform(str.begin(), str.end(), str.begin(), [](char ch) {
+ return static_cast((ch >= 'A' && ch <= 'Z') ? ch + ('a' - 'A') : ch);
+ });
+ return str;
+}
+
+// inplace trim spaces
+inline std::string &trim_(std::string &str) {
+ const char *spaces = " \n\r\t";
+ str.erase(str.find_last_not_of(spaces) + 1);
+ str.erase(0, str.find_first_not_of(spaces));
+ return str;
+}
+
+// return (name,value) trimmed pair from given "name=value" string.
+// return empty string on missing parts
+// "key=val" => ("key", "val")
+// " key = val " => ("key", "val")
+// "key=" => ("key", "")
+// "val" => ("", "val")
+
+inline std::pair extract_kv_(char sep, const std::string &str) {
+ auto n = str.find(sep);
+ std::string k, v;
+ if (n == std::string::npos) {
+ v = str;
+ } else {
+ k = str.substr(0, n);
+ v = str.substr(n + 1);
+ }
+ return std::make_pair(trim_(k), trim_(v));
+}
+
+// return vector of key/value pairs from sequence of "K1=V1,K2=V2,.."
+// "a=AAA,b=BBB,c=CCC,.." => {("a","AAA"),("b","BBB"),("c", "CCC"),...}
+inline std::unordered_map extract_key_vals_(const std::string &str) {
+ std::string token;
+ std::istringstream token_stream(str);
+ std::unordered_map rv{};
+ while (std::getline(token_stream, token, ',')) {
+ if (token.empty()) {
+ continue;
+ }
+ auto kv = extract_kv_('=', token);
+ rv[kv.first] = kv.second;
+ }
+ return rv;
+}
+
+SPDLOG_INLINE void load_levels(const std::string &input) {
+ if (input.empty() || input.size() > 512) {
+ return;
+ }
+
+ auto key_vals = extract_key_vals_(input);
+ std::unordered_map levels;
+ level::level_enum global_level = level::info;
+ bool global_level_found = false;
+
+ for (auto &name_level : key_vals) {
+ auto &logger_name = name_level.first;
+ auto level_name = to_lower_(name_level.second);
+ auto level = level::from_str(level_name);
+ // ignore unrecognized level names
+ if (level == level::off && level_name != "off") {
+ continue;
+ }
+ if (logger_name.empty()) // no logger name indicate global level
+ {
+ global_level_found = true;
+ global_level = level;
+ } else {
+ levels[logger_name] = level;
+ }
+ }
+
+ details::registry::instance().set_levels(std::move(levels),
+ global_level_found ? &global_level : nullptr);
+}
+
+} // namespace helpers
+} // namespace cfg
+} // namespace spdlog
diff --git a/cloudphone/src/main/cpp/libs/spdlog/cfg/helpers.h b/cloudphone/src/main/cpp/libs/spdlog/cfg/helpers.h
new file mode 100644
index 0000000000000000000000000000000000000000..c02381891c3dc3812701c2433ba35756bce968de
--- /dev/null
+++ b/cloudphone/src/main/cpp/libs/spdlog/cfg/helpers.h
@@ -0,0 +1,29 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include
+#include
+
+namespace spdlog {
+namespace cfg {
+namespace helpers {
+//
+// Init levels from given string
+//
+// Examples:
+//
+// set global level to debug: "debug"
+// turn off all logging except for logger1: "off,logger1=debug"
+// turn off all logging except for logger1 and logger2: "off,logger1=debug,logger2=info"
+//
+SPDLOG_API void load_levels(const std::string &txt);
+} // namespace helpers
+
+} // namespace cfg
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+ #include "helpers-inl.h"
+#endif // SPDLOG_HEADER_ONLY
diff --git a/cloudphone/src/main/cpp/libs/spdlog/common-inl.h b/cloudphone/src/main/cpp/libs/spdlog/common-inl.h
new file mode 100644
index 0000000000000000000000000000000000000000..a8a0453c14d72550d29f23122d01fd638e7828ac
--- /dev/null
+++ b/cloudphone/src/main/cpp/libs/spdlog/common-inl.h
@@ -0,0 +1,68 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+ #include
+#endif
+
+#include
+#include
+
+namespace spdlog {
+namespace level {
+
+#if __cplusplus >= 201703L
+constexpr
+#endif
+ static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES;
+
+static const char *short_level_names[] SPDLOG_SHORT_LEVEL_NAMES;
+
+SPDLOG_INLINE const string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT {
+ return level_string_views[l];
+}
+
+SPDLOG_INLINE const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT {
+ return short_level_names[l];
+}
+
+SPDLOG_INLINE spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT {
+ auto it = std::find(std::begin(level_string_views), std::end(level_string_views), name);
+ if (it != std::end(level_string_views))
+ return static_cast(std::distance(std::begin(level_string_views), it));
+
+ // check also for "warn" and "err" before giving up..
+ if (name == "warn") {
+ return level::warn;
+ }
+ if (name == "err") {
+ return level::err;
+ }
+ return level::off;
+}
+} // namespace level
+
+SPDLOG_INLINE spdlog_ex::spdlog_ex(std::string msg)
+ : msg_(std::move(msg)) {}
+
+SPDLOG_INLINE spdlog_ex::spdlog_ex(const std::string &msg, int last_errno) {
+#ifdef SPDLOG_USE_STD_FORMAT
+ msg_ = std::system_error(std::error_code(last_errno, std::generic_category()), msg).what();
+#else
+ memory_buf_t outbuf;
+ fmt::format_system_error(outbuf, last_errno, msg.c_str());
+ msg_ = fmt::to_string(outbuf);
+#endif
+}
+
+SPDLOG_INLINE const char *spdlog_ex::what() const SPDLOG_NOEXCEPT { return msg_.c_str(); }
+
+SPDLOG_INLINE void throw_spdlog_ex(const std::string &msg, int last_errno) {
+ SPDLOG_THROW(spdlog_ex(msg, last_errno));
+}
+
+SPDLOG_INLINE void throw_spdlog_ex(std::string msg) { SPDLOG_THROW(spdlog_ex(std::move(msg))); }
+
+} // namespace spdlog
diff --git a/cloudphone/src/main/cpp/libs/spdlog/common.h b/cloudphone/src/main/cpp/libs/spdlog/common.h
new file mode 100644
index 0000000000000000000000000000000000000000..aca483c2077b2c2c828e69b07d1b675a56de3a26
--- /dev/null
+++ b/cloudphone/src/main/cpp/libs/spdlog/common.h
@@ -0,0 +1,411 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef SPDLOG_USE_STD_FORMAT
+ #include
+ #if __cpp_lib_format >= 202207L
+ #include
+ #else
+ #include
+ #endif
+#endif
+
+#ifdef SPDLOG_COMPILED_LIB
+ #undef SPDLOG_HEADER_ONLY
+ #if defined(SPDLOG_SHARED_LIB)
+ #if defined(_WIN32)
+ #ifdef spdlog_EXPORTS
+ #define SPDLOG_API __declspec(dllexport)
+ #else // !spdlog_EXPORTS
+ #define SPDLOG_API __declspec(dllimport)
+ #endif
+ #else // !defined(_WIN32)
+ #define SPDLOG_API __attribute__((visibility("default")))
+ #endif
+ #else // !defined(SPDLOG_SHARED_LIB)
+ #define SPDLOG_API
+ #endif
+ #define SPDLOG_INLINE
+#else // !defined(SPDLOG_COMPILED_LIB)
+ #define SPDLOG_API
+ #define SPDLOG_HEADER_ONLY
+ #define SPDLOG_INLINE inline
+#endif // #ifdef SPDLOG_COMPILED_LIB
+
+#include
+
+#if !defined(SPDLOG_USE_STD_FORMAT) && \
+ FMT_VERSION >= 80000 // backward compatibility with fmt versions older than 8
+ #define SPDLOG_FMT_RUNTIME(format_string) fmt::runtime(format_string)
+ #define SPDLOG_FMT_STRING(format_string) FMT_STRING(format_string)
+ #if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
+ #include
+ #endif
+#else
+ #define SPDLOG_FMT_RUNTIME(format_string) format_string
+ #define SPDLOG_FMT_STRING(format_string) format_string
+#endif
+
+// visual studio up to 2013 does not support noexcept nor constexpr
+#if defined(_MSC_VER) && (_MSC_VER < 1900)
+ #define SPDLOG_NOEXCEPT _NOEXCEPT
+ #define SPDLOG_CONSTEXPR
+#else
+ #define SPDLOG_NOEXCEPT noexcept
+ #define SPDLOG_CONSTEXPR constexpr
+#endif
+
+// If building with std::format, can just use constexpr, otherwise if building with fmt
+// SPDLOG_CONSTEXPR_FUNC needs to be set the same as FMT_CONSTEXPR to avoid situations where
+// a constexpr function in spdlog could end up calling a non-constexpr function in fmt
+// depending on the compiler
+// If fmt determines it can't use constexpr, we should inline the function instead
+#ifdef SPDLOG_USE_STD_FORMAT
+ #define SPDLOG_CONSTEXPR_FUNC constexpr
+#else // Being built with fmt
+ #if FMT_USE_CONSTEXPR
+ #define SPDLOG_CONSTEXPR_FUNC FMT_CONSTEXPR
+ #else
+ #define SPDLOG_CONSTEXPR_FUNC inline
+ #endif
+#endif
+
+#if defined(__GNUC__) || defined(__clang__)
+ #define SPDLOG_DEPRECATED __attribute__((deprecated))
+#elif defined(_MSC_VER)
+ #define SPDLOG_DEPRECATED __declspec(deprecated)
+#else
+ #define SPDLOG_DEPRECATED
+#endif
+
+// disable thread local on msvc 2013
+#ifndef SPDLOG_NO_TLS
+ #if (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt)
+ #define SPDLOG_NO_TLS 1
+ #endif
+#endif
+
+#ifndef SPDLOG_FUNCTION
+ #define SPDLOG_FUNCTION static_cast(__FUNCTION__)
+#endif
+
+#ifdef SPDLOG_NO_EXCEPTIONS
+ #define SPDLOG_TRY
+ #define SPDLOG_THROW(ex) \
+ do { \
+ printf("spdlog fatal error: %s\n", ex.what()); \
+ std::abort(); \
+ } while (0)
+ #define SPDLOG_CATCH_STD
+#else
+ #define SPDLOG_TRY try
+ #define SPDLOG_THROW(ex) throw(ex)
+ #define SPDLOG_CATCH_STD \
+ catch (const std::exception &) { \
+ }
+#endif
+
+namespace spdlog {
+
+class formatter;
+
+namespace sinks {
+class sink;
+}
+
+#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
+using filename_t = std::wstring;
+ // allow macro expansion to occur in SPDLOG_FILENAME_T
+ #define SPDLOG_FILENAME_T_INNER(s) L##s
+ #define SPDLOG_FILENAME_T(s) SPDLOG_FILENAME_T_INNER(s)
+#else
+using filename_t = std::string;
+ #define SPDLOG_FILENAME_T(s) s
+#endif
+
+using log_clock = std::chrono::system_clock;
+using sink_ptr = std::shared_ptr;
+using sinks_init_list = std::initializer_list;
+using err_handler = std::function;
+#ifdef SPDLOG_USE_STD_FORMAT
+namespace fmt_lib = std;
+
+using string_view_t = std::string_view;
+using memory_buf_t = std::string;
+
+template
+ #if __cpp_lib_format >= 202207L
+using format_string_t = std::format_string;
+ #else
+using format_string_t = std::string_view;
+ #endif
+
+template
+struct is_convertible_to_basic_format_string
+ : std::integral_constant>::value> {};
+
+ #if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
+using wstring_view_t = std::wstring_view;
+using wmemory_buf_t = std::wstring;
+
+template
+ #if __cpp_lib_format >= 202207L
+using wformat_string_t = std::wformat_string;
+ #else
+using wformat_string_t = std::wstring_view;
+ #endif
+ #endif
+ #define SPDLOG_BUF_TO_STRING(x) x
+#else // use fmt lib instead of std::format
+namespace fmt_lib = fmt;
+
+using string_view_t = fmt::basic_string_view;
+using memory_buf_t = fmt::basic_memory_buffer;
+
+template
+using format_string_t = fmt::format_string;
+
+template
+using remove_cvref_t = typename std::remove_cv::type>::type;
+
+template
+ #if FMT_VERSION >= 90101
+using fmt_runtime_string = fmt::runtime_format_string;
+ #else
+using fmt_runtime_string = fmt::basic_runtime;
+ #endif
+
+// clang doesn't like SFINAE disabled constructor in std::is_convertible<> so have to repeat the
+// condition from basic_format_string here, in addition, fmt::basic_runtime is only
+// convertible to basic_format_string but not basic_string_view
+template
+struct is_convertible_to_basic_format_string
+ : std::integral_constant>::value ||
+ std::is_same, fmt_runtime_string>::value> {
+};
+
+ #if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
+using wstring_view_t = fmt::basic_string_view;
+using wmemory_buf_t = fmt::basic_memory_buffer;
+
+template
+using wformat_string_t = fmt::wformat_string;
+ #endif
+ #define SPDLOG_BUF_TO_STRING(x) fmt::to_string(x)
+#endif
+
+#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
+ #ifndef _WIN32
+ #error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
+ #endif // _WIN32
+#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
+
+template
+struct is_convertible_to_any_format_string
+ : std::integral_constant::value ||
+ is_convertible_to_basic_format_string::value> {};
+
+#if defined(SPDLOG_NO_ATOMIC_LEVELS)
+using level_t = details::null_atomic_int;
+#else
+using level_t = std::atomic;
+#endif
+
+#define SPDLOG_LEVEL_TRACE 0
+#define SPDLOG_LEVEL_DEBUG 1
+#define SPDLOG_LEVEL_INFO 2
+#define SPDLOG_LEVEL_WARN 3
+#define SPDLOG_LEVEL_ERROR 4
+#define SPDLOG_LEVEL_CRITICAL 5
+#define SPDLOG_LEVEL_OFF 6
+
+#if !defined(SPDLOG_ACTIVE_LEVEL)
+ #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
+#endif
+
+// Log level enum
+namespace level {
+enum level_enum : int {
+ trace = SPDLOG_LEVEL_TRACE,
+ debug = SPDLOG_LEVEL_DEBUG,
+ info = SPDLOG_LEVEL_INFO,
+ warn = SPDLOG_LEVEL_WARN,
+ err = SPDLOG_LEVEL_ERROR,
+ critical = SPDLOG_LEVEL_CRITICAL,
+ off = SPDLOG_LEVEL_OFF,
+ n_levels
+};
+
+#define SPDLOG_LEVEL_NAME_TRACE spdlog::string_view_t("trace", 5)
+#define SPDLOG_LEVEL_NAME_DEBUG spdlog::string_view_t("debug", 5)
+#define SPDLOG_LEVEL_NAME_INFO spdlog::string_view_t("info", 4)
+#define SPDLOG_LEVEL_NAME_WARNING spdlog::string_view_t("warning", 7)
+#define SPDLOG_LEVEL_NAME_ERROR spdlog::string_view_t("error", 5)
+#define SPDLOG_LEVEL_NAME_CRITICAL spdlog::string_view_t("critical", 8)
+#define SPDLOG_LEVEL_NAME_OFF spdlog::string_view_t("off", 3)
+
+#if !defined(SPDLOG_LEVEL_NAMES)
+ #define SPDLOG_LEVEL_NAMES \
+ { \
+ SPDLOG_LEVEL_NAME_TRACE, SPDLOG_LEVEL_NAME_DEBUG, SPDLOG_LEVEL_NAME_INFO, \
+ SPDLOG_LEVEL_NAME_WARNING, SPDLOG_LEVEL_NAME_ERROR, SPDLOG_LEVEL_NAME_CRITICAL, \
+ SPDLOG_LEVEL_NAME_OFF \
+ }
+#endif
+
+#if !defined(SPDLOG_SHORT_LEVEL_NAMES)
+
+ #define SPDLOG_SHORT_LEVEL_NAMES \
+ { "T", "D", "I", "W", "E", "C", "O" }
+#endif
+
+SPDLOG_API const string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
+SPDLOG_API const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
+SPDLOG_API spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT;
+
+} // namespace level
+
+//
+// Color mode used by sinks with color support.
+//
+enum class color_mode { always, automatic, never };
+
+//
+// Pattern time - specific time getting to use for pattern_formatter.
+// local time by default
+//
+enum class pattern_time_type {
+ local, // log localtime
+ utc // log utc
+};
+
+//
+// Log exception
+//
+class SPDLOG_API spdlog_ex : public std::exception {
+public:
+ explicit spdlog_ex(std::string msg);
+ spdlog_ex(const std::string &msg, int last_errno);
+ const char *what() const SPDLOG_NOEXCEPT override;
+
+private:
+ std::string msg_;
+};
+
+[[noreturn]] SPDLOG_API void throw_spdlog_ex(const std::string &msg, int last_errno);
+[[noreturn]] SPDLOG_API void throw_spdlog_ex(std::string msg);
+
+struct source_loc {
+ SPDLOG_CONSTEXPR source_loc() = default;
+ SPDLOG_CONSTEXPR source_loc(const char *filename_in, int line_in, const char *funcname_in)
+ : filename{filename_in},
+ line{line_in},
+ funcname{funcname_in} {}
+
+ SPDLOG_CONSTEXPR bool empty() const SPDLOG_NOEXCEPT { return line <= 0; }
+ const char *filename{nullptr};
+ int line{0};
+ const char *funcname{nullptr};
+};
+
+struct file_event_handlers {
+ file_event_handlers()
+ : before_open(nullptr),
+ after_open(nullptr),
+ before_close(nullptr),
+ after_close(nullptr) {}
+
+ std::function before_open;
+ std::function after_open;
+ std::function before_close;
+ std::function after_close;
+};
+
+namespace details {
+
+// to_string_view
+
+SPDLOG_CONSTEXPR_FUNC spdlog::string_view_t to_string_view(const memory_buf_t &buf)
+ SPDLOG_NOEXCEPT {
+ return spdlog::string_view_t{buf.data(), buf.size()};
+}
+
+SPDLOG_CONSTEXPR_FUNC spdlog::string_view_t to_string_view(spdlog::string_view_t str)
+ SPDLOG_NOEXCEPT {
+ return str;
+}
+
+#if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
+SPDLOG_CONSTEXPR_FUNC spdlog::wstring_view_t to_string_view(const wmemory_buf_t &buf)
+ SPDLOG_NOEXCEPT {
+ return spdlog::wstring_view_t{buf.data(), buf.size()};
+}
+
+SPDLOG_CONSTEXPR_FUNC spdlog::wstring_view_t to_string_view(spdlog::wstring_view_t str)
+ SPDLOG_NOEXCEPT {
+ return str;
+}
+#endif
+
+#ifndef SPDLOG_USE_STD_FORMAT
+template
+inline fmt::basic_string_view to_string_view(fmt::basic_format_string fmt) {
+ return fmt;
+}
+#elif __cpp_lib_format >= 202207L
+template
+SPDLOG_CONSTEXPR_FUNC std::basic_string_view to_string_view(
+ std::basic_format_string fmt) SPDLOG_NOEXCEPT {
+ return fmt.get();
+}
+#endif
+
+// make_unique support for pre c++14
+#if __cplusplus >= 201402L // C++14 and beyond
+using std::enable_if_t;
+using std::make_unique;
+#else
+template
+using enable_if_t = typename std::enable_if::type;
+
+template
+std::unique_ptr make_unique(Args &&...args) {
+ static_assert(!std::is_array::value, "arrays not supported");
+ return std::unique_ptr(new T(std::forward(args)...));
+}
+#endif
+
+// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324)
+template ::value, int> = 0>
+constexpr T conditional_static_cast(U value) {
+ return static_cast(value);
+}
+
+template ::value, int> = 0>
+constexpr T conditional_static_cast(U value) {
+ return value;
+}
+
+} // namespace details
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+ #include "common-inl.h"
+#endif
diff --git a/cloudphone/src/main/cpp/libs/spdlog/details/backtracer-inl.h b/cloudphone/src/main/cpp/libs/spdlog/details/backtracer-inl.h
new file mode 100644
index 0000000000000000000000000000000000000000..43d1002478c8407e3d1ba2bf18fadf76a9241b5e
--- /dev/null
+++ b/cloudphone/src/main/cpp/libs/spdlog/details/backtracer-inl.h
@@ -0,0 +1,63 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+ #include
+#endif
+namespace spdlog {
+namespace details {
+SPDLOG_INLINE backtracer::backtracer(const backtracer &other) {
+ std::lock_guard lock(other.mutex_);
+ enabled_ = other.enabled();
+ messages_ = other.messages_;
+}
+
+SPDLOG_INLINE backtracer::backtracer(backtracer &&other) SPDLOG_NOEXCEPT {
+ std::lock_guard lock(other.mutex_);
+ enabled_ = other.enabled();
+ messages_ = std::move(other.messages_);
+}
+
+SPDLOG_INLINE backtracer &backtracer::operator=(backtracer other) {
+ std::lock_guard lock(mutex_);
+ enabled_ = other.enabled();
+ messages_ = std::move(other.messages_);
+ return *this;
+}
+
+SPDLOG_INLINE void backtracer::enable(size_t size) {
+ std::lock_guard lock{mutex_};
+ enabled_.store(true, std::memory_order_relaxed);
+ messages_ = circular_q{size};
+}
+
+SPDLOG_INLINE void backtracer::disable() {
+ std::lock_guard lock{mutex_};
+ enabled_.store(false, std::memory_order_relaxed);
+}
+
+SPDLOG_INLINE bool backtracer::enabled() const { return enabled_.load(std::memory_order_relaxed); }
+
+SPDLOG_INLINE void backtracer::push_back(const log_msg &msg) {
+ std::lock_guard lock{mutex_};
+ messages_.push_back(log_msg_buffer{msg});
+}
+
+SPDLOG_INLINE bool backtracer::empty() const {
+ std::lock_guard lock{mutex_};
+ return messages_.empty();
+}
+
+// pop all items in the q and apply the given fun on each of them.
+SPDLOG_INLINE void backtracer::foreach_pop(std::function fun) {
+ std::lock_guard lock{mutex_};
+ while (!messages_.empty()) {
+ auto &front_msg = messages_.front();
+ fun(front_msg);
+ messages_.pop_front();
+ }
+}
+} // namespace details
+} // namespace spdlog
diff --git a/cloudphone/src/main/cpp/libs/spdlog/details/backtracer.h b/cloudphone/src/main/cpp/libs/spdlog/details/backtracer.h
new file mode 100644
index 0000000000000000000000000000000000000000..541339cdc32caba890fc0a2a4e8728085099de76
--- /dev/null
+++ b/cloudphone/src/main/cpp/libs/spdlog/details/backtracer.h
@@ -0,0 +1,45 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include
+#include
+
+#include
+#include
+#include
+
+// Store log messages in circular buffer.
+// Useful for storing debug data in case of error/warning happens.
+
+namespace spdlog {
+namespace details {
+class SPDLOG_API backtracer {
+ mutable std::mutex mutex_;
+ std::atomic enabled_{false};
+ circular_q messages_;
+
+public:
+ backtracer() = default;
+ backtracer(const backtracer &other);
+
+ backtracer(backtracer &&other) SPDLOG_NOEXCEPT;
+ backtracer &operator=(backtracer other);
+
+ void enable(size_t size);
+ void disable();
+ bool enabled() const;
+ void push_back(const log_msg &msg);
+ bool empty() const;
+
+ // pop all items in the q and apply the given fun on each of them.
+ void foreach_pop(std::function fun);
+};
+
+} // namespace details
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+ #include "backtracer-inl.h"
+#endif
diff --git a/cloudphone/src/main/cpp/libs/spdlog/details/circular_q.h b/cloudphone/src/main/cpp/libs/spdlog/details/circular_q.h
new file mode 100644
index 0000000000000000000000000000000000000000..29e9d255f4f4c5ffba08f3ccb234688698035be7
--- /dev/null
+++ b/cloudphone/src/main/cpp/libs/spdlog/details/circular_q.h
@@ -0,0 +1,115 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+// circular q view of std::vector.
+#pragma once
+
+#include
+#include
+
+#include "spdlog/common.h"
+
+namespace spdlog {
+namespace details {
+template
+class circular_q {
+ size_t max_items_ = 0;
+ typename std::vector::size_type head_ = 0;
+ typename std::vector::size_type tail_ = 0;
+ size_t overrun_counter_ = 0;
+ std::vector v_;
+
+public:
+ using value_type = T;
+
+ // empty ctor - create a disabled queue with no elements allocated at all
+ circular_q() = default;
+
+ explicit circular_q(size_t max_items)
+ : max_items_(max_items + 1) // one item is reserved as marker for full q
+ ,
+ v_(max_items_) {}
+
+ circular_q(const circular_q &) = default;
+ circular_q &operator=(const circular_q &) = default;
+
+ // move cannot be default,
+ // since we need to reset head_, tail_, etc to zero in the moved object
+ circular_q(circular_q &&other) SPDLOG_NOEXCEPT { copy_moveable(std::move(other)); }
+
+ circular_q &operator=(circular_q &&other) SPDLOG_NOEXCEPT {
+ copy_moveable(std::move(other));
+ return *this;
+ }
+
+ // push back, overrun (oldest) item if no room left
+ void push_back(T &&item) {
+ if (max_items_ > 0) {
+ v_[tail_] = std::move(item);
+ tail_ = (tail_ + 1) % max_items_;
+
+ if (tail_ == head_) // overrun last item if full
+ {
+ head_ = (head_ + 1) % max_items_;
+ ++overrun_counter_;
+ }
+ }
+ }
+
+ // Return reference to the front item.
+ // If there are no elements in the container, the behavior is undefined.
+ const T &front() const { return v_[head_]; }
+
+ T &front() { return v_[head_]; }
+
+ // Return number of elements actually stored
+ size_t size() const {
+ if (tail_ >= head_) {
+ return tail_ - head_;
+ } else {
+ return max_items_ - (head_ - tail_);
+ }
+ }
+
+ // Return const reference to item by index.
+ // If index is out of range 0…size()-1, the behavior is undefined.
+ const T &at(size_t i) const {
+ assert(i < size());
+ return v_[(head_ + i) % max_items_];
+ }
+
+ // Pop item from front.
+ // If there are no elements in the container, the behavior is undefined.
+ void pop_front() { head_ = (head_ + 1) % max_items_; }
+
+ bool empty() const { return tail_ == head_; }
+
+ bool full() const {
+ // head is ahead of the tail by 1
+ if (max_items_ > 0) {
+ return ((tail_ + 1) % max_items_) == head_;
+ }
+ return false;
+ }
+
+ size_t overrun_counter() const { return overrun_counter_; }
+
+ void reset_overrun_counter() { overrun_counter_ = 0; }
+
+private:
+ // copy from other&& and reset it to disabled state
+ void copy_moveable(circular_q &&other) SPDLOG_NOEXCEPT {
+ max_items_ = other.max_items_;
+ head_ = other.head_;
+ tail_ = other.tail_;
+ overrun_counter_ = other.overrun_counter_;
+ v_ = std::move(other.v_);
+
+ // put &&other in disabled, but valid state
+ other.max_items_ = 0;
+ other.head_ = other.tail_ = 0;
+ other.overrun_counter_ = 0;
+ }
+};
+} // namespace details
+} // namespace spdlog
diff --git a/cloudphone/src/main/cpp/libs/spdlog/details/console_globals.h b/cloudphone/src/main/cpp/libs/spdlog/details/console_globals.h
new file mode 100644
index 0000000000000000000000000000000000000000..9c552106a4e3c3cdfab1cddb857969c126f5a024
--- /dev/null
+++ b/cloudphone/src/main/cpp/libs/spdlog/details/console_globals.h
@@ -0,0 +1,28 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include
+#include
+
+namespace spdlog {
+namespace details {
+
+struct console_mutex {
+ using mutex_t = std::mutex;
+ static mutex_t &mutex() {
+ static mutex_t s_mutex;
+ return s_mutex;
+ }
+};
+
+struct console_nullmutex {
+ using mutex_t = null_mutex;
+ static mutex_t &mutex() {
+ static mutex_t s_mutex;
+ return s_mutex;
+ }
+};
+} // namespace details
+} // namespace spdlog
diff --git a/cloudphone/src/main/cpp/libs/spdlog/details/file_helper-inl.h b/cloudphone/src/main/cpp/libs/spdlog/details/file_helper-inl.h
new file mode 100644
index 0000000000000000000000000000000000000000..37d1d46fd173821afb03ff4e18b29b2ba2820e13
--- /dev/null
+++ b/cloudphone/src/main/cpp/libs/spdlog/details/file_helper-inl.h
@@ -0,0 +1,152 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+ #include
+#endif
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace spdlog {
+namespace details {
+
+SPDLOG_INLINE file_helper::file_helper(const file_event_handlers &event_handlers)
+ : event_handlers_(event_handlers) {}
+
+SPDLOG_INLINE file_helper::~file_helper() { close(); }
+
+SPDLOG_INLINE void file_helper::open(const filename_t &fname, bool truncate) {
+ close();
+ filename_ = fname;
+
+ auto *mode = SPDLOG_FILENAME_T("ab");
+ auto *trunc_mode = SPDLOG_FILENAME_T("wb");
+
+ if (event_handlers_.before_open) {
+ event_handlers_.before_open(filename_);
+ }
+ for (int tries = 0; tries < open_tries_; ++tries) {
+ // create containing folder if not exists already.
+ os::create_dir(os::dir_name(fname));
+ if (truncate) {
+ // Truncate by opening-and-closing a tmp file in "wb" mode, always
+ // opening the actual log-we-write-to in "ab" mode, since that
+ // interacts more politely with eternal processes that might
+ // rotate/truncate the file underneath us.
+ std::FILE *tmp;
+ if (os::fopen_s(&tmp, fname, trunc_mode)) {
+ continue;
+ }
+ std::fclose(tmp);
+ }
+ if (!os::fopen_s(&fd_, fname, mode)) {
+ if (event_handlers_.after_open) {
+ event_handlers_.after_open(filename_, fd_);
+ }
+ return;
+ }
+
+ details::os::sleep_for_millis(open_interval_);
+ }
+
+ throw_spdlog_ex("Failed opening file " + os::filename_to_str(filename_) + " for writing",
+ errno);
+}
+
+SPDLOG_INLINE void file_helper::reopen(bool truncate) {
+ if (filename_.empty()) {
+ throw_spdlog_ex("Failed re opening file - was not opened before");
+ }
+ this->open(filename_, truncate);
+}
+
+SPDLOG_INLINE void file_helper::flush() {
+ if (std::fflush(fd_) != 0) {
+ throw_spdlog_ex("Failed flush to file " + os::filename_to_str(filename_), errno);
+ }
+}
+
+SPDLOG_INLINE void file_helper::sync() {
+ if (!os::fsync(fd_)) {
+ throw_spdlog_ex("Failed to fsync file " + os::filename_to_str(filename_), errno);
+ }
+}
+
+SPDLOG_INLINE void file_helper::close() {
+ if (fd_ != nullptr) {
+ if (event_handlers_.before_close) {
+ event_handlers_.before_close(filename_, fd_);
+ }
+
+ std::fclose(fd_);
+ fd_ = nullptr;
+
+ if (event_handlers_.after_close) {
+ event_handlers_.after_close(filename_);
+ }
+ }
+}
+
+SPDLOG_INLINE void file_helper::write(const memory_buf_t &buf) {
+ if (fd_ == nullptr) return;
+ size_t msg_size = buf.size();
+ auto data = buf.data();
+ if (std::fwrite(data, 1, msg_size, fd_) != msg_size) {
+ throw_spdlog_ex("Failed writing to file " + os::filename_to_str(filename_), errno);
+ }
+}
+
+SPDLOG_INLINE size_t file_helper::size() const {
+ if (fd_ == nullptr) {
+ throw_spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(filename_));
+ }
+ return os::filesize(fd_);
+}
+
+SPDLOG_INLINE const filename_t &file_helper::filename() const { return filename_; }
+
+//
+// return file path and its extension:
+//
+// "mylog.txt" => ("mylog", ".txt")
+// "mylog" => ("mylog", "")
+// "mylog." => ("mylog.", "")
+// "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
+//
+// the starting dot in filenames is ignored (hidden files):
+//
+// ".mylog" => (".mylog". "")
+// "my_folder/.mylog" => ("my_folder/.mylog", "")
+// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
+SPDLOG_INLINE std::tuple file_helper::split_by_extension(
+ const filename_t &fname) {
+ auto ext_index = fname.rfind('.');
+
+ // no valid extension found - return whole path and empty string as
+ // extension
+ if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1) {
+ return std::make_tuple(fname, filename_t());
+ }
+
+ // treat cases like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
+ auto folder_index = fname.find_last_of(details::os::folder_seps_filename);
+ if (folder_index != filename_t::npos && folder_index >= ext_index - 1) {
+ return std::make_tuple(fname, filename_t());
+ }
+
+ // finally - return a valid base and extension tuple
+ return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index));
+}
+
+} // namespace details
+} // namespace spdlog
diff --git a/cloudphone/src/main/cpp/libs/spdlog/details/file_helper.h b/cloudphone/src/main/cpp/libs/spdlog/details/file_helper.h
new file mode 100644
index 0000000000000000000000000000000000000000..f0e5d180e331a4e0477469100495a1ea0ffa4e87
--- /dev/null
+++ b/cloudphone/src/main/cpp/libs/spdlog/details/file_helper.h
@@ -0,0 +1,61 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include
+#include
+
+namespace spdlog {
+namespace details {
+
+// Helper class for file sinks.
+// When failing to open a file, retry several times(5) with a delay interval(10 ms).
+// Throw spdlog_ex exception on errors.
+
+class SPDLOG_API file_helper {
+public:
+ file_helper() = default;
+ explicit file_helper(const file_event_handlers &event_handlers);
+
+ file_helper(const file_helper &) = delete;
+ file_helper &operator=(const file_helper &) = delete;
+ ~file_helper();
+
+ void open(const filename_t &fname, bool truncate = false);
+ void reopen(bool truncate);
+ void flush();
+ void sync();
+ void close();
+ void write(const memory_buf_t &buf);
+ size_t size() const;
+ const filename_t &filename() const;
+
+ //
+ // return file path and its extension:
+ //
+ // "mylog.txt" => ("mylog", ".txt")
+ // "mylog" => ("mylog", "")
+ // "mylog." => ("mylog.", "")
+ // "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
+ //
+ // the starting dot in filenames is ignored (hidden files):
+ //
+ // ".mylog" => (".mylog". "")
+ // "my_folder/.mylog" => ("my_folder/.mylog", "")
+ // "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
+ static std::tuple split_by_extension(const filename_t &fname);
+
+private:
+ const int open_tries_ = 5;
+ const unsigned int open_interval_ = 10;
+ std::FILE *fd_{nullptr};
+ filename_t filename_;
+ file_event_handlers event_handlers_;
+};
+} // namespace details
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+ #include "file_helper-inl.h"
+#endif
diff --git a/cloudphone/src/main/cpp/libs/spdlog/details/fmt_helper.h b/cloudphone/src/main/cpp/libs/spdlog/details/fmt_helper.h
new file mode 100644
index 0000000000000000000000000000000000000000..61306003b2107e9e61e7aaaf212afc64e7ede39f
--- /dev/null
+++ b/cloudphone/src/main/cpp/libs/spdlog/details/fmt_helper.h
@@ -0,0 +1,141 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+#ifdef SPDLOG_USE_STD_FORMAT
+ #include
+ #include
+#endif
+
+// Some fmt helpers to efficiently format and pad ints and strings
+namespace spdlog {
+namespace details {
+namespace fmt_helper {
+
+inline void append_string_view(spdlog::string_view_t view, memory_buf_t &dest) {
+ auto *buf_ptr = view.data();
+ dest.append(buf_ptr, buf_ptr + view.size());
+}
+
+#ifdef SPDLOG_USE_STD_FORMAT
+template
+inline void append_int(T n, memory_buf_t &dest) {
+ // Buffer should be large enough to hold all digits (digits10 + 1) and a sign
+ SPDLOG_CONSTEXPR const auto BUF_SIZE = std::numeric_limits::digits10 + 2;
+ char buf[BUF_SIZE];
+
+ auto [ptr, ec] = std::to_chars(buf, buf + BUF_SIZE, n, 10);
+ if (ec == std::errc()) {
+ dest.append(buf, ptr);
+ } else {
+ throw_spdlog_ex("Failed to format int", static_cast(ec));
+ }
+}
+#else
+template
+inline void append_int(T n, memory_buf_t &dest) {
+ fmt::format_int i(n);
+ dest.append(i.data(), i.data() + i.size());
+}
+#endif
+
+template
+SPDLOG_CONSTEXPR_FUNC unsigned int count_digits_fallback(T n) {
+ // taken from fmt: https://github.com/fmtlib/fmt/blob/8.0.1/include/fmt/format.h#L899-L912
+ unsigned int count = 1;
+ for (;;) {
+ // Integer division is slow so do it for a group of four digits instead
+ // of for every digit. The idea comes from the talk by Alexandrescu
+ // "Three Optimization Tips for C++". See speed-test for a comparison.
+ if (n < 10) return count;
+ if (n < 100) return count + 1;
+ if (n < 1000) return count + 2;
+ if (n < 10000) return count + 3;
+ n /= 10000u;
+ count += 4;
+ }
+}
+
+template
+inline unsigned int count_digits(T n) {
+ using count_type =
+ typename std::conditional<(sizeof(T) > sizeof(uint32_t)), uint64_t, uint32_t>::type;
+#ifdef SPDLOG_USE_STD_FORMAT
+ return count_digits_fallback(static_cast(n));
+#else
+ return static_cast(fmt::
+ // fmt 7.0.0 renamed the internal namespace to detail.
+ // See: https://github.com/fmtlib/fmt/issues/1538
+ #if FMT_VERSION < 70000
+ internal
+ #else
+ detail
+ #endif
+ ::count_digits(static_cast(n)));
+#endif
+}
+
+inline void pad2(int n, memory_buf_t &dest) {
+ if (n >= 0 && n < 100) // 0-99
+ {
+ dest.push_back(static_cast('0' + n / 10));
+ dest.push_back(static_cast('0' + n % 10));
+ } else // unlikely, but just in case, let fmt deal with it
+ {
+ fmt_lib::format_to(std::back_inserter(dest), SPDLOG_FMT_STRING("{:02}"), n);
+ }
+}
+
+template
+inline void pad_uint(T n, unsigned int width, memory_buf_t &dest) {
+ static_assert(std::is_unsigned::value, "pad_uint must get unsigned T");
+ for (auto digits = count_digits(n); digits < width; digits++) {
+ dest.push_back('0');
+ }
+ append_int(n, dest);
+}
+
+template
+inline void pad3(T n, memory_buf_t &dest) {
+ static_assert(std::is_unsigned::value, "pad3 must get unsigned T");
+ if (n < 1000) {
+ dest.push_back(static_cast(n / 100 + '0'));
+ n = n % 100;
+ dest.push_back(static_cast((n / 10) + '0'));
+ dest.push_back(static_cast((n % 10) + '0'));
+ } else {
+ append_int(n, dest);
+ }
+}
+
+template
+inline void pad6(T n, memory_buf_t &dest) {
+ pad_uint(n, 6, dest);
+}
+
+template
+inline void pad9(T n, memory_buf_t &dest) {
+ pad_uint(n, 9, dest);
+}
+
+// return fraction of a second of the given time_point.
+// e.g.
+// fraction(tp) -> will return the millis part of the second
+template
+inline ToDuration time_fraction(log_clock::time_point tp) {
+ using std::chrono::duration_cast;
+ using std::chrono::seconds;
+ auto duration = tp.time_since_epoch();
+ auto secs = duration_cast(duration);
+ return duration_cast(duration) - duration_cast(secs);
+}
+
+} // namespace fmt_helper
+} // namespace details
+} // namespace spdlog
diff --git a/cloudphone/src/main/cpp/libs/spdlog/details/log_msg-inl.h b/cloudphone/src/main/cpp/libs/spdlog/details/log_msg-inl.h
new file mode 100644
index 0000000000000000000000000000000000000000..aa3a957682e5dfecd132a3e2e7667b239bfb06ed
--- /dev/null
+++ b/cloudphone/src/main/cpp/libs/spdlog/details/log_msg-inl.h
@@ -0,0 +1,44 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+ #include
+#endif
+
+#include
+
+namespace spdlog {
+namespace details {
+
+SPDLOG_INLINE log_msg::log_msg(spdlog::log_clock::time_point log_time,
+ spdlog::source_loc loc,
+ string_view_t a_logger_name,
+ spdlog::level::level_enum lvl,
+ spdlog::string_view_t msg)
+ : logger_name(a_logger_name),
+ level(lvl),
+ time(log_time)
+#ifndef SPDLOG_NO_THREAD_ID
+ ,
+ thread_id(os::thread_id())
+#endif
+ ,
+ source(loc),
+ payload(msg) {
+}
+
+SPDLOG_INLINE log_msg::log_msg(spdlog::source_loc loc,
+ string_view_t a_logger_name,
+ spdlog::level::level_enum lvl,
+ spdlog::string_view_t msg)
+ : log_msg(os::now(), loc, a_logger_name, lvl, msg) {}
+
+SPDLOG_INLINE log_msg::log_msg(string_view_t a_logger_name,
+ spdlog::level::level_enum lvl,
+ spdlog::string_view_t msg)
+ : log_msg(os::now(), source_loc{}, a_logger_name, lvl, msg) {}
+
+} // namespace details
+} // namespace spdlog
diff --git a/cloudphone/src/main/cpp/libs/spdlog/details/log_msg.h b/cloudphone/src/main/cpp/libs/spdlog/details/log_msg.h
new file mode 100644
index 0000000000000000000000000000000000000000..87df1e833277a74eac4729fd4c4d2ce9ebf75ab9
--- /dev/null
+++ b/cloudphone/src/main/cpp/libs/spdlog/details/log_msg.h
@@ -0,0 +1,40 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include
+#include
+
+namespace spdlog {
+namespace details {
+struct SPDLOG_API log_msg {
+ log_msg() = default;
+ log_msg(log_clock::time_point log_time,
+ source_loc loc,
+ string_view_t logger_name,
+ level::level_enum lvl,
+ string_view_t msg);
+ log_msg(source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg);
+ log_msg(string_view_t logger_name, level::level_enum lvl, string_view_t msg);
+ log_msg(const log_msg &other) = default;
+ log_msg &operator=(const log_msg &other) = default;
+
+ string_view_t logger_name;
+ level::level_enum level{level::off};
+ log_clock::time_point time;
+ size_t thread_id{0};
+
+ // wrapping the formatted text with color (updated by pattern_formatter).
+ mutable size_t color_range_start{0};
+ mutable size_t color_range_end{0};
+
+ source_loc source;
+ string_view_t payload;
+};
+} // namespace details
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+ #include "log_msg-inl.h"
+#endif
diff --git a/cloudphone/src/main/cpp/libs/spdlog/details/log_msg_buffer-inl.h b/cloudphone/src/main/cpp/libs/spdlog/details/log_msg_buffer-inl.h
new file mode 100644
index 0000000000000000000000000000000000000000..2eb242859ea2ca76ba8239b6c410bce42726e7f2
--- /dev/null
+++ b/cloudphone/src/main/cpp/libs/spdlog/details/log_msg_buffer-inl.h
@@ -0,0 +1,54 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#ifndef SPDLOG_HEADER_ONLY
+ #include
+#endif
+
+namespace spdlog {
+namespace details {
+
+SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg &orig_msg)
+ : log_msg{orig_msg} {
+ buffer.append(logger_name.begin(), logger_name.end());
+ buffer.append(payload.begin(), payload.end());
+ update_string_views();
+}
+
+SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg_buffer &other)
+ : log_msg{other} {
+ buffer.append(logger_name.begin(), logger_name.end());
+ buffer.append(payload.begin(), payload.end());
+ update_string_views();
+}
+
+SPDLOG_INLINE log_msg_buffer::log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT
+ : log_msg{other},
+ buffer{std::move(other.buffer)} {
+ update_string_views();
+}
+
+SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(const log_msg_buffer &other) {
+ log_msg::operator=(other);
+ buffer.clear();
+ buffer.append(other.buffer.data(), other.buffer.data() + other.buffer.size());
+ update_string_views();
+ return *this;
+}
+
+SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT {
+ log_msg::operator=(other);
+ buffer = std::move(other.buffer);
+ update_string_views();
+ return *this;
+}
+
+SPDLOG_INLINE void log_msg_buffer::update_string_views() {
+ logger_name = string_view_t{buffer.data(), logger_name.size()};
+ payload = string_view_t{buffer.data() + logger_name.size(), payload.size()};
+}
+
+} // namespace details
+} // namespace spdlog
diff --git a/cloudphone/src/main/cpp/libs/spdlog/details/log_msg_buffer.h b/cloudphone/src/main/cpp/libs/spdlog/details/log_msg_buffer.h
new file mode 100644
index 0000000000000000000000000000000000000000..1143b3ba45062f38721923323a8d321360d7fbad
--- /dev/null
+++ b/cloudphone/src/main/cpp/libs/spdlog/details/log_msg_buffer.h
@@ -0,0 +1,32 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+#include
+
+namespace spdlog {
+namespace details {
+
+// Extend log_msg with internal buffer to store its payload.
+// This is needed since log_msg holds string_views that points to stack data.
+
+class SPDLOG_API log_msg_buffer : public log_msg {
+ memory_buf_t buffer;
+ void update_string_views();
+
+public:
+ log_msg_buffer() = default;
+ explicit log_msg_buffer(const log_msg &orig_msg);
+ log_msg_buffer(const log_msg_buffer &other);
+ log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT;
+ log_msg_buffer &operator=(const log_msg_buffer &other);
+ log_msg_buffer &operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT;
+};
+
+} // namespace details
+} // namespace spdlog
+
+#ifdef SPDLOG_HEADER_ONLY
+ #include "log_msg_buffer-inl.h"
+#endif
diff --git a/cloudphone/src/main/cpp/libs/spdlog/details/mpmc_blocking_q.h b/cloudphone/src/main/cpp/libs/spdlog/details/mpmc_blocking_q.h
new file mode 100644
index 0000000000000000000000000000000000000000..5a474bf33a4073a5554ba2a0de517f42ddc54c45
--- /dev/null
+++ b/cloudphone/src/main/cpp/libs/spdlog/details/mpmc_blocking_q.h
@@ -0,0 +1,177 @@
+// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+#pragma once
+
+// multi producer-multi consumer blocking queue.
+// enqueue(..) - will block until room found to put the new message.
+// enqueue_nowait(..) - will return immediately with false if no room left in
+// the queue.
+// dequeue_for(..) - will block until the queue is not empty or timeout have
+// passed.
+
+#include
+
+#include
+#include
+#include
+
+namespace spdlog {
+namespace details {
+
+template
+class mpmc_blocking_queue {
+public:
+ using item_type = T;
+ explicit mpmc_blocking_queue(size_t max_items)
+ : q_(max_items) {}
+
+#ifndef __MINGW32__
+ // try to enqueue and block if no room left
+ void enqueue(T &&item) {
+ {
+ std::unique_lock lock(queue_mutex_);
+ pop_cv_.wait(lock, [this] { return !this->q_.full(); });
+ q_.push_back(std::move(item));
+ }
+ push_cv_.notify_one();
+ }
+
+ // enqueue immediately. overrun oldest message in the queue if no room left.
+ void enqueue_nowait(T &&item) {
+ {
+ std::unique_lock lock(queue_mutex_);
+ q_.push_back(std::move(item));
+ }
+ push_cv_.notify_one();
+ }
+
+ void enqueue_if_have_room(T &&item) {
+ bool pushed = false;
+ {
+ std::unique_lock