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