1 /*
2 * Copyright (c) 2023-2024 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 "jpeg_image_processor.h"
17
18 #include <cstring>
19 #include <fstream>
20 #include <iostream>
21 #include "jpeglib.h"
22 #include <securec.h>
23 #include <string>
24
25 #include "dscreen_errcode.h"
26 #include "dscreen_log.h"
27
28 namespace OHOS {
29 namespace DistributedHardware {
SetOutputSurface(sptr<Surface> surface)30 int32_t JpegImageProcessor::SetOutputSurface(sptr<Surface> surface)
31 {
32 DHLOGI("%{public}s: SetOutputSurface.", DSCREEN_LOG_TAG);
33 if (surface == nullptr) {
34 DHLOGE("%{public}s: SetOutputSurface surface is nullptr.", DSCREEN_LOG_TAG);
35 return ERR_DH_SCREEN_TRANS_NULL_VALUE;
36 }
37 imageSurface_ = surface;
38 return DH_SUCCESS;
39 }
40
FillDirtyImages2Surface(const std::shared_ptr<DataBuffer> &data, uint8_t *lastFrame)41 int32_t JpegImageProcessor::FillDirtyImages2Surface(const std::shared_ptr<DataBuffer> &data, uint8_t *lastFrame)
42 {
43 DHLOGI("%{public}s: FillDirtyImages2Surface.", DSCREEN_LOG_TAG);
44 if (imageSurface_ == nullptr) {
45 DHLOGE("%{public}s: imageSurface_ is nullptr.", DSCREEN_LOG_TAG);
46 return ERR_DH_SCREEN_SURFACE_INVALIED;
47 }
48 uint32_t lastFrameSize = configParam_.GetScreenWidth() * configParam_.GetScreenHeight() * RGB_CHROMA / TWO;
49 int32_t ret = DecodeDamageData(data, lastFrame);
50 if (ret != DH_SUCCESS) {
51 DHLOGE("%{public}s: Merge dirty failed, ret: %{public}" PRId32, DSCREEN_LOG_TAG, ret);
52 return ret;
53 }
54 sptr<OHOS::SurfaceBuffer> windowSurfaceBuffer = nullptr;
55 int32_t releaseFence = -1;
56 OHOS::BufferRequestConfig requestConfig = {
57 .width = configParam_.GetScreenWidth(),
58 .height = configParam_.GetScreenHeight(),
59 .strideAlignment = STRIDE_ALIGNMENT,
60 .format = GRAPHIC_PIXEL_FMT_YCBCR_420_SP,
61 .usage = BUFFER_USAGE_CPU_READ | BUFFER_USAGE_CPU_WRITE | BUFFER_USAGE_MEM_DMA,
62 };
63 SurfaceError surfaceErr = imageSurface_->RequestBuffer(windowSurfaceBuffer, releaseFence, requestConfig);
64 if (surfaceErr != SURFACE_ERROR_OK || windowSurfaceBuffer == nullptr) {
65 DHLOGE("%{public}s: imageSurface request buffer failed, surfaceErr: %{public}" PRId32,
66 DSCREEN_LOG_TAG, surfaceErr);
67 imageSurface_->CancelBuffer(windowSurfaceBuffer);
68 return surfaceErr;
69 }
70 uint32_t surfaceBuffeSize = windowSurfaceBuffer->GetSize();
71 auto windowSurfaceAddr = static_cast<uint8_t*>(windowSurfaceBuffer->GetVirAddr());
72 ret = memcpy_s(windowSurfaceAddr, surfaceBuffeSize, lastFrame, lastFrameSize);
73 if (ret != DH_SUCCESS) {
74 DHLOGE("%{public}s: memcpy lastFrame failed,ret: %{public}" PRId32, DSCREEN_LOG_TAG, ret);
75 imageSurface_->CancelBuffer(windowSurfaceBuffer);
76 return ret;
77 }
78 BufferFlushConfig flushConfig = { {0, 0, windowSurfaceBuffer->GetWidth(), windowSurfaceBuffer-> GetHeight()}, 0};
79 surfaceErr = imageSurface_->FlushBuffer(windowSurfaceBuffer, -1, flushConfig);
80 if (surfaceErr != SURFACE_ERROR_OK) {
81 DHLOGE("%{public}s: imageSurface flush buffer failed, surfaceErr: %{public}" PRId32,
82 DSCREEN_LOG_TAG, surfaceErr);
83 imageSurface_->CancelBuffer(windowSurfaceBuffer);
84 return surfaceErr;
85 }
86 DHLOGI("%{public}s: FillDirtyImages2Surface success.", DSCREEN_LOG_TAG);
87 return DH_SUCCESS;
88 }
89
ProcessDamageSurface(sptr<SurfaceBuffer> &surfaceBuffer, const std::vector<OHOS::Rect> &damages)90 int32_t JpegImageProcessor::ProcessDamageSurface(sptr<SurfaceBuffer> &surfaceBuffer,
91 const std::vector<OHOS::Rect> &damages)
92 {
93 DHLOGI("%{public}s: ProcessDamageSurface.", DSCREEN_LOG_TAG);
94 std::shared_ptr<DataBuffer> dataBuf = std::make_shared<DataBuffer>(configParam_.GetScreenWidth() *
95 configParam_.GetScreenHeight() * RGBA_CHROMA);
96 dataBuf->SetSize(0);
97 for (auto item : damages) {
98 EncodeDamageData(surfaceBuffer, item, dataBuf);
99 }
100 std::shared_ptr<IImageSourceProcessorListener> listener = imageProcessorListener_.lock();
101 if (listener == nullptr) {
102 DHLOGE("%{public}s: Processor listener is null.", DSCREEN_LOG_TAG);
103 imageSurface_->ReleaseBuffer(surfaceBuffer, -1);
104 return ERR_DH_SCREEN_CODEC_SURFACE_ERROR;
105 }
106 dataBuf->SetDataType(VIDEO_PART_SCREEN_DATA);
107 listener->OnImageProcessDone(dataBuf);
108 return DH_SUCCESS;
109 }
110
SetImageProcessListener(std::shared_ptr<IImageSourceProcessorListener> &listener)111 int32_t JpegImageProcessor::SetImageProcessListener(std::shared_ptr<IImageSourceProcessorListener> &listener)
112 {
113 DHLOGI("%{public}s: SetImageProcessorListener.", DSCREEN_LOG_TAG);
114 imageProcessorListener_ = listener;
115 return DH_SUCCESS;
116 }
117
EncodeDamageData(sptr<SurfaceBuffer> &surfaceBuffer, const OHOS::Rect &damage, std::shared_ptr<DataBuffer> &data)118 void JpegImageProcessor::EncodeDamageData(sptr<SurfaceBuffer> &surfaceBuffer,
119 const OHOS::Rect &damage, std::shared_ptr<DataBuffer> &data)
120 {
121 DHLOGI("%{public}s: EncodeDamageData.", DSCREEN_LOG_TAG);
122 uint32_t partialSize = damage.w * damage.h * RGBA_CHROMA;
123 unsigned char *partialBuffer = new unsigned char[partialSize];
124 unsigned char *partialBufferIdx = partialBuffer;
125 auto surfaceAddrIdx = static_cast<uint8_t*>(surfaceBuffer->GetVirAddr());
126 surfaceAddrIdx += damage.y * configParam_.GetScreenWidth() * RGBA_CHROMA + damage.x * RGBA_CHROMA;
127 for (int32_t row = 0 ; row < damage.h ; row++) {
128 int32_t ret = memcpy_s(partialBufferIdx, damage.w * RGBA_CHROMA, surfaceAddrIdx, damage.w * RGBA_CHROMA);
129 if (ret != DH_SUCCESS) {
130 DHLOGE("%{public}s: get partail data failed.", DSCREEN_LOG_TAG);
131 imageSurface_->ReleaseBuffer(surfaceBuffer, -1);
132 delete [] partialBuffer;
133 return;
134 }
135 partialBufferIdx += damage.w * RGBA_CHROMA;
136 surfaceAddrIdx += configParam_.GetScreenWidth() * RGBA_CHROMA;
137 }
138 uint32_t jpegSize = CompressRgbaToJpeg(damage, partialBuffer, partialSize, data);
139 DHLOGI("CompressRgbaToJpeg end, jpegSize %{public}" PRIu32, jpegSize);
140 delete [] partialBuffer;
141 }
142
DecodeDamageData(const std::shared_ptr<DataBuffer> &data, uint8_t *lastFrame)143 int32_t JpegImageProcessor::DecodeDamageData(const std::shared_ptr<DataBuffer> &data, uint8_t *lastFrame)
144 {
145 DHLOGI("%{public}s: DecodeDamageData.", DSCREEN_LOG_TAG);
146 std::vector<DirtyRect> dirtyRectVec = data->GetDirtyRectVec();
147 int32_t offset = 0;
148 uint32_t screenWidth = configParam_.GetScreenWidth();
149 uint32_t screenHeight = configParam_.GetScreenHeight();
150 for (auto item : dirtyRectVec) {
151 if (item.xPos > screenWidth || item.yPos > screenHeight ||
152 item.width > screenWidth - item.xPos || item.height > screenHeight - item.yPos) {
153 DHLOGE("%{public}s: Dirty rect invalid.", DSCREEN_LOG_TAG);
154 return ERR_DH_SCREEN_INPUT_PARAM_INVALID;
155 }
156 if (item.dirtySize > DIRTY_MAX_BUF_SIZE) {
157 DHLOGE("%{public}s: Dirty rect invalid, dirtySize = %{public}" PRIu32, DSCREEN_LOG_TAG, item.dirtySize);
158 return ERR_DH_SCREEN_INPUT_PARAM_INVALID;
159 }
160 uint8_t *jpegData = new uint8_t[item.dirtySize] {0};
161 int32_t ret = data->GetData(offset, item.dirtySize, jpegData);
162 if (ret != DH_SUCCESS) {
163 delete [] jpegData;
164 return ret;
165 }
166 offset += static_cast<int32_t>(item.dirtySize);
167 uint32_t dirtyImageDataSize = item.width * item.height * RGB_CHROMA;
168 if (dirtyImageDataSize > DIRTY_MAX_IMAGE_DATA_SIZE) {
169 DHLOGE("%{public}s: The dirtyImageDataSize is out of range. Expected max: %{public}" PRIu32
170 ", actual: %{public}" PRIu32, DSCREEN_LOG_TAG, DIRTY_MAX_IMAGE_DATA_SIZE, dirtyImageDataSize);
171 delete[] jpegData;
172 return ERR_DH_SCREEN_INPUT_PARAM_INVALID;
173 }
174 uint8_t *dirtyImageData = new uint8_t[dirtyImageDataSize] {0};
175 DHLOGI("%{public}s: DecompressJpegToNV12.", DSCREEN_LOG_TAG);
176 DecompressJpegToNV12(item.dirtySize, jpegData, dirtyImageData, dirtyImageDataSize, item);
177 DHLOGI("%{public}s: DecompressJpegToNV12 success.", DSCREEN_LOG_TAG);
178 ret = ReplaceDamage2LastFrame(lastFrame, dirtyImageData, item);
179 if (ret != DH_SUCCESS) {
180 DHLOGE("ReplaceDamage2LastFrame failed, ret: %{public}" PRId32, ret);
181 delete [] jpegData;
182 delete [] dirtyImageData;
183 return ret;
184 }
185 delete [] jpegData;
186 delete [] dirtyImageData;
187 }
188 DHLOGI("%{public}s: DecodeDamageData success.", DSCREEN_LOG_TAG);
189 return DH_SUCCESS;
190 }
191
ReplaceDamage2LastFrame(uint8_t *lastFrame, uint8_t *dirtyImageData, const DirtyRect rect)192 int32_t JpegImageProcessor::ReplaceDamage2LastFrame(uint8_t *lastFrame, uint8_t *dirtyImageData, const DirtyRect rect)
193 {
194 DHLOGI("%{public}s: ReplaceDamage2LastFrame.", DSCREEN_LOG_TAG);
195 uint8_t *lastFrameIdx = lastFrame;
196 uint8_t *yData = lastFrameIdx + (configParam_.GetScreenWidth() * rect.yPos + rect.xPos);
197 uint8_t *uData = lastFrameIdx + configParam_.GetScreenWidth() * configParam_.GetScreenHeight() +
198 (configParam_.GetScreenWidth() * (rect.yPos / TWO) + rect.xPos);
199 uint8_t *yDirtyData = dirtyImageData;
200 uint8_t *uDirtyData = dirtyImageData + rect.width * rect.height;
201 uint8_t *yTempData = nullptr;
202 uint8_t *uTempData = nullptr;
203 for (uint32_t i = 0 ; i < rect.height ; i++) {
204 yTempData = yData + i * configParam_.GetScreenWidth();
205 int32_t ret = memcpy_s(yTempData, rect.width, yDirtyData, rect.width);
206 if (ret != EOK) {
207 DHLOGE("%{public}s: memcpy yData failed.", DSCREEN_LOG_TAG);
208 return ret;
209 }
210 yDirtyData += rect.width;
211 if (i % TWO) {
212 uTempData = uData + configParam_.GetScreenWidth() * (i / TWO);
213 ret = memcpy_s(uTempData, rect.width, uDirtyData, rect.width);
214 if (ret != EOK) {
215 DHLOGE("%{public}s: memcpy uData failed.", DSCREEN_LOG_TAG);
216 return ret;
217 }
218 uDirtyData += rect.width;
219 }
220 }
221 DHLOGI("%{public}s: ReplaceDamage2LastFrame success.", DSCREEN_LOG_TAG);
222 return DH_SUCCESS;
223 }
224
CompressRgbaToJpeg(const OHOS::Rect &damage, uint8_t *inputData, uint32_t inputDataSize, std::shared_ptr<DataBuffer> &data)225 uint32_t JpegImageProcessor::CompressRgbaToJpeg(const OHOS::Rect &damage,
226 uint8_t *inputData, uint32_t inputDataSize, std::shared_ptr<DataBuffer> &data)
227 {
228 DHLOGI("%{public}s: CompressRgbaToJpeg.", DSCREEN_LOG_TAG);
229 if (inputDataSize != damage.w * damage.h * RGBA_CHROMA) {
230 return ERR_DH_SCREEN_CODEC_PARTAIL_DATA_ERROR;
231 }
232 jpeg_compress_struct cinfo;
233 jpeg_error_mgr jerr;
234 JSAMPROW row_pointer[1];
235 cinfo.err = jpeg_std_error(&jerr);
236 jpeg_create_compress(&cinfo);
237 unsigned char *outBuffer = nullptr;
238 unsigned long outSize = 0;
239 jpeg_mem_dest(&cinfo, &outBuffer, &outSize);
240 cinfo.image_width = damage.w;
241 cinfo.image_height = damage.h;
242 cinfo.input_components = RGB_CHROMA;
243 cinfo.in_color_space = JCS_RGB;
244 jpeg_set_defaults(&cinfo);
245 jpeg_set_quality(&cinfo, JPEG_QUALITY, TRUE);
246 jpeg_start_compress(&cinfo, TRUE);
247 unsigned char rgb_buffer[damage.w * RGB_CHROMA];
248 unsigned char *pB = inputData;
249 unsigned char *pG = inputData + 1;
250 unsigned char *pR = inputData + TWO;
251 while (cinfo.next_scanline < cinfo.image_height) {
252 int index = 0;
253 for (int i = 0 ; i < damage.w ; i++) {
254 rgb_buffer[index++] = *pB;
255 rgb_buffer[index++] = *pG;
256 rgb_buffer[index++] = *pR;
257 pB += RGBA_CHROMA;
258 pG += RGBA_CHROMA;
259 pR += RGBA_CHROMA;
260 }
261 row_pointer[0] = rgb_buffer;
262 (void)jpeg_write_scanlines(&cinfo, row_pointer, 1);
263 }
264 jpeg_finish_compress(&cinfo);
265 DirtyRect rect = { static_cast<uint32_t>(damage.x), static_cast<uint32_t>(damage.y), damage.w, damage.h,
266 static_cast<uint32_t>(outSize) };
267 data->AddData(static_cast<size_t>(outSize), outBuffer);
268 data->AddDirtyRect(rect);
269 jpeg_destroy_compress(&cinfo);
270 if (outBuffer != NULL) {
271 free(outBuffer);
272 outBuffer = NULL;
273 }
274 return (uint32_t)outSize;
275 }
276
DecompressJpegToNV12( size_t jpegSize, uint8_t *inputData, uint8_t *outputData, const uint32_t &outputDataSize, const DirtyRect &rect)277 void JpegImageProcessor::DecompressJpegToNV12(
278 size_t jpegSize, uint8_t *inputData, uint8_t *outputData, const uint32_t &outputDataSize, const DirtyRect &rect)
279 {
280 jpeg_decompress_struct cinfo;
281 jpeg_error_mgr jerr;
282 cinfo.err = jpeg_std_error(&jerr);
283 jpeg_create_decompress(&cinfo);
284 jpeg_mem_src(&cinfo, inputData, jpegSize);
285 (void)jpeg_read_header(&cinfo, TRUE);
286 (void)jpeg_start_decompress(&cinfo);
287 if ((cinfo.output_width != rect.width) || (cinfo.output_height != rect.height)) {
288 DHLOGE("%{public}s: JPEG image dimensions exceed DSCREEN_MAX_LEN: width = %{public}" PRIu32
289 ", height = %{public}" PRIu32, DSCREEN_LOG_TAG, cinfo.output_width, cinfo.output_height);
290 jpeg_destroy_decompress(&cinfo);
291 return;
292 }
293 if (cinfo.output_components != RGB_CHROMA) {
294 DHLOGE("%{public}s: color components is not RGB, color components = %{public}" PRIu32,
295 DSCREEN_LOG_TAG, cinfo.output_components);
296 jpeg_destroy_decompress(&cinfo);
297 return;
298 }
299 uint32_t row_stride = static_cast<uint32_t>(cinfo.output_width) * static_cast<uint32_t>(cinfo.output_components);
300 JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, row_stride, 1);
301 if (buffer == nullptr) {
302 DHLOGE("%{public}s: buffer is nullptr.", DSCREEN_LOG_TAG);
303 jpeg_destroy_decompress(&cinfo);
304 return;
305 }
306 uint32_t uvIndex = cinfo.output_width * cinfo.output_height;
307 int32_t i = 0;
308 int32_t yIndex = 0;
309 while (cinfo.output_scanline < cinfo.output_height) {
310 (void)jpeg_read_scanlines(&cinfo, buffer, 1);
311 for (unsigned int j = 0 ; j < cinfo.output_width ; j++) {
312 int32_t y = ((YR_PARAM * buffer[0][j * RGB_CHROMA] + YG_PARAM * buffer[0][j * RGB_CHROMA + 1] +
313 YB_PARAM * buffer[0][j * RGB_CHROMA + TWO] + UA_PARAM) >> MOVEBITS) + YA_PARAM;
314 int32_t u = ((UB_PARAM * buffer[0][j * RGB_CHROMA + TWO] - UR_PARAM * buffer[0][j * RGB_CHROMA] -
315 UG_PARAM * buffer[0][j * RGB_CHROMA + 1] + UA_PARAM) >> MOVEBITS) + UA_PARAM;
316 int32_t v = ((UB_PARAM * buffer[0][j * RGB_CHROMA] - VG_PARAM * buffer[0][j * RGB_CHROMA + 1] -
317 VB_PARAM * buffer[0][j * RGB_CHROMA + TWO] + UA_PARAM) >> MOVEBITS) + UA_PARAM;
318 outputData[yIndex++] = static_cast<uint8_t>((y < 0) ? 0 : (y > YUV_PARAM) ? YUV_PARAM : y);
319 if ((i % TWO == 0) && (j % TWO == 0) && (uvIndex < outputDataSize - 1)) {
320 outputData[uvIndex++] = static_cast<uint8_t>((u < 0) ? 0 : (u > YUV_PARAM) ? YUV_PARAM : u);
321 outputData[uvIndex++] = static_cast<uint8_t>((v < 0) ? 0 : (v > YUV_PARAM) ? YUV_PARAM : v);
322 }
323 }
324 ++i;
325 }
326 (void)jpeg_finish_decompress(&cinfo);
327 jpeg_destroy_decompress(&cinfo);
328 }
329 } // namespace DistributedHardware
330 } // namespace OHOS