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 * http://www.apache.org/licenses/LICENSE-2.0
7 * Unless required by applicable law or agreed to in writing, software
8 * distributed under the License is distributed on an "AS IS" BASIS,
9 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 * See the License for the specific language governing permissions and
11 * limitations under the License.
12 */
13
14 #include "codec_node.h"
15 #include <securec.h>
16 #include "camera_dump.h"
17 #include "camera_hal_hisysevent.h"
18
19 #include "node_utils.h"
20 extern "C" {
21 #include <jpeglib.h>
22 #include <transupp.h>
23 }
24
25 namespace OHOS::Camera {
26 const unsigned long long TIME_CONVERSION_NS_S = 1000000000ULL; /* ns to s */
27
CodecNode(const std::string & name,const std::string & type,const std::string & cameraId)28 CodecNode::CodecNode(const std::string& name, const std::string& type, const std::string &cameraId)
29 : NodeBase(name, type, cameraId)
30 {
31 CAMERA_LOGV("CodecNode::CodecNode, %{public}s enter, type(%{public}s)\n", name_.c_str(), type_.c_str());
32 jpegRotation_ = static_cast<uint32_t>(JXFORM_ROT_270);
33 jpegQuality_ = 100; // 100:jpeg quality
34 }
35
~CodecNode()36 CodecNode::~CodecNode()
37 {
38 CAMERA_LOGI("~CodecNode Node exit.");
39 }
40
Start(const int32_t streamId)41 RetCode CodecNode::Start(const int32_t streamId)
42 {
43 CAMERA_LOGI("CodecNode::Start streamId = %{public}d\n", streamId);
44 return RC_OK;
45 }
46
Stop(const int32_t streamId)47 RetCode CodecNode::Stop(const int32_t streamId)
48 {
49 CAMERA_LOGI("CodecNode::Stop streamId = %{public}d\n", streamId);
50 return RC_OK;
51 }
52
Flush(const int32_t streamId)53 RetCode CodecNode::Flush(const int32_t streamId)
54 {
55 CAMERA_LOGI("CodecNode::Flush streamId = %{public}d\n", streamId);
56 return RC_OK;
57 }
58
RotJpegImg(const uint8_t * inputImg,size_t inputSize,uint8_t ** outImg,size_t * outSize,JXFORM_CODE rotDegrees)59 static void RotJpegImg(
60 const uint8_t *inputImg, size_t inputSize, uint8_t **outImg, size_t *outSize, JXFORM_CODE rotDegrees)
61 {
62 struct jpeg_decompress_struct inputInfo;
63 struct jpeg_error_mgr jerrIn;
64 struct jpeg_compress_struct outInfo;
65 struct jpeg_error_mgr jerrOut;
66 jvirt_barray_ptr *src_coef_arrays;
67 jvirt_barray_ptr *dst_coef_arrays;
68
69 inputInfo.err = jpeg_std_error(&jerrIn);
70 jpeg_create_decompress(&inputInfo);
71 outInfo.err = jpeg_std_error(&jerrOut);
72 jpeg_create_compress(&outInfo);
73 jpeg_mem_src(&inputInfo, inputImg, inputSize);
74 jpeg_mem_dest(&outInfo, outImg, (unsigned long *)outSize);
75
76 JCOPY_OPTION copyoption;
77 jpeg_transform_info transformoption;
78 transformoption.transform = rotDegrees;
79 transformoption.perfect = TRUE;
80 transformoption.trim = FALSE;
81 transformoption.force_grayscale = FALSE;
82 transformoption.crop = FALSE;
83
84 jcopy_markers_setup(&inputInfo, copyoption);
85 (void)jpeg_read_header(&inputInfo, TRUE);
86
87 if (!jtransform_request_workspace(&inputInfo, &transformoption)) {
88 CAMERA_LOGE("%s: transformation is not perfect", __func__);
89 return;
90 }
91
92 src_coef_arrays = jpeg_read_coefficients(&inputInfo);
93 jpeg_copy_critical_parameters(&inputInfo, &outInfo);
94 dst_coef_arrays = jtransform_adjust_parameters(&inputInfo, &outInfo, src_coef_arrays, &transformoption);
95 jpeg_write_coefficients(&outInfo, dst_coef_arrays);
96 jcopy_markers_execute(&inputInfo, &outInfo, copyoption);
97 jtransform_execute_transformation(&inputInfo, &outInfo, src_coef_arrays, &transformoption);
98
99 jpeg_finish_compress(&outInfo);
100 jpeg_destroy_compress(&outInfo);
101 (void)jpeg_finish_decompress(&inputInfo);
102 jpeg_destroy_decompress(&inputInfo);
103 }
104
ConfigJpegOrientation(common_metadata_header_t * data)105 RetCode CodecNode::ConfigJpegOrientation(common_metadata_header_t* data)
106 {
107 camera_metadata_item_t entry;
108 int ret = FindCameraMetadataItem(data, OHOS_JPEG_ORIENTATION, &entry);
109 if (ret != 0 || entry.data.i32 == nullptr) {
110 CAMERA_LOGE("tag not found");
111 return RC_ERROR;
112 }
113
114 JXFORM_CODE jxRotation = JXFORM_ROT_270;
115 int32_t ohosRotation = *entry.data.i32;
116 if (ohosRotation == OHOS_CAMERA_JPEG_ROTATION_0) {
117 jxRotation = JXFORM_NONE;
118 } else if (ohosRotation == OHOS_CAMERA_JPEG_ROTATION_90) {
119 jxRotation = JXFORM_ROT_90;
120 } else if (ohosRotation == OHOS_CAMERA_JPEG_ROTATION_180) {
121 jxRotation = JXFORM_ROT_180;
122 } else {
123 jxRotation = JXFORM_ROT_270;
124 }
125 jpegRotation_ = static_cast<uint32_t>(jxRotation);
126 return RC_OK;
127 }
128
ConfigJpegQuality(common_metadata_header_t * data)129 RetCode CodecNode::ConfigJpegQuality(common_metadata_header_t* data)
130 {
131 camera_metadata_item_t entry;
132 int ret = FindCameraMetadataItem(data, OHOS_JPEG_QUALITY, &entry);
133 if (ret != 0) {
134 CAMERA_LOGE("tag OHOS_JPEG_QUALITY not found");
135 return RC_ERROR;
136 }
137
138 const int highQualityJpeg = 100;
139 const int middleQualityJpeg = 95;
140 const int lowQualityJpeg = 85;
141
142 CAMERA_LOGI("OHOS_JPEG_QUALITY is = %{public}d", static_cast<int>(entry.data.u8[0]));
143 if (*entry.data.i32 == OHOS_CAMERA_JPEG_LEVEL_LOW) {
144 jpegQuality_ = lowQualityJpeg;
145 } else if (*entry.data.i32 == OHOS_CAMERA_JPEG_LEVEL_MIDDLE) {
146 jpegQuality_ = middleQualityJpeg;
147 } else if (*entry.data.i32 == OHOS_CAMERA_JPEG_LEVEL_HIGH) {
148 jpegQuality_ = highQualityJpeg;
149 } else {
150 jpegQuality_ = highQualityJpeg;
151 }
152 return RC_OK;
153 }
154
Config(const int32_t streamId,const CaptureMeta & meta)155 RetCode CodecNode::Config(const int32_t streamId, const CaptureMeta& meta)
156 {
157 CAMERA_LOGD("CodecNode::Config streamid = %{public}d", streamId);
158 if (meta == nullptr) {
159 CAMERA_LOGE("meta is nullptr");
160 return RC_ERROR;
161 }
162
163 common_metadata_header_t* data = meta->get();
164 if (data == nullptr) {
165 CAMERA_LOGE("data is nullptr");
166 return RC_ERROR;
167 }
168
169 RetCode rc = ConfigJpegOrientation(data);
170 if (rc != RC_OK) {
171 CAMERA_LOGE("config jpeg orientation failed");
172 return RC_ERROR;
173 }
174
175 rc = ConfigJpegQuality(data);
176 return rc;
177 }
178
EncodeJpegToMemory(uint8_t * image,JpegData jpegData,const char * comment,unsigned long * jpegSize,uint8_t ** jpegBuf)179 void CodecNode::EncodeJpegToMemory(uint8_t* image, JpegData jpegData,
180 const char* comment, unsigned long* jpegSize, uint8_t** jpegBuf)
181 {
182 struct jpeg_compress_struct cInfo;
183 struct jpeg_error_mgr jErr;
184 JSAMPROW row_pointer[1];
185 int rowStride = 0;
186 constexpr uint32_t colorMap = 3;
187 constexpr uint32_t pixelsThick = 3;
188
189 cInfo.err = jpeg_std_error(&jErr);
190
191 jpeg_create_compress(&cInfo);
192 cInfo.image_width = jpegData.width;
193 cInfo.image_height = jpegData.height;
194 cInfo.input_components = colorMap;
195 cInfo.in_color_space = JCS_RGB;
196
197 jpeg_set_defaults(&cInfo);
198 CAMERA_LOGI("CodecNode::EncodeJpegToMemory jpegQuality_ is = %{public}d", jpegQuality_);
199 jpeg_set_quality(&cInfo, jpegQuality_, TRUE);
200 jpeg_mem_dest(&cInfo, jpegBuf, jpegSize);
201 jpeg_start_compress(&cInfo, TRUE);
202
203 if (comment) {
204 jpeg_write_marker(&cInfo, JPEG_COM, (const JOCTET*)comment, strlen(comment));
205 }
206
207 rowStride = jpegData.width;
208 while (cInfo.next_scanline < cInfo.image_height) {
209 row_pointer[0] = &image[cInfo.next_scanline * rowStride * pixelsThick];
210 jpeg_write_scanlines(&cInfo, row_pointer, 1);
211 }
212
213 jpeg_finish_compress(&cInfo);
214 jpeg_destroy_compress(&cInfo);
215
216 size_t rotJpgSize = 0;
217 uint8_t* rotJpgBuf = nullptr;
218 /* rotate image */
219 RotJpegImg(*jpegBuf, *jpegSize, &rotJpgBuf, &rotJpgSize, static_cast<JXFORM_CODE>(jpegRotation_));
220 if (rotJpgBuf != nullptr && rotJpgSize != 0) {
221 free(*jpegBuf);
222 *jpegBuf = rotJpgBuf;
223 *jpegSize = rotJpgSize;
224 }
225 }
226
Yuv422ToJpeg(std::shared_ptr<IBuffer> & buffer)227 void CodecNode::Yuv422ToJpeg(std::shared_ptr<IBuffer>& buffer)
228 {
229 CAMERA_LOGD("CodecNode::Yuv422ToJpeg begin");
230 int ret = 0;
231 constexpr uint8_t pixWidthRGB888 = 3;
232 uint32_t tmpBufferSize = buffer->GetWidth() * buffer->GetHeight() * pixWidthRGB888;
233 void* tmpBufferAddr = malloc(tmpBufferSize);
234 if (tmpBufferAddr == nullptr) {
235 CAMERA_LOGE("CodecNode::Yuv422ToJpeg fail, malloc tmpBufferAddr fail");
236 return;
237 }
238 auto oldFormat = buffer->GetCurFormat();
239 buffer->SetFormat(CAMERA_FORMAT_RGB_888);
240 NodeUtils::BufferScaleFormatTransform(buffer, tmpBufferAddr, tmpBufferSize);
241 buffer->SetFormat(oldFormat);
242
243 uint8_t* jBuf = nullptr;
244 unsigned long jpegSize = 0;
245
246 JpegData jpegdata = {buffer->GetWidth(), buffer->GetHeight()};
247 EncodeJpegToMemory((uint8_t *)tmpBufferAddr, jpegdata, nullptr, &jpegSize, &jBuf);
248
249 ret = memcpy_s((uint8_t *)buffer->GetSuffaceBufferAddr(), buffer->GetSuffaceBufferSize(), jBuf, jpegSize);
250 if (ret == 0) {
251 buffer->SetEsFrameSize(jpegSize);
252 } else {
253 CAMERA_LOGE("CodecNode::Yuv422ToJpeg memcpy_s failed 2 , ret = %{public}d\n", ret);
254 CameraHalHisysevent::WriteFaultHisysEvent(CameraHalHisysevent::GetEventName(COPY_BUFFER_ERROR),
255 CameraHalHisysevent::CreateMsg("streamId:%d Yuv422ToJpeg failed ret:%d", buffer->GetStreamId(), ret));
256 buffer->SetEsFrameSize(0);
257 }
258 CAMERA_LOGI("CodecNode::Yuv422ToJpeg jpegSize = %{public}d\n", jpegSize);
259 free(jBuf);
260 free(tmpBufferAddr);
261 buffer->SetIsValidDataInSurfaceBuffer(true);
262 }
263
DeliverBuffer(std::shared_ptr<IBuffer> & buffer)264 void CodecNode::DeliverBuffer(std::shared_ptr<IBuffer>& buffer)
265 {
266 if (buffer == nullptr) {
267 CAMERA_LOGE("CodecNode::DeliverBuffer frameSpec is null");
268 return;
269 }
270
271 if (buffer->GetBufferStatus() != CAMERA_BUFFER_STATUS_OK) {
272 CAMERA_LOGE("BufferStatus() != CAMERA_BUFFER_STATUS_OK");
273 return NodeBase::DeliverBuffer(buffer);
274 }
275
276 int32_t id = buffer->GetStreamId();
277 CAMERA_LOGI("CodecNode::DeliverBuffer, streamId[%{public}d], index[%{public}d],\
278 format = %{public}d, encode = %{public}d",
279 id, buffer->GetIndex(), buffer->GetFormat(), buffer->GetEncodeType());
280
281 if (buffer->GetEncodeType() == ENCODE_TYPE_JPEG) {
282 Yuv422ToJpeg(buffer);
283 } else {
284 NodeUtils::BufferScaleFormatTransform(buffer);
285 }
286
287 if (buffer->GetEncodeType() == ENCODE_TYPE_H264) {
288 struct timespec ts = {};
289 int64_t timestamp = 0;
290 clock_gettime(CLOCK_MONOTONIC, &ts);
291 timestamp = ts.tv_nsec + ts.tv_sec * TIME_CONVERSION_NS_S;
292 buffer->SetEsTimestamp(timestamp);
293 buffer->SetEsFrameSize(buffer->GetSuffaceBufferSize());
294 buffer->SetEsKeyFrame(0);
295 }
296
297 CameraDumper& dumper = CameraDumper::GetInstance();
298 dumper.DumpBuffer("CodecNode", ENABLE_CODEC_NODE_CONVERTED, buffer);
299
300 NodeBase::DeliverBuffer(buffer);
301 }
302
Capture(const int32_t streamId,const int32_t captureId)303 RetCode CodecNode::Capture(const int32_t streamId, const int32_t captureId)
304 {
305 CAMERA_LOGV("CodecNode::Capture streamid = %{public}d and captureId = %{public}d", streamId, captureId);
306 return RC_OK;
307 }
308
CancelCapture(const int32_t streamId)309 RetCode CodecNode::CancelCapture(const int32_t streamId)
310 {
311 CAMERA_LOGI("CodecNode::CancelCapture streamid = %{public}d", streamId);
312
313 return RC_OK;
314 }
315
316 REGISTERNODE(CodecNode, {"Codec"})
317 } // namespace OHOS::Camera
318