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}