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