• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <condition_variable>
17 #include <display_manager.h>
18 #include <jpeglib.h>
19 #include <memory>
20 #include <mutex>
21 #include <pixel_map.h>
22 #include <refbase.h>
23 #include <screen_manager.h>
24 #include <securec.h>
25 #include <csetjmp>
26 #include "common_utilities_hpp.h"
27 #include "screen_copy.h"
28 
29 namespace OHOS::uitest {
30 using namespace std;
31 using namespace OHOS;
32 using namespace OHOS::Media;
33 using namespace OHOS::Rosen;
34 
35 using OnScreenChangeHandler = function<void()>;
36 class ScreenChangeListener : public ScreenManager::IScreenListener {
37 public:
ScreenChangeListener(OnScreenChangeHandler hdl,ScreenId id)38     explicit ScreenChangeListener(OnScreenChangeHandler hdl, ScreenId id): handler_(hdl), targetId_(id) {}
OnConnect(ScreenId id)39     void OnConnect(ScreenId id) override {};
OnDisconnect(ScreenId id)40     void OnDisconnect(ScreenId id) override {};
OnChange(ScreenId id)41     void OnChange(ScreenId id) override
42     {
43         if (handler_ != nullptr && id == targetId_) {
44             handler_();
45         }
46     };
Destroy()47     void Destroy() { handler_ = nullptr; }
48 private:
49     OnScreenChangeHandler handler_ = nullptr;
50     ScreenId targetId_ = SCREEN_ID_INVALID;
51 };
52 
53 class ScreenCopy {
54 public:
ScreenCopy(float scale)55     explicit ScreenCopy(float scale): scale_(scale) {};
56     virtual ~ScreenCopy();
57     bool Run(int32_t displayId);
58     void Destroy();
59     const char* pendingError_ = nullptr;
60     const float scale_ = 0.5f;
61 private:
62     void PollAndNotifyFrames(int32_t displayId);
63     void WaitAndConsumeFrames();
64     void UpdateFrameLocked(shared_ptr<PixelMap> frame, bool &changed, bool &muted);
65     shared_ptr<PixelMap> ScaleNewsetFrameLocked();
66     sptr<Screen> sourceScreen_;
67     shared_ptr<PixelMap> lastFrame_ = nullptr;
68     shared_ptr<PixelMap> newestFrame_ = nullptr;
69     mutex frameLock_;
70     condition_variable frameCond_;
71     unique_ptr<thread> snapshotThread = nullptr;
72     unique_ptr<thread> encodeThread = nullptr;
73     atomic_bool stopped_ = false;
74     static sptr<ScreenChangeListener> screenChangeListener_;
75 };
76 sptr<ScreenChangeListener> ScreenCopy::screenChangeListener_ = nullptr;
77 static unique_ptr<ScreenCopy> g_screenCopy = nullptr;
78 static ScreenCopyHandler g_screenCopyHandler = nullptr;
79 
AdapteScreenChange(int32_t displayId)80 static void AdapteScreenChange(int32_t displayId)
81 {
82     if (g_screenCopy == nullptr) {
83         return;
84     }
85     // destrory current one and create a new one
86     LOG_D("Screen changed, auto restart ScreenCopy");
87     const auto scale = g_screenCopy->scale_;
88     g_screenCopy->Destroy();
89     g_screenCopy = make_unique<ScreenCopy>(scale);
90     g_screenCopy->Run(displayId);
91 }
92 
~ScreenCopy()93 ScreenCopy::~ScreenCopy()
94 {
95     if (!stopped_.load()) {
96         Destroy();
97     }
98 }
99 
Run(int32_t displayId)100 bool ScreenCopy::Run(int32_t displayId)
101 {
102     if (scale_ <= 0 || scale_ > 1.0) {
103         pendingError_ = "Error: Illegal scale value!";
104         return false;
105     }
106     // get source screen
107     if (displayId == UNASSIGNED) {
108         displayId = DisplayManager::GetInstance().GetDefaultDisplayId();
109     }
110     sourceScreen_ = ScreenManager::GetInstance().GetScreenById(displayId);
111     if (displayId == SCREEN_ID_INVALID || sourceScreen_ == nullptr) {
112         pendingError_ = "Error: Get main screen failed!";
113         return false;
114     }
115     // listen screen changes for auto-adapting
116     if (screenChangeListener_ == nullptr) {
117         screenChangeListener_ = new ScreenChangeListener([displayId]() { AdapteScreenChange(displayId); }, displayId);
118         auto ret = ScreenManager::GetInstance().RegisterScreenListener(screenChangeListener_);
119         LOG_D("Register ScreenListener, ret=%{public}d", ret);
120     }
121     // run snapshot thread and encode thread
122     snapshotThread = make_unique<thread>([this, displayId]() { this->PollAndNotifyFrames(displayId); });
123     encodeThread = make_unique<thread>([this]() { this->WaitAndConsumeFrames(); });
124     return true;
125 }
126 
Destroy()127 void ScreenCopy::Destroy()
128 {
129     if (stopped_.load()) {
130         return;
131     }
132     unique_lock<mutex> lock(frameLock_);
133     stopped_.store(true);
134     frameCond_.notify_all(); // mark stopped and wakeup the waiting thread
135     lock.unlock();
136     LOG_D("Begin to wait for threads exit");
137     if (snapshotThread != nullptr && snapshotThread->joinable()) {
138         snapshotThread->join();
139         snapshotThread = nullptr;
140     }
141     sourceScreen_ = nullptr;
142     lastFrame_ = nullptr;
143     newestFrame_ = nullptr;
144     if (encodeThread != nullptr && encodeThread->joinable()) {
145         encodeThread->join();
146         encodeThread = nullptr;
147     }
148     LOG_D("All threads exited");
149 }
150 
PollAndNotifyFrames(int32_t displayId)151 void ScreenCopy::PollAndNotifyFrames(int32_t displayId)
152 {
153     constexpr int32_t screenCheckIntervalUs = 50 * 1000;
154     auto &dm = DisplayManager::GetInstance();
155     LOG_I("Start PollAndNotifyFrames");
156     bool changed = false;
157     bool screenOff = false;
158     while (!stopped_.load()) {
159         if (screenOff) {
160             usleep(screenCheckIntervalUs);
161             if (dm.GetDisplayState(displayId) != DisplayState::OFF) {
162                 screenOff = false;
163                 LOG_I("Screen turned on! resume screenCopy");
164             }
165             continue;
166         }
167         shared_ptr<PixelMap> frame = dm.GetScreenshot(displayId);
168         if (frame == nullptr) {
169             continue;
170         }
171         frameLock_.lock();
172         UpdateFrameLocked(frame, changed, screenOff);
173         frameLock_.unlock();
174         LOG_D("GetOneFrameDone, Changed=%{public}d", changed);
175         if (changed) {
176             frameCond_.notify_all();
177         }
178         if (screenOff) {
179             LOG_I("Screen turned off! mute screenCopy");
180         }
181     }
182 }
183 
UpdateFrameLocked(shared_ptr<PixelMap> frame,bool & changed,bool & screenOff)184 void ScreenCopy::UpdateFrameLocked(shared_ptr<PixelMap> frame, bool &changed, bool &screenOff)
185 {
186     DCHECK(sourceScreen_);
187     lastFrame_ = newestFrame_;
188     newestFrame_ = frame;
189     changed = true;
190     const size_t newestFrameSize = newestFrame_->GetHeight() * newestFrame_->GetRowStride();
191     // if this is the first frame
192     if (lastFrame_ == nullptr) {
193         // if screen copy starts with screen-off, given a black image
194         if (DisplayManager::GetInstance().GetDisplayState(sourceScreen_->GetId()) == DisplayState::OFF) {
195             memset_s(frame->GetWritablePixels(), newestFrameSize, 0, newestFrameSize);
196         }
197         return;
198     }
199     // compare this frame and last frame
200     const size_t lastFrameSize = lastFrame_->GetHeight() * lastFrame_->GetRowStride();
201     if (lastFrameSize == newestFrameSize) {
202         changed = memcmp(lastFrame_->GetPixels(), newestFrame_->GetPixels(), newestFrameSize) != 0;
203     }
204     // detect screen of only when not changed
205     if (!changed && !screenOff) {
206         screenOff = DisplayManager::GetInstance().GetDisplayState(sourceScreen_->GetId()) == DisplayState::OFF;
207         if (screenOff) {
208             // mark changed and reset pixels to black so we provide a black image
209             changed = true;
210             memset_s(frame->GetWritablePixels(), newestFrameSize, 0, newestFrameSize);
211         }
212     }
213 }
214 
215 struct MissionErrorMgr : public jpeg_error_mgr {
216     jmp_buf setjmp_buffer;
217 };
218 
AdaptJpegSize(jpeg_compress_struct & jpeg,uint32_t width,uint32_t height)219 static void AdaptJpegSize(jpeg_compress_struct &jpeg, uint32_t width, uint32_t height)
220 {
221     constexpr int32_t alignment = 32;
222     if (width % alignment == 0) {
223         jpeg.image_width = width;
224     } else {
225         LOG_D("The width need to be adapted!");
226         jpeg.image_width = ceil((double)width / (double)alignment) * alignment;
227     }
228     jpeg.image_height = height;
229 }
230 
ScaleNewsetFrameLocked()231 shared_ptr<PixelMap> ScreenCopy::ScaleNewsetFrameLocked()
232 {
233     if (newestFrame_ == nullptr) {
234         return newestFrame_;
235     }
236     // resize the pixelmap to fit scale
237     auto origWidth = newestFrame_->GetWidth();
238     auto origHeight = newestFrame_->GetHeight();
239     Media::Rect rect = {.left = 0, .top = 0, .width = origWidth, .height = origHeight};
240     Media::InitializationOptions opt;
241     opt.size.width = ceil(origWidth * scale_);
242     opt.size.height = ceil(origHeight * scale_);
243     opt.scaleMode = Media::ScaleMode::FIT_TARGET_SIZE;
244     opt.editable = false;
245     return Media::PixelMap::Create(*newestFrame_, rect, opt);
246 }
247 
WaitAndConsumeFrames()248 void ScreenCopy::WaitAndConsumeFrames()
249 {
250     LOG_I("Start WaitAndConsumeFrames");
251     while (!stopped_.load()) {
252         unique_lock<mutex> lock(frameLock_);
253         frameCond_.wait(lock);
254         if (stopped_.load()) {
255             break;
256         }
257         LOG_D("ConsumeFrame_Begin");
258         // resize the pixelmap to fit scale
259         auto scaledPixels = ScaleNewsetFrameLocked();
260         lock.unlock();
261         if (scaledPixels == nullptr) {
262             continue;
263         }
264         constexpr int32_t rgbaPixelBytes = 4;
265         jpeg_compress_struct jpeg;
266         MissionErrorMgr jerr;
267         jpeg.err = jpeg_std_error(&jerr);
268         jpeg_create_compress(&jpeg);
269         AdaptJpegSize(jpeg, scaledPixels->GetWidth(), scaledPixels->GetHeight());
270         jpeg.input_components = rgbaPixelBytes;
271         jpeg.in_color_space = JCS_EXT_RGBX;
272         jpeg_set_defaults(&jpeg);
273         constexpr int32_t compressQuality = 75;
274         jpeg_set_quality(&jpeg, compressQuality, 1);
275         uint8_t *imgBuf = nullptr;
276         unsigned long imgSize = 0;
277         jpeg_mem_dest(&jpeg, &imgBuf, &imgSize);
278         jpeg_start_compress(&jpeg, 1);
279         JSAMPROW rowPointer[1024 * 4];
280         const auto stride = scaledPixels->GetRowStride();
281         auto memAddr = (uint8_t *)(scaledPixels->GetPixels());
282         for (int32_t rowIndex = 0; rowIndex < scaledPixels->GetHeight(); rowIndex++) {
283             rowPointer[rowIndex] = memAddr;
284             memAddr += stride;
285         }
286         (void)jpeg_write_scanlines(&jpeg, rowPointer, jpeg.image_height);
287         jpeg_finish_compress(&jpeg);
288         jpeg_destroy_compress(&jpeg);
289         LOG_D("ConsumeFrame_End, size=%{public}lu", imgSize);
290         if (g_screenCopyHandler != nullptr) {
291             g_screenCopyHandler(imgBuf, imgSize);
292         } else {
293             free(imgBuf);
294         }
295     }
296     LOG_I("Stop WaitAndConsumeFrames");
297 }
298 
StartScreenCopy(float scale,int32_t displayId,ScreenCopyHandler handler)299 bool StartScreenCopy(float scale, int32_t displayId, ScreenCopyHandler handler)
300 {
301     if (scale <= 0 || scale > 1 || handler == nullptr) {
302         LOG_E("Illegal arguments");
303         return false;
304     }
305     StopScreenCopy();
306     g_screenCopyHandler = handler;
307     g_screenCopy = make_unique<ScreenCopy>(scale);
308     bool success = g_screenCopy != nullptr && g_screenCopy->Run(displayId);
309     if (!success) {
310         constexpr size_t BUF_SIZE = 128;
311         auto buf = (uint8_t *)malloc(BUF_SIZE);
312         memset_s(buf, BUF_SIZE, 0, BUF_SIZE);
313         if (g_screenCopy->pendingError_ != nullptr) {
314             g_screenCopy->pendingError_ = "Failed to run ScreenCopy, unknown error";
315         }
316         memcpy_s(buf, BUF_SIZE, g_screenCopy->pendingError_, strlen(g_screenCopy->pendingError_));
317         LOG_E("The error message is %{public}s", buf);
318         handler(buf, strlen(g_screenCopy->pendingError_));
319         g_screenCopy->pendingError_ = nullptr;
320         StopScreenCopy();
321     }
322     return success;
323 }
324 
StopScreenCopy()325 void StopScreenCopy()
326 {
327     if (g_screenCopy != nullptr) {
328         g_screenCopy->Destroy();
329         g_screenCopy = nullptr;
330     }
331 }
332 }
333