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