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