1/* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "VirtualScreenImpl.h" 17 18#include <cinttypes> 19#define boolean jpegboolean 20#include "jpeglib.h" 21#undef boolean 22 23#include "CommandLineInterface.h" 24#include "CommandParser.h" 25#include "PreviewerEngineLog.h" 26#include "TraceTool.h" 27#include <sstream> 28 29VirtualScreenImpl& VirtualScreenImpl::GetInstance() 30{ 31 static VirtualScreenImpl virtualScreen; 32 return virtualScreen; 33} 34 35void VirtualScreenImpl::SendBufferOnTimer() 36{ 37 GetInstance().SetLoadDocFlag(VirtualScreen::LoadDocType::NORMAL); 38 if (GetInstance().loadDocTempBuffer == nullptr) { 39 PrintLoadDocFinishedLog("onRender timeout,no buffer to send"); 40 return; 41 } 42 VirtualScreen::isStartCount = true; 43 { 44 std::lock_guard<std::mutex> guard(WebSocketServer::GetInstance().mutex); 45 if (GetInstance().loadDocCopyBuffer != nullptr) { 46 delete [] GetInstance().loadDocCopyBuffer; 47 GetInstance().loadDocCopyBuffer = nullptr; 48 } 49 GetInstance().loadDocCopyBuffer = new(std::nothrow) uint8_t[GetInstance().lengthTemp]; 50 if (!GetInstance().loadDocCopyBuffer) { 51 ELOG("Memory allocation failed : loadDocCopyBuffer."); 52 return; 53 } 54 std::copy(GetInstance().loadDocTempBuffer, 55 GetInstance().loadDocTempBuffer + GetInstance().lengthTemp, 56 GetInstance().loadDocCopyBuffer); 57 } 58 VirtualScreenImpl::GetInstance().protocolVersion = 59 static_cast<uint16_t>(VirtualScreen::ProtocolVersion::LOADDOC); 60 GetInstance().bufferSize = GetInstance().lengthTemp + GetInstance().headSize; 61 GetInstance().wholeBuffer = new(std::nothrow) uint8_t[LWS_PRE + GetInstance().bufferSize]; 62 if (!GetInstance().wholeBuffer) { 63 ELOG("Memory allocation failed : wholeBuffer."); 64 return; 65 } 66 GetInstance().screenBuffer = GetInstance().wholeBuffer + LWS_PRE; 67 GetInstance().SendPixmap(GetInstance().loadDocCopyBuffer, GetInstance().lengthTemp, 68 GetInstance().widthTemp, GetInstance().heightTemp); 69} 70 71void VirtualScreenImpl::PrintLoadDocFinishedLog(const std::string& logStr) 72{ 73 ILOG("loadDoc: LoadDocFlag2:finished %s, onRenderTime:%" PRIu64 ", flushEmptyTime:%" PRIu64 ", \ 74 onRenderTimeStamp:%" PRIu64 " flushEmptyTimeStamp:%" PRIu64 "", logStr.c_str(), 75 GetInstance().onRenderTime, GetInstance().flushEmptyTime, 76 GetInstance().timeStampTemp, GetInstance().flushEmptyTimeStamp); 77} 78 79bool VirtualScreenImpl::FlushEmptyFunc(std::chrono::system_clock::time_point endTime, int64_t timePassed) 80{ 81 if (GetInstance().onRenderTime > GetInstance().flushEmptyTime) { 82 if (GetInstance().timeStampTemp < GetInstance().flushEmptyTimeStamp) { 83 SendBufferOnTimer(); // 有收到结束标记,且flushEmpty后有继续出图 84 PrintLoadDocFinishedLog("flushEmpty normal, onRender normal"); 85 return true; 86 } 87 } else { 88 // flushEmpty后没有继续出图,计时100ms,如果仍没有onRender,发送上一次的的onRender buffer 89 int64_t timePassed2 = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - 90 GetInstance().flushEmptyTime).count(); 91 if (timePassed2 > TIMEOUT_ONRENDER_DURATION_MS) { 92 if (GetInstance().timeStampTemp < GetInstance().flushEmptyTimeStamp) { 93 SendBufferOnTimer(); 94 PrintLoadDocFinishedLog("flushEmpty normal, onRender timeout"); 95 return true; 96 } 97 } 98 } 99 if (timePassed >= TIMEOUT_NINE_S) { // 有结束点,无出图 100 PrintLoadDocFinishedLog("flushEmpty normal, onRender timeout"); 101 return true; // 有收到结束标记,且超过最大时限还没有出图则结束 102 } 103 return false; 104} 105 106bool VirtualScreenImpl::NoFlushEmptyFunc(int64_t timePassed) 107{ 108 if (timePassed >= SEND_IMG_DURATION_MS && 109 GetInstance().onRenderTime != std::chrono::system_clock::time_point::min()) { 110 SendBufferOnTimer(); // 没有收到结束标记,300ms内有出图选取最后一张图 111 PrintLoadDocFinishedLog("async load, flushEmpty timeout, onRender normal"); 112 return true; 113 } 114 if (timePassed >= TIMEOUT_NINE_S) { 115 SendBufferOnTimer(); 116 PrintLoadDocFinishedLog("flushEmpty timeout, onRender unknown"); 117 return true; // 没有收到结束标记,且超过最大时限还没有出图则结束 118 } 119 return false; 120} 121 122 123void VirtualScreenImpl::StartTimer() 124{ 125 while (true) { 126 auto endTime = std::chrono::system_clock::now(); 127 int64_t timePassed = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - 128 VirtualScreenImpl::GetInstance().startTime).count(); 129 bool ret = false; 130 if (GetInstance().isFlushEmpty) { 131 ret = FlushEmptyFunc(endTime, timePassed); 132 } else { 133 ret = NoFlushEmptyFunc(timePassed); 134 } 135 if (ret) { 136 return; 137 } 138 } 139} 140 141bool VirtualScreenImpl::LoadDocCallback(const void* data, const size_t length, const int32_t width, 142 const int32_t height, const uint64_t timeStamp) 143{ 144 if (timeStamp < GetInstance().loadDocTimeStamp) { 145 return false; 146 } 147 if (GetInstance().GetLoadDocFlag() == VirtualScreen::LoadDocType::FINISHED) { 148 { 149 std::lock_guard<std::mutex> guard(WebSocketServer::GetInstance().mutex); 150 if (GetInstance().loadDocTempBuffer != nullptr) { 151 delete [] GetInstance().loadDocTempBuffer; 152 GetInstance().loadDocTempBuffer = nullptr; 153 } 154 GetInstance().lengthTemp = length; 155 GetInstance().widthTemp = width; 156 GetInstance().heightTemp = height; 157 GetInstance().timeStampTemp = timeStamp; 158 if (length <= 0) { 159 return false; 160 } 161 GetInstance().loadDocTempBuffer = new(std::nothrow) uint8_t[length]; 162 if (!GetInstance().loadDocTempBuffer) { 163 ELOG("Memory allocation failed : loadDocTempBuffer."); 164 return false; 165 } 166 uint8_t* dataPtr = reinterpret_cast<uint8_t*>(const_cast<void*>(data)); 167 std::copy(dataPtr, dataPtr + length, GetInstance().loadDocTempBuffer); 168 GetInstance().onRenderTime = std::chrono::system_clock::now(); 169 } 170 if (VirtualScreen::isStartCount) { 171 VirtualScreen::isStartCount = false; 172 VirtualScreen::startTime = std::chrono::system_clock::now(); 173 std::thread timerThread(std::ref(VirtualScreenImpl::StartTimer)); 174 timerThread.detach(); 175 } 176 return false; 177 } 178 return true; 179} 180 181bool VirtualScreenImpl::Callback(const void* data, const size_t length, 182 const int32_t width, const int32_t height, const uint64_t timeStamp) 183{ 184 if (VirtualScreenImpl::GetInstance().StopSendStaticCardImage(STOP_SEND_CARD_DURATION_MS)) { 185 return false; // 静态卡片 186 } 187 if (VirtualScreenImpl::GetInstance().GetLoadDocFlag() < VirtualScreen::LoadDocType::FINISHED) { 188 return false; 189 } 190 if (VirtualScreenImpl::GetInstance().JudgeAndDropFrame()) { 191 return false; // 丢帧* 192 } 193 bool staticRet = VirtualScreen::JudgeStaticImage(SEND_IMG_DURATION_MS); 194 if (!staticRet) { 195 return false; // 平行世界 196 } 197 if (!LoadDocCallback(data, length, width, height, timeStamp)) { 198 return false; // 组件预览 199 } 200 201 GetInstance().bufferSize = length + GetInstance().headSize; 202 GetInstance().wholeBuffer = new(std::nothrow) uint8_t[LWS_PRE + GetInstance().bufferSize]; 203 if (!GetInstance().wholeBuffer) { 204 ELOG("Memory allocation failed : wholeBuffer."); 205 return false; 206 } 207 GetInstance().screenBuffer = GetInstance().wholeBuffer + LWS_PRE; 208 209 return GetInstance().SendPixmap(data, length, width, height); 210} 211 212bool VirtualScreenImpl::FlushEmptyCallback(const uint64_t timeStamp) 213{ 214 if (timeStamp < GetInstance().loadDocTimeStamp) { 215 return false; 216 } 217 ILOG("loadDoc: flushEmptyTimeStamp:%" PRIu64 ", loadDocTimeStamp:%" PRIu64 "", 218 timeStamp, GetInstance().loadDocTimeStamp); 219 GetInstance().isFlushEmpty = true; 220 GetInstance().flushEmptyTime = std::chrono::system_clock::now(); 221 GetInstance().flushEmptyTimeStamp = timeStamp; 222 return true; 223} 224 225void VirtualScreenImpl::InitFlushEmptyTime() 226{ 227 GetInstance().isFlushEmpty = false; 228 GetInstance().flushEmptyTime = std::chrono::system_clock::time_point::min(); 229 GetInstance().onRenderTime = std::chrono::system_clock::time_point::min(); 230 GetInstance().flushEmptyTimeStamp = 0; 231 struct timespec ts; 232 clock_gettime(CLOCK_MONOTONIC, &ts); 233 loadDocTimeStamp = ts.tv_sec * SEC_TO_NANOSEC + ts.tv_nsec; 234 ILOG("loadDoc: loadDocTimeStamp:%" PRIu64 "", loadDocTimeStamp); 235} 236 237bool VirtualScreenImpl::PageCallback(const std::string currentRouterPath) 238{ 239 std::string currentRouter = currentRouterPath.substr(0, currentRouterPath.size() - 3); 240 ILOG("PageCallback currentPage is : %s", currentRouter.c_str()); 241 GetInstance().SetCurrentRouter(currentRouter); 242 Json2::Value val; 243 CommandLineInterface::GetInstance().CreatCommandToSendData("CurrentRouter", val, "get"); 244 return true; 245} 246 247bool VirtualScreenImpl::LoadContentCallback(const std::string currentRouterPath) 248{ 249 ILOG("LoadContentCallback currentPage is : %s", currentRouterPath.c_str()); 250 GetInstance().SetAbilityCurrentRouter(currentRouterPath); 251 Json2::Value val; 252 CommandLineInterface::GetInstance().CreatCommandToSendData("LoadContent", val, "get"); 253 return true; 254} 255 256void VirtualScreenImpl::FastPreviewCallback(const std::string& jsonStr) 257{ 258 GetInstance().SetFastPreviewMsg(jsonStr); 259 Json2::Value val; 260 CommandLineInterface::GetInstance().CreatCommandToSendData("FastPreviewMsg", val, "get"); 261} 262 263void VirtualScreenImpl::InitAll(std::string pipeName, std::string pipePort) 264{ 265 VirtualScreen::InitPipe(pipeName, pipePort); 266} 267 268VirtualScreenImpl::VirtualScreenImpl() 269 : isFirstSend(true), 270 isFirstRender(true), 271 writed(0), 272 wholeBuffer(nullptr), 273 screenBuffer(nullptr), 274 bufferSize(0), 275 currentPos(0) 276{ 277} 278 279VirtualScreenImpl::~VirtualScreenImpl() 280{ 281 FreeJpgMemory(); 282 if (WebSocketServer::GetInstance().firstImageBuffer) { 283 delete [] WebSocketServer::GetInstance().firstImageBuffer; 284 WebSocketServer::GetInstance().firstImageBuffer = nullptr; 285 } 286 if (VirtualScreenImpl::GetInstance().loadDocTempBuffer != nullptr) { 287 delete [] VirtualScreenImpl::GetInstance().loadDocTempBuffer; 288 VirtualScreenImpl::GetInstance().loadDocTempBuffer = nullptr; 289 } 290 if (VirtualScreenImpl::GetInstance().loadDocCopyBuffer != nullptr) { 291 delete [] VirtualScreenImpl::GetInstance().loadDocCopyBuffer; 292 VirtualScreenImpl::GetInstance().loadDocCopyBuffer = nullptr; 293 } 294} 295 296void VirtualScreenImpl::Send(const void* data, int32_t retWidth, int32_t retHeight) 297{ 298 if (CommandParser::GetInstance().GetScreenMode() == CommandParser::ScreenMode::STATIC 299 && VirtualScreen::isOutOfSeconds) { 300 return; 301 } 302 303 if (retWidth < 1 || retHeight < 1) { 304 FLOG("VirtualScreenImpl::RgbToJpg the retWidth or height is invalid value"); 305 return; 306 } 307 unsigned char* dataTemp = new(std::nothrow) unsigned char[retWidth * retHeight * jpgPix]; 308 if (!dataTemp) { 309 ELOG("Memory allocation failed : dataTemp."); 310 return; 311 } 312 for (int i = 0; i < retHeight; i++) { 313 for (int j = 0; j < retWidth; j++) { 314 int inputBasePos = i * retWidth * pixelSize + j * pixelSize; 315 int nowBasePos = i * retWidth * jpgPix + j * jpgPix; 316 dataTemp[nowBasePos + redPos] = *((char*)data + inputBasePos + redPos); 317 dataTemp[nowBasePos + greenPos] = *((char*)data + inputBasePos + greenPos); 318 dataTemp[nowBasePos + bluePos] = *((char*)data + inputBasePos + bluePos); 319 } 320 } 321 VirtualScreen::RgbToJpg(dataTemp, retWidth, retHeight); 322 delete [] dataTemp; 323 if (jpgBufferSize > bufferSize - headSize) { 324 FLOG("VirtualScreenImpl::Send length must < %d", bufferSize - headSize); 325 return; 326 } 327 328 std::copy(jpgScreenBuffer, jpgScreenBuffer + jpgBufferSize, screenBuffer + headSize); 329 writed = WebSocketServer::GetInstance().WriteData(screenBuffer, headSize + jpgBufferSize); 330 std::lock_guard<std::mutex> guard(WebSocketServer::GetInstance().mutex); 331 if (WebSocketServer::GetInstance().firstImageBuffer) { 332 delete [] WebSocketServer::GetInstance().firstImageBuffer; 333 WebSocketServer::GetInstance().firstImageBuffer = nullptr; 334 } 335 WebSocketServer::GetInstance().firstImageBuffer = new(std::nothrow) uint8_t[LWS_PRE + bufferSize]; 336 if (!WebSocketServer::GetInstance().firstImageBuffer) { 337 ELOG("Memory allocation failed : firstImageBuffer."); 338 return; 339 } 340 WebSocketServer::GetInstance().firstImagebufferSize = headSize + jpgBufferSize; 341 std::copy(screenBuffer, 342 screenBuffer + headSize + jpgBufferSize, 343 WebSocketServer::GetInstance().firstImageBuffer + LWS_PRE); 344 345 FreeJpgMemory(); 346} 347 348bool VirtualScreenImpl::SendPixmap(const void* data, size_t length, int32_t retWidth, int32_t retHeight) 349{ 350 if (data == nullptr) { 351 ELOG("render callback data is null."); 352 invalidFrameCountPerMinute++; 353 return false; 354 } 355 356 if (!isWebSocketConfiged) { 357 ELOG("image socket is not ready"); 358 return false; 359 } 360 361 if (isFirstRender) { 362 ILOG("Get first render buffer"); 363 TraceTool::GetInstance().HandleTrace("Get first render buffer"); 364 isFirstRender = false; 365 } 366 367 isFrameUpdated = true; 368 currentPos = 0; 369 370 WriteBuffer(headStart); 371 WriteBuffer(retWidth); 372 WriteBuffer(retHeight); 373 WriteBuffer(retWidth); 374 WriteBuffer(retHeight); 375 if (!CommandParser::GetInstance().IsRegionRefresh()) { 376 for (size_t i = 0; i < headReservedSize / sizeof(int32_t); i++) { 377 WriteBuffer(static_cast<uint32_t>(0)); 378 } 379 } else { 380 uint16_t x1 = 0; 381 uint16_t y1 = 0; 382 uint16_t width = static_cast<uint16_t>(retWidth); 383 uint16_t height = static_cast<uint16_t>(retHeight); 384 WriteBuffer(protocolVersion); 385 WriteBuffer(x1); 386 WriteBuffer(y1); 387 WriteBuffer(width); 388 WriteBuffer(height); 389 for (size_t i = 0; i < 10 / sizeof(uint16_t); i++) { // fill 10bytes for header 390 WriteBuffer(static_cast<uint16_t>(0)); 391 } 392 } 393 Send(data, retWidth, retHeight); 394 if (isFirstSend) { 395 ILOG("Send first buffer finish"); 396 TraceTool::GetInstance().HandleTrace("Send first buffer finish"); 397 isFirstSend = false; 398 } 399 400 validFrameCountPerMinute++; 401 sendFrameCountPerMinute++; 402 return writed == length; 403} 404 405void VirtualScreenImpl::FreeJpgMemory() 406{ 407 if (wholeBuffer != nullptr) { 408 delete [] wholeBuffer; 409 wholeBuffer = nullptr; 410 screenBuffer = nullptr; 411 } 412 if (jpgScreenBuffer != nullptr) { 413 free(jpgScreenBuffer); 414 jpgScreenBuffer = NULL; 415 jpgBufferSize = 0; 416 } 417 if (loadDocCopyBuffer != nullptr) { 418 delete [] loadDocCopyBuffer; 419 loadDocCopyBuffer = nullptr; 420 } 421 if (loadDocTempBuffer != nullptr) { 422 delete [] loadDocTempBuffer; 423 loadDocTempBuffer = nullptr; 424 } 425} 426 427ScreenInfo VirtualScreenImpl::GetScreenInfo() 428{ 429 ScreenInfo info; 430 info.orignalResolutionWidth = GetOrignalWidth(); 431 info.orignalResolutionHeight = GetOrignalHeight(); 432 info.compressionResolutionWidth = GetCompressionWidth(); 433 info.compressionResolutionHeight = GetCompressionHeight(); 434 info.foldStatus = GetFoldStatus(); 435 info.foldable = GetFoldable(); 436 info.foldWidth = GetFoldWidth(); 437 info.foldHeight = GetFoldHeight(); 438 return info; 439} 440 441void VirtualScreenImpl::InitFoldParams() 442{ 443 CommandParser& parser = CommandParser::GetInstance(); 444 FoldInfo info; 445 parser.GetFoldInfo(info); 446 if (parser.IsSet("foldable")) { 447 SetFoldable(info.foldable); 448 } 449 if (parser.IsSet("foldStatus")) { 450 SetFoldStatus(info.foldStatus); 451 } 452 if (parser.IsSet("fr")) { 453 SetFoldResolution(info.foldResolutionWidth, info.foldResolutionHeight); 454 } 455}