• 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();
58     void Destroy();
59     const char* pendingError_ = nullptr;
60     const float scale_ = 0.5f;
61 private:
62     void PollAndNotifyFrames();
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 
80 
AdapteScreenChange()81 static void AdapteScreenChange()
82 {
83     if (g_screenCopy == nullptr) {
84         return;
85     }
86     // destrory current one and create a new one
87     LOG_I("Screen changed, auto restart ScreenCopy");
88     const auto scale = g_screenCopy->scale_;
89     g_screenCopy->Destroy();
90     g_screenCopy = make_unique<ScreenCopy>(scale);
91     g_screenCopy->Run();
92 }
93 
~ScreenCopy()94 ScreenCopy::~ScreenCopy()
95 {
96     if (!stopped_.load()) {
97         Destroy();
98     }
99 }
100 
Run()101 bool ScreenCopy::Run()
102 {
103     if (scale_ <= 0 || scale_ > 1.0) {
104         pendingError_ = "Error: Illegal scale value!";
105         return false;
106     }
107     // get source screen
108     auto id = static_cast<ScreenId>(DisplayManager::GetInstance().GetDefaultDisplayId());
109     sourceScreen_ = ScreenManager::GetInstance().GetScreenById(id);
110     if (id == SCREEN_ID_INVALID || sourceScreen_ == nullptr) {
111         pendingError_ = "Error: Get main screen failed!";
112         return false;
113     }
114     // listen screen changes for auto-adapting
115     if (screenChangeListener_ == nullptr) {
116         screenChangeListener_ = new ScreenChangeListener([]() { AdapteScreenChange(); }, id);
117         auto ret = ScreenManager::GetInstance().RegisterScreenListener(screenChangeListener_);
118         LOG_D("Register ScreenListener, ret=%{public}d", ret);
119     }
120     // run snapshot thread and encode thread
121     snapshotThread = make_unique<thread>([this]() { this->PollAndNotifyFrames(); });
122     encodeThread = make_unique<thread>([this]() { this->WaitAndConsumeFrames(); });
123     return true;
124 }
125 
Destroy()126 void ScreenCopy::Destroy()
127 {
128     if (stopped_.load()) {
129         return;
130     }
131     unique_lock<mutex> lock(frameLock_);
132     stopped_.store(true);
133     frameCond_.notify_all(); // mark stopped and wakeup the waiting thread
134     lock.unlock();
135     LOG_D("Begin to wait for threads exit");
136     if (snapshotThread != nullptr && snapshotThread->joinable()) {
137         snapshotThread->join();
138         snapshotThread = nullptr;
139     }
140     sourceScreen_ = nullptr;
141     lastFrame_ = nullptr;
142     newestFrame_ = nullptr;
143     if (encodeThread != nullptr && encodeThread->joinable()) {
144         encodeThread->join();
145         encodeThread = nullptr;
146     }
147     LOG_D("All threads exited");
148 }
149 
PollAndNotifyFrames()150 void ScreenCopy::PollAndNotifyFrames()
151 {
152     constexpr int32_t screenCheckIntervalUs = 50 * 1000;
153     auto &dm = DisplayManager::GetInstance();
154     const auto displayId = dm.GetDefaultDisplayId();
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     lastFrame_ = newestFrame_;
187     newestFrame_ = frame;
188     changed = true;
189     const size_t newestFrameSize = newestFrame_->GetHeight() * newestFrame_->GetRowStride();
190     // if this is the first frame
191     if (lastFrame_ == nullptr) {
192         // if screen copy starts with screen-off, given a black image
193         if (DisplayManager::GetInstance().GetDisplayState(sourceScreen_->GetId()) == DisplayState::OFF) {
194             memset_s(frame->GetWritablePixels(), newestFrameSize, 0, newestFrameSize);
195         }
196         return;
197     }
198     // compare this frame and last frame
199     const size_t lastFrameSize = lastFrame_->GetHeight() * lastFrame_->GetRowStride();
200     if (lastFrameSize == newestFrameSize) {
201         changed = memcmp(lastFrame_->GetPixels(), newestFrame_->GetPixels(), newestFrameSize) != 0;
202     }
203     // detect screen of only when not changed
204     if (!changed && !screenOff) {
205         screenOff = DisplayManager::GetInstance().GetDisplayState(sourceScreen_->GetId()) == DisplayState::OFF;
206         if (screenOff) {
207             // mark changed and reset pixels to black so we provide a black image
208             changed = true;
209             memset_s(frame->GetWritablePixels(), newestFrameSize, 0, newestFrameSize);
210         }
211     }
212 }
213 
214 struct MissionErrorMgr : public jpeg_error_mgr {
215     jmp_buf setjmp_buffer;
216 };
217 
AdaptJpegSize(jpeg_compress_struct & jpeg,uint32_t width,uint32_t height)218 static void AdaptJpegSize(jpeg_compress_struct &jpeg, uint32_t width, uint32_t height)
219 {
220     constexpr int32_t alignment = 32;
221     if (width % alignment == 0) {
222         jpeg.image_width = width;
223     } else {
224         LOG_D("The width need to be adapted!");
225         jpeg.image_width = ceil((double)width / (double)alignment) * alignment;
226     }
227     jpeg.image_height = height;
228 }
229 
ScaleNewsetFrameLocked()230 shared_ptr<PixelMap> ScreenCopy::ScaleNewsetFrameLocked()
231 {
232     if (newestFrame_ == nullptr) {
233         return newestFrame_;
234     }
235     // resize the pixelmap to fit scale
236     auto origWidth = newestFrame_->GetWidth();
237     auto origHeight = newestFrame_->GetHeight();
238     Media::Rect rect = {.left = 0, .top = 0, .width = origWidth, .height = origHeight};
239     Media::InitializationOptions opt;
240     opt.size.width = ceil(origWidth * scale_);
241     opt.size.height = ceil(origHeight * scale_);
242     opt.scaleMode = Media::ScaleMode::FIT_TARGET_SIZE;
243     opt.editable = false;
244     return Media::PixelMap::Create(*newestFrame_, rect, opt);
245 }
246 
WaitAndConsumeFrames()247 void ScreenCopy::WaitAndConsumeFrames()
248 {
249     LOG_I("Start WaitAndConsumeFrames");
250     while (!stopped_.load()) {
251         unique_lock<mutex> lock(frameLock_);
252         frameCond_.wait(lock);
253         if (stopped_.load()) {
254             break;
255         }
256         LOG_D("ConsumeFrame_Begin");
257         // resize the pixelmap to fit scale
258         auto scaledPixels = ScaleNewsetFrameLocked();
259         lock.unlock();
260         if (scaledPixels == nullptr) {
261             continue;
262         }
263         constexpr int32_t rgbaPixelBytes = 4;
264         jpeg_compress_struct jpeg;
265         MissionErrorMgr jerr;
266         jpeg.err = jpeg_std_error(&jerr);
267         jpeg_create_compress(&jpeg);
268         AdaptJpegSize(jpeg, scaledPixels->GetWidth(), scaledPixels->GetHeight());
269         jpeg.input_components = rgbaPixelBytes;
270         jpeg.in_color_space = JCS_EXT_RGBX;
271         jpeg_set_defaults(&jpeg);
272         constexpr int32_t compressQuality = 75;
273         jpeg_set_quality(&jpeg, compressQuality, 1);
274         uint8_t *imgBuf = nullptr;
275         unsigned long imgSize = 0;
276         jpeg_mem_dest(&jpeg, &imgBuf, &imgSize);
277         jpeg_start_compress(&jpeg, 1);
278         JSAMPROW rowPointer[1024 * 4];
279         const auto stride = scaledPixels->GetRowStride();
280         auto memAddr = (uint8_t *)(scaledPixels->GetPixels());
281         for (int32_t rowIndex = 0; rowIndex < scaledPixels->GetHeight(); rowIndex++) {
282             rowPointer[rowIndex] = memAddr;
283             memAddr += stride;
284         }
285         (void)jpeg_write_scanlines(&jpeg, rowPointer, jpeg.image_height);
286         jpeg_finish_compress(&jpeg);
287         jpeg_destroy_compress(&jpeg);
288         LOG_D("ConsumeFrame_End, size=%{public}lu", imgSize);
289         if (g_screenCopyHandler != nullptr) {
290             g_screenCopyHandler(imgBuf, imgSize);
291         } else {
292             free(imgBuf);
293         }
294     }
295     LOG_I("Stop WaitAndConsumeFrames");
296 }
297 
StartScreenCopy(float scale,ScreenCopyHandler handler)298 bool StartScreenCopy(float scale, ScreenCopyHandler handler)
299 {
300     if (scale <= 0 || scale > 1 || handler == nullptr) {
301         LOG_E("Illegal arguments");
302         return false;
303     }
304     StopScreenCopy();
305     g_screenCopyHandler = handler;
306     g_screenCopy = make_unique<ScreenCopy>(scale);
307     bool success = g_screenCopy != nullptr && g_screenCopy->Run();
308     if (!success) {
309         constexpr size_t BUF_SIZE = 128;
310         auto buf = (uint8_t *)malloc(BUF_SIZE);
311         memset_s(buf, BUF_SIZE, 0, BUF_SIZE);
312         if (g_screenCopy->pendingError_ != nullptr) {
313             g_screenCopy->pendingError_ = "Failed to run ScreenCopy, unknown error";
314         }
315         memcpy_s(buf, BUF_SIZE, g_screenCopy->pendingError_, strlen(g_screenCopy->pendingError_));
316         LOG_E("The error message is %{public}s", buf);
317         handler(buf, strlen(g_screenCopy->pendingError_));
318         g_screenCopy->pendingError_ = nullptr;
319         StopScreenCopy();
320     }
321     return success;
322 }
323 
StopScreenCopy()324 void StopScreenCopy()
325 {
326     if (g_screenCopy != nullptr) {
327         g_screenCopy->Destroy();
328         g_screenCopy = nullptr;
329     }
330 }
331 }
332