1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "JpegCompressor"
19
20 #include "JpegCompressor.h"
21
22 #include <camera_blob.h>
23 #include <cutils/properties.h>
24 #include <libyuv.h>
25 #include <ultrahdr/jpegr.h>
26 #include <utils/Log.h>
27 #include <utils/Trace.h>
28
29 namespace android {
30
31 using google_camera_hal::CameraBlob;
32 using google_camera_hal::CameraBlobId;
33 using google_camera_hal::ErrorCode;
34 using google_camera_hal::MessageType;
35 using google_camera_hal::NotifyMessage;
36
37 // All ICC profile data sourced from https://github.com/saucecontrol/Compact-ICC-Profiles
38 static constexpr uint8_t kIccProfileDisplayP3[] = {
39 0x00, 0x00, 0x01, 0xe0, 0x6c, 0x63, 0x6d, 0x73, 0x04, 0x20, 0x00, 0x00,
40 0x6d, 0x6e, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59, 0x5a, 0x20,
41 0x07, 0xe2, 0x00, 0x03, 0x00, 0x14, 0x00, 0x09, 0x00, 0x0e, 0x00, 0x1d,
42 0x61, 0x63, 0x73, 0x70, 0x4d, 0x53, 0x46, 0x54, 0x00, 0x00, 0x00, 0x00,
43 0x73, 0x61, 0x77, 0x73, 0x63, 0x74, 0x72, 0x6c, 0x00, 0x00, 0x00, 0x00,
44 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xd6,
45 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d, 0x68, 0x61, 0x6e, 0x64,
46 0x51, 0xb1, 0x0e, 0x57, 0x9c, 0x0c, 0x00, 0x19, 0x38, 0xb9, 0x93, 0x88,
47 0x06, 0x61, 0xb8, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
48 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
49 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a,
50 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x22,
51 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x22,
52 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x01, 0x44, 0x00, 0x00, 0x00, 0x14,
53 0x63, 0x68, 0x61, 0x64, 0x00, 0x00, 0x01, 0x58, 0x00, 0x00, 0x00, 0x2c,
54 0x72, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x01, 0x84, 0x00, 0x00, 0x00, 0x14,
55 0x67, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x01, 0x98, 0x00, 0x00, 0x00, 0x14,
56 0x62, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x01, 0xac, 0x00, 0x00, 0x00, 0x14,
57 0x72, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x00, 0x00, 0x20,
58 0x67, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x00, 0x00, 0x20,
59 0x62, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x00, 0x00, 0x20,
60 0x6d, 0x6c, 0x75, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
61 0x00, 0x00, 0x00, 0x0c, 0x65, 0x6e, 0x55, 0x53, 0x00, 0x00, 0x00, 0x06,
62 0x00, 0x00, 0x00, 0x1c, 0x00, 0x73, 0x00, 0x50, 0x00, 0x33, 0x00, 0x00,
63 0x6d, 0x6c, 0x75, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
64 0x00, 0x00, 0x00, 0x0c, 0x65, 0x6e, 0x55, 0x53, 0x00, 0x00, 0x00, 0x06,
65 0x00, 0x00, 0x00, 0x1c, 0x00, 0x43, 0x00, 0x43, 0x00, 0x30, 0x00, 0x21,
66 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xd6,
67 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d, 0x73, 0x66, 0x33, 0x32,
68 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0c, 0x42, 0x00, 0x00, 0x05, 0xde,
69 0xff, 0xff, 0xf3, 0x25, 0x00, 0x00, 0x07, 0x93, 0x00, 0x00, 0xfd, 0x90,
70 0xff, 0xff, 0xfb, 0xa1, 0xff, 0xff, 0xfd, 0xa2, 0x00, 0x00, 0x03, 0xdc,
71 0x00, 0x00, 0xc0, 0x6e, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00,
72 0x00, 0x00, 0x83, 0xdf, 0x00, 0x00, 0x3d, 0xbf, 0xff, 0xff, 0xff, 0xbb,
73 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0xbf,
74 0x00, 0x00, 0xb1, 0x37, 0x00, 0x00, 0x0a, 0xb9, 0x58, 0x59, 0x5a, 0x20,
75 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x38, 0x00, 0x00, 0x11, 0x0a,
76 0x00, 0x00, 0xc8, 0xb9, 0x70, 0x61, 0x72, 0x61, 0x00, 0x00, 0x00, 0x00,
77 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x66, 0x69, 0x00, 0x00, 0xf2, 0xa7,
78 0x00, 0x00, 0x0d, 0x59, 0x00, 0x00, 0x13, 0xd0, 0x00, 0x00, 0x0a, 0x5b};
79
80 static constexpr uint8_t kIccProfileDciP3[] = {
81 0x00, 0x00, 0x01, 0xd0, 0x6c, 0x63, 0x6d, 0x73, 0x04, 0x20, 0x00, 0x00,
82 0x6d, 0x6e, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59, 0x5a, 0x20,
83 0x07, 0xe5, 0x00, 0x04, 0x00, 0x1b, 0x00, 0x0a, 0x00, 0x1b, 0x00, 0x00,
84 0x61, 0x63, 0x73, 0x70, 0x4d, 0x53, 0x46, 0x54, 0x00, 0x00, 0x00, 0x00,
85 0x73, 0x61, 0x77, 0x73, 0x63, 0x74, 0x72, 0x6c, 0x00, 0x00, 0x00, 0x00,
86 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xd6,
87 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d, 0x68, 0x61, 0x6e, 0x64,
88 0x79, 0xb3, 0x51, 0x32, 0xc4, 0xd7, 0x5b, 0x84, 0xf1, 0xbb, 0xcb, 0x58,
89 0x53, 0xb0, 0xfa, 0xf6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
90 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
91 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a,
92 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x22,
93 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x22,
94 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x01, 0x44, 0x00, 0x00, 0x00, 0x14,
95 0x63, 0x68, 0x61, 0x64, 0x00, 0x00, 0x01, 0x58, 0x00, 0x00, 0x00, 0x2c,
96 0x72, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x01, 0x84, 0x00, 0x00, 0x00, 0x14,
97 0x67, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x01, 0x98, 0x00, 0x00, 0x00, 0x14,
98 0x62, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x01, 0xac, 0x00, 0x00, 0x00, 0x14,
99 0x72, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x00, 0x00, 0x10,
100 0x67, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x00, 0x00, 0x10,
101 0x62, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x00, 0x00, 0x10,
102 0x6d, 0x6c, 0x75, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
103 0x00, 0x00, 0x00, 0x0c, 0x65, 0x6e, 0x55, 0x53, 0x00, 0x00, 0x00, 0x06,
104 0x00, 0x00, 0x00, 0x1c, 0x00, 0x54, 0x00, 0x50, 0x00, 0x33, 0x00, 0x00,
105 0x6d, 0x6c, 0x75, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
106 0x00, 0x00, 0x00, 0x0c, 0x65, 0x6e, 0x55, 0x53, 0x00, 0x00, 0x00, 0x06,
107 0x00, 0x00, 0x00, 0x1c, 0x00, 0x43, 0x00, 0x43, 0x00, 0x30, 0x00, 0x21,
108 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xd6,
109 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d, 0x73, 0x66, 0x33, 0x32,
110 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x12, 0xe6, 0x00, 0x00, 0x09, 0xef,
111 0xff, 0xff, 0xf6, 0x8d, 0x00, 0x00, 0x0e, 0x3a, 0x00, 0x00, 0xf6, 0xc8,
112 0xff, 0xff, 0xfc, 0x53, 0xff, 0xff, 0xfe, 0xe7, 0x00, 0x00, 0x01, 0x5b,
113 0x00, 0x00, 0xdc, 0xdf, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00,
114 0x00, 0x00, 0x7c, 0x75, 0x00, 0x00, 0x3a, 0x08, 0xff, 0xff, 0xff, 0xcc,
115 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0xe8,
116 0x00, 0x00, 0xb5, 0xd8, 0x00, 0x00, 0x0b, 0x11, 0x58, 0x59, 0x5a, 0x20,
117 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x79, 0x00, 0x00, 0x10, 0x20,
118 0x00, 0x00, 0xc8, 0x50, 0x70, 0x61, 0x72, 0x61, 0x00, 0x00, 0x00, 0x00,
119 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x99, 0x9a};
120
121 static constexpr uint8_t kIccProfileSrgb[] = {
122 0x00, 0x00, 0x01, 0xe0, 0x6c, 0x63, 0x6d, 0x73, 0x04, 0x20, 0x00, 0x00,
123 0x6d, 0x6e, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59, 0x5a, 0x20,
124 0x07, 0xe2, 0x00, 0x03, 0x00, 0x14, 0x00, 0x09, 0x00, 0x0e, 0x00, 0x1d,
125 0x61, 0x63, 0x73, 0x70, 0x4d, 0x53, 0x46, 0x54, 0x00, 0x00, 0x00, 0x00,
126 0x73, 0x61, 0x77, 0x73, 0x63, 0x74, 0x72, 0x6c, 0x00, 0x00, 0x00, 0x00,
127 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xd6,
128 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d, 0x68, 0x61, 0x6e, 0x64,
129 0xa3, 0xb2, 0xab, 0xdf, 0x5c, 0xa7, 0x03, 0x12, 0xa8, 0x55, 0xa4, 0xec,
130 0x35, 0x7a, 0xd1, 0xf3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
131 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
132 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a,
133 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x24,
134 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x22,
135 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x01, 0x44, 0x00, 0x00, 0x00, 0x14,
136 0x63, 0x68, 0x61, 0x64, 0x00, 0x00, 0x01, 0x58, 0x00, 0x00, 0x00, 0x2c,
137 0x72, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x01, 0x84, 0x00, 0x00, 0x00, 0x14,
138 0x67, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x01, 0x98, 0x00, 0x00, 0x00, 0x14,
139 0x62, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x01, 0xac, 0x00, 0x00, 0x00, 0x14,
140 0x72, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x00, 0x00, 0x20,
141 0x67, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x00, 0x00, 0x20,
142 0x62, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x00, 0x00, 0x20,
143 0x6d, 0x6c, 0x75, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
144 0x00, 0x00, 0x00, 0x0c, 0x65, 0x6e, 0x55, 0x53, 0x00, 0x00, 0x00, 0x08,
145 0x00, 0x00, 0x00, 0x1c, 0x00, 0x73, 0x00, 0x52, 0x00, 0x47, 0x00, 0x42,
146 0x6d, 0x6c, 0x75, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
147 0x00, 0x00, 0x00, 0x0c, 0x65, 0x6e, 0x55, 0x53, 0x00, 0x00, 0x00, 0x06,
148 0x00, 0x00, 0x00, 0x1c, 0x00, 0x43, 0x00, 0x43, 0x00, 0x30, 0x00, 0x21,
149 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf6, 0xd6,
150 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d, 0x73, 0x66, 0x33, 0x32,
151 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0c, 0x3f, 0x00, 0x00, 0x05, 0xdd,
152 0xff, 0xff, 0xf3, 0x26, 0x00, 0x00, 0x07, 0x90, 0x00, 0x00, 0xfd, 0x92,
153 0xff, 0xff, 0xfb, 0xa1, 0xff, 0xff, 0xfd, 0xa2, 0x00, 0x00, 0x03, 0xdc,
154 0x00, 0x00, 0xc0, 0x71, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00,
155 0x00, 0x00, 0x6f, 0xa0, 0x00, 0x00, 0x38, 0xf2, 0x00, 0x00, 0x03, 0x8f,
156 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x96,
157 0x00, 0x00, 0xb7, 0x89, 0x00, 0x00, 0x18, 0xda, 0x58, 0x59, 0x5a, 0x20,
158 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa0, 0x00, 0x00, 0x0f, 0x85,
159 0x00, 0x00, 0xb6, 0xc4, 0x70, 0x61, 0x72, 0x61, 0x00, 0x00, 0x00, 0x00,
160 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x66, 0x69, 0x00, 0x00, 0xf2, 0xa7,
161 0x00, 0x00, 0x0d, 0x59, 0x00, 0x00, 0x13, 0xd0, 0x00, 0x00, 0x0a, 0x5b};
162
JpegCompressor()163 JpegCompressor::JpegCompressor() {
164 ATRACE_CALL();
165 char value[PROPERTY_VALUE_MAX];
166 if (property_get("ro.product.manufacturer", value, "unknown") <= 0) {
167 ALOGW("%s: No Exif make data!", __FUNCTION__);
168 }
169 exif_make_ = std::string(value);
170
171 if (property_get("ro.product.model", value, "unknown") <= 0) {
172 ALOGW("%s: No Exif model data!", __FUNCTION__);
173 }
174 exif_model_ = std::string(value);
175
176 jpeg_processing_thread_ = std::thread([this] { this->ThreadLoop(); });
177 }
178
~JpegCompressor()179 JpegCompressor::~JpegCompressor() {
180 ATRACE_CALL();
181
182 // Abort the ongoing compression and flush any pending jobs
183 jpeg_done_ = true;
184 condition_.notify_one();
185 jpeg_processing_thread_.join();
186 while (!pending_yuv_jobs_.empty()) {
187 auto job = std::move(pending_yuv_jobs_.front());
188 job->output->stream_buffer.status = BufferStatus::kError;
189 pending_yuv_jobs_.pop();
190 }
191 }
192
QueueYUV420(std::unique_ptr<JpegYUV420Job> job)193 status_t JpegCompressor::QueueYUV420(std::unique_ptr<JpegYUV420Job> job) {
194 ATRACE_CALL();
195
196 if ((job->input.get() == nullptr) || (job->output.get() == nullptr) ||
197 (job->output->format != PixelFormat::BLOB) ||
198 ((job->output->dataSpace !=
199 static_cast<android_dataspace_t>(
200 ::aidl::android::hardware::graphics::common::Dataspace::JPEG_R)) &&
201 (job->output->dataSpace != HAL_DATASPACE_V0_JFIF))) {
202 ALOGE("%s: Unable to find buffers for JPEG source/destination",
203 __FUNCTION__);
204 return BAD_VALUE;
205 }
206
207 std::unique_lock<std::mutex> lock(mutex_);
208 pending_yuv_jobs_.push(std::move(job));
209 condition_.notify_one();
210
211 return OK;
212 }
213
ThreadLoop()214 void JpegCompressor::ThreadLoop() {
215 ATRACE_CALL();
216
217 while (!jpeg_done_) {
218 std::unique_ptr<JpegYUV420Job> current_yuv_job = nullptr;
219 {
220 std::lock_guard<std::mutex> lock(mutex_);
221 if (!pending_yuv_jobs_.empty()) {
222 current_yuv_job = std::move(pending_yuv_jobs_.front());
223 pending_yuv_jobs_.pop();
224 }
225 }
226
227 if (current_yuv_job.get() != nullptr) {
228 CompressYUV420(std::move(current_yuv_job));
229 }
230
231 std::unique_lock<std::mutex> lock(mutex_);
232 auto ret = condition_.wait_for(lock, std::chrono::milliseconds(10));
233 if (ret == std::cv_status::timeout) {
234 ALOGV("%s: Jpeg thread timeout", __FUNCTION__);
235 }
236 }
237 }
238
CompressYUV420(std::unique_ptr<JpegYUV420Job> job)239 void JpegCompressor::CompressYUV420(std::unique_ptr<JpegYUV420Job> job) {
240 const uint8_t* app1_buffer = nullptr;
241 size_t app1_buffer_size = 0;
242 std::vector<uint8_t> thumbnail_jpeg_buffer;
243 size_t encoded_thumbnail_size = 0;
244 if ((job->exif_utils.get() != nullptr) &&
245 (job->result_metadata.get() != nullptr)) {
246 if (job->exif_utils->Initialize()) {
247 camera_metadata_ro_entry_t entry;
248 size_t thumbnail_width = 0;
249 size_t thumbnail_height = 0;
250 std::vector<uint8_t> thumb_yuv420_frame;
251 YCbCrPlanes thumb_planes;
252 auto ret = job->result_metadata->Get(ANDROID_JPEG_THUMBNAIL_SIZE, &entry);
253 if ((ret == OK) && (entry.count == 2)) {
254 thumbnail_width = entry.data.i32[0];
255 thumbnail_height = entry.data.i32[1];
256 if ((thumbnail_width > 0) && (thumbnail_height > 0)) {
257 thumb_yuv420_frame.resize((thumbnail_width * thumbnail_height * 3) /
258 2);
259 thumb_planes = {
260 .img_y = thumb_yuv420_frame.data(),
261 .img_cb = thumb_yuv420_frame.data() +
262 thumbnail_width * thumbnail_height,
263 .img_cr = thumb_yuv420_frame.data() +
264 (thumbnail_width * thumbnail_height * 5) / 4,
265 .y_stride = static_cast<uint32_t>(thumbnail_width),
266 .cbcr_stride = static_cast<uint32_t>(thumbnail_width) / 2};
267 // TODO: Crop thumbnail according to documentation
268 auto stat = I420Scale(
269 job->input->yuv_planes.img_y, job->input->yuv_planes.y_stride,
270 job->input->yuv_planes.img_cb, job->input->yuv_planes.cbcr_stride,
271 job->input->yuv_planes.img_cr, job->input->yuv_planes.cbcr_stride,
272 job->input->width, job->input->height, thumb_planes.img_y,
273 thumb_planes.y_stride, thumb_planes.img_cb,
274 thumb_planes.cbcr_stride, thumb_planes.img_cr,
275 thumb_planes.cbcr_stride, thumbnail_width, thumbnail_height,
276 libyuv::kFilterNone);
277 if (stat != 0) {
278 ALOGE("%s: Failed during thumbnail scaling: %d", __FUNCTION__, stat);
279 thumb_yuv420_frame.clear();
280 }
281 }
282 }
283
284 if (job->exif_utils->SetFromMetadata(
285 *job->result_metadata, job->input->width, job->input->height)) {
286 if (!thumb_yuv420_frame.empty()) {
287 thumbnail_jpeg_buffer.resize(64 * 1024); // APP1 is limited by 64k
288 encoded_thumbnail_size = CompressYUV420Frame(
289 {.output_buffer = thumbnail_jpeg_buffer.data(),
290 .output_buffer_size = thumbnail_jpeg_buffer.size(),
291 .yuv_planes = thumb_planes,
292 .width = thumbnail_width,
293 .height = thumbnail_height,
294 .app1_buffer = nullptr,
295 .app1_buffer_size = 0,
296 .color_space = job->input->color_space});
297 if (encoded_thumbnail_size > 0) {
298 job->output->stream_buffer.status = BufferStatus::kOk;
299 } else {
300 ALOGE("%s: Failed encoding thumbail!", __FUNCTION__);
301 thumbnail_jpeg_buffer.clear();
302 }
303 }
304
305 job->exif_utils->SetMake(exif_make_);
306 job->exif_utils->SetModel(exif_model_);
307 job->exif_utils->SetColorSpace(COLOR_SPACE_ICC_PROFILE);
308 if (job->exif_utils->GenerateApp1(thumbnail_jpeg_buffer.empty()
309 ? nullptr
310 : thumbnail_jpeg_buffer.data(),
311 encoded_thumbnail_size)) {
312 app1_buffer = job->exif_utils->GetApp1Buffer();
313 app1_buffer_size = job->exif_utils->GetApp1Length();
314 } else {
315 ALOGE("%s: Unable to generate App1 buffer", __FUNCTION__);
316 }
317 } else {
318 ALOGE("%s: Unable to generate EXIF section!", __FUNCTION__);
319 }
320 } else {
321 ALOGE("%s: Unable to initialize Exif generator!", __FUNCTION__);
322 }
323 }
324
325 size_t encoded_size = 0;
326 YUV420Frame frame = {.output_buffer = job->output->plane.img.img,
327 .output_buffer_size = job->output->plane.img.buffer_size,
328 .yuv_planes = job->input->yuv_planes,
329 .width = job->input->width,
330 .height = job->input->height,
331 .app1_buffer = app1_buffer,
332 .app1_buffer_size = app1_buffer_size,
333 .color_space = job->input->color_space};
334 if (job->output->dataSpace ==
335 static_cast<android_dataspace_t>(
336 ::aidl::android::hardware::graphics::common::Dataspace::JPEG_R)) {
337 encoded_size = JpegRCompressYUV420Frame(frame);
338 } else {
339 encoded_size = CompressYUV420Frame(frame);
340 }
341 if (encoded_size > 0) {
342 job->output->stream_buffer.status = BufferStatus::kOk;
343 } else {
344 job->output->stream_buffer.status = BufferStatus::kError;
345 return;
346 }
347
348 auto jpeg_header_offset =
349 job->output->plane.img.buffer_size - sizeof(struct CameraBlob);
350 if (jpeg_header_offset > encoded_size) {
351 struct CameraBlob* blob = reinterpret_cast<struct CameraBlob*>(
352 job->output->plane.img.img + jpeg_header_offset);
353 blob->blob_id = CameraBlobId::JPEG;
354 blob->blob_size = encoded_size;
355 } else {
356 ALOGW("%s: No space for jpeg header at offset: %u and jpeg size: %u",
357 __FUNCTION__, static_cast<unsigned>(jpeg_header_offset),
358 static_cast<unsigned>(encoded_size));
359 }
360 }
361
JpegRCompressYUV420Frame(YUV420Frame p010_frame)362 size_t JpegCompressor::JpegRCompressYUV420Frame(YUV420Frame p010_frame) {
363 ATRACE_CALL();
364
365 ultrahdr::jpegr_uncompressed_struct p010;
366 ultrahdr::jpegr_compressed_struct jpeg_r;
367 ultrahdr::JpegR jpeg_r_encoder;
368
369 p010.height = p010_frame.height;
370 p010.width = p010_frame.width;
371 p010.colorGamut = ultrahdr::ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
372 p010.data = p010_frame.yuv_planes.img_y;
373 p010.chroma_data = p010_frame.yuv_planes.img_cb;
374 // Strides are expected to be in pixels not bytes
375 p010.luma_stride = p010_frame.yuv_planes.y_stride / 2;
376 p010.chroma_stride = p010_frame.yuv_planes.cbcr_stride / 2;
377
378 jpeg_r.data = p010_frame.output_buffer;
379 jpeg_r.maxLength = p010_frame.output_buffer_size;
380
381 ultrahdr::ultrahdr_transfer_function transferFunction =
382 ultrahdr::ultrahdr_transfer_function::ULTRAHDR_TF_HLG;
383
384 ultrahdr::jpegr_exif_struct exif;
385 exif.data =
386 reinterpret_cast<void*>(const_cast<uint8_t*>(p010_frame.app1_buffer));
387 exif.length = p010_frame.app1_buffer_size;
388
389 auto res = jpeg_r_encoder.encodeJPEGR(&p010, transferFunction, &jpeg_r,
390 /*jpegQuality*/ 100, &exif);
391 if (res != OK) {
392 ALOGE("%s: Error trying to encode JPEG/R: %s (%d)", __FUNCTION__,
393 strerror(-res), res);
394 return 0;
395 }
396
397 return jpeg_r.length;
398 }
399
CompressYUV420Frame(YUV420Frame frame)400 size_t JpegCompressor::CompressYUV420Frame(YUV420Frame frame) {
401 ATRACE_CALL();
402
403 struct CustomJpegDestMgr : public jpeg_destination_mgr {
404 JOCTET* buffer;
405 size_t buffer_size;
406 size_t encoded_size;
407 bool success;
408 } dmgr;
409
410 // Set up error management
411 jpeg_error_info_ = NULL;
412 jpeg_error_mgr jerr;
413
414 auto cinfo = std::make_unique<jpeg_compress_struct>();
415 cinfo->err = jpeg_std_error(&jerr);
416 cinfo->err->error_exit = [](j_common_ptr cinfo) {
417 (*cinfo->err->output_message)(cinfo);
418 if (cinfo->client_data) {
419 auto& dmgr = *static_cast<CustomJpegDestMgr*>(cinfo->client_data);
420 dmgr.success = false;
421 }
422 };
423
424 jpeg_create_compress(cinfo.get());
425 if (CheckError("Error initializing compression")) {
426 return 0;
427 }
428
429 dmgr.buffer = static_cast<JOCTET*>(frame.output_buffer);
430 dmgr.buffer_size = frame.output_buffer_size;
431 dmgr.encoded_size = 0;
432 dmgr.success = true;
433 cinfo->client_data = static_cast<void*>(&dmgr);
434 dmgr.init_destination = [](j_compress_ptr cinfo) {
435 auto& dmgr = static_cast<CustomJpegDestMgr&>(*cinfo->dest);
436 dmgr.next_output_byte = dmgr.buffer;
437 dmgr.free_in_buffer = dmgr.buffer_size;
438 ALOGV("%s:%d jpeg start: %p [%zu]", __FUNCTION__, __LINE__, dmgr.buffer,
439 dmgr.buffer_size);
440 };
441
442 dmgr.empty_output_buffer = [](j_compress_ptr) {
443 ALOGE("%s:%d Out of buffer", __FUNCTION__, __LINE__);
444 return 0;
445 };
446
447 dmgr.term_destination = [](j_compress_ptr cinfo) {
448 auto& dmgr = static_cast<CustomJpegDestMgr&>(*cinfo->dest);
449 dmgr.encoded_size = dmgr.buffer_size - dmgr.free_in_buffer;
450 ALOGV("%s:%d Done with jpeg: %zu", __FUNCTION__, __LINE__,
451 dmgr.encoded_size);
452 };
453
454 cinfo->dest = reinterpret_cast<struct jpeg_destination_mgr*>(&dmgr);
455
456 // Set up compression parameters
457 cinfo->image_width = frame.width;
458 cinfo->image_height = frame.height;
459 cinfo->input_components = 3;
460 cinfo->in_color_space = JCS_YCbCr;
461
462 jpeg_set_defaults(cinfo.get());
463 if (CheckError("Error configuring defaults")) {
464 return 0;
465 }
466
467 jpeg_set_colorspace(cinfo.get(), JCS_YCbCr);
468 if (CheckError("Error configuring color space")) {
469 return 0;
470 }
471
472 cinfo->raw_data_in = 1;
473 // YUV420 planar with chroma subsampling
474 cinfo->comp_info[0].h_samp_factor = 2;
475 cinfo->comp_info[0].v_samp_factor = 2;
476 cinfo->comp_info[1].h_samp_factor = 1;
477 cinfo->comp_info[1].v_samp_factor = 1;
478 cinfo->comp_info[2].h_samp_factor = 1;
479 cinfo->comp_info[2].v_samp_factor = 1;
480
481 int max_vsamp_factor = std::max({cinfo->comp_info[0].v_samp_factor,
482 cinfo->comp_info[1].v_samp_factor,
483 cinfo->comp_info[2].v_samp_factor});
484 int c_vsub_sampling =
485 cinfo->comp_info[0].v_samp_factor / cinfo->comp_info[1].v_samp_factor;
486
487 // Start compression
488 jpeg_start_compress(cinfo.get(), TRUE);
489 if (CheckError("Error starting compression")) {
490 return 0;
491 }
492
493 if ((frame.app1_buffer != nullptr) && (frame.app1_buffer_size > 0)) {
494 jpeg_write_marker(cinfo.get(), JPEG_APP0 + 1,
495 static_cast<const JOCTET*>(frame.app1_buffer),
496 frame.app1_buffer_size);
497 }
498
499 const uint8_t* icc_profile = nullptr;
500 size_t icc_profile_size = 0;
501 switch (frame.color_space) {
502 case 0: // sRGB
503 icc_profile = kIccProfileSrgb;
504 icc_profile_size = std::size(kIccProfileSrgb);
505 break;
506 case 7: // DISPLAY_P3
507 icc_profile = kIccProfileDisplayP3;
508 icc_profile_size = std::size(kIccProfileDisplayP3);
509 break;
510 case 6: // DCI_P3
511 icc_profile = kIccProfileDciP3;
512 icc_profile_size = std::size(kIccProfileDciP3);
513 break;
514 }
515
516 if (icc_profile != nullptr && icc_profile_size > 0) {
517 jpeg_write_icc_profile(cinfo.get(), static_cast<const JOCTET*>(icc_profile),
518 icc_profile_size);
519 }
520
521 // Compute our macroblock height, so we can pad our input to be vertically
522 // macroblock aligned.
523
524 size_t mcu_v = DCTSIZE * max_vsamp_factor;
525 size_t padded_height = mcu_v * ((cinfo->image_height + mcu_v - 1) / mcu_v);
526
527 std::vector<JSAMPROW> y_lines(padded_height);
528 std::vector<JSAMPROW> cb_lines(padded_height / c_vsub_sampling);
529 std::vector<JSAMPROW> cr_lines(padded_height / c_vsub_sampling);
530
531 uint8_t* py = static_cast<uint8_t*>(frame.yuv_planes.img_y);
532 uint8_t* pcr = static_cast<uint8_t*>(frame.yuv_planes.img_cr);
533 uint8_t* pcb = static_cast<uint8_t*>(frame.yuv_planes.img_cb);
534
535 for (uint32_t i = 0; i < padded_height; i++) {
536 /* Once we are in the padding territory we still point to the last line
537 * effectively replicating it several times ~ CLAMP_TO_EDGE */
538 int li = std::min(i, cinfo->image_height - 1);
539 y_lines[i] = static_cast<JSAMPROW>(py + li * frame.yuv_planes.y_stride);
540 if (i < padded_height / c_vsub_sampling) {
541 li = std::min(i, (cinfo->image_height - 1) / c_vsub_sampling);
542 cr_lines[i] =
543 static_cast<JSAMPROW>(pcr + li * frame.yuv_planes.cbcr_stride);
544 cb_lines[i] =
545 static_cast<JSAMPROW>(pcb + li * frame.yuv_planes.cbcr_stride);
546 }
547 }
548
549 const uint32_t batch_size = DCTSIZE * max_vsamp_factor;
550 while (cinfo->next_scanline < cinfo->image_height) {
551 JSAMPARRAY planes[3]{&y_lines[cinfo->next_scanline],
552 &cb_lines[cinfo->next_scanline / c_vsub_sampling],
553 &cr_lines[cinfo->next_scanline / c_vsub_sampling]};
554
555 jpeg_write_raw_data(cinfo.get(), planes, batch_size);
556 if (CheckError("Error while compressing")) {
557 return 0;
558 }
559
560 if (jpeg_done_) {
561 ALOGV("%s: Cancel called, exiting early", __FUNCTION__);
562 jpeg_finish_compress(cinfo.get());
563 return 0;
564 }
565 }
566
567 jpeg_finish_compress(cinfo.get());
568 if (CheckError("Error while finishing compression")) {
569 return 0;
570 }
571
572 return dmgr.encoded_size;
573 }
574
CheckError(const char * msg)575 bool JpegCompressor::CheckError(const char* msg) {
576 if (jpeg_error_info_) {
577 char err_buffer[JMSG_LENGTH_MAX];
578 jpeg_error_info_->err->format_message(jpeg_error_info_, err_buffer);
579 ALOGE("%s: %s: %s", __FUNCTION__, msg, err_buffer);
580 jpeg_error_info_ = NULL;
581 return true;
582 }
583
584 return false;
585 }
586
587 } // namespace android
588