• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "Test_NV12Compressor"
19 
20 #include "NV12Compressor.h"
21 
22 #include <libexif/exif-data.h>
23 #include <netinet/in.h>
24 
25 using namespace android;
26 using namespace android::camera3;
27 
28 namespace std {
29 template <>
30 struct default_delete<ExifEntry> {
operator ()std::default_delete31     inline void operator()(ExifEntry* entry) const { exif_entry_unref(entry); }
32 };
33 
34 template <>
35 struct default_delete<ExifData> {
operator ()std::default_delete36     inline void operator()(ExifData* data) const { exif_data_unref(data); }
37 };
38 
39 }  // namespace std
40 
compress(const unsigned char * data,int width,int height,int quality)41 bool NV12Compressor::compress(const unsigned char* data, int width, int height, int quality) {
42     if (!configureCompressor(width, height, quality)) {
43         // the method will have logged a more detailed error message than we can
44         // provide here so just return.
45         return false;
46     }
47 
48     return compressData(data, /*exifData*/ nullptr);
49 }
50 
compressWithExifOrientation(const unsigned char * data,int width,int height,int quality,android::camera3::ExifOrientation exifValue)51 bool NV12Compressor::compressWithExifOrientation(const unsigned char* data, int width, int height,
52         int quality, android::camera3::ExifOrientation exifValue) {
53     std::unique_ptr<ExifData> exifData(exif_data_new());
54     if (exifData.get() == nullptr) {
55         return false;
56     }
57 
58     exif_data_set_option(exifData.get(), EXIF_DATA_OPTION_FOLLOW_SPECIFICATION);
59     exif_data_set_data_type(exifData.get(), EXIF_DATA_TYPE_COMPRESSED);
60     exif_data_set_byte_order(exifData.get(), EXIF_BYTE_ORDER_INTEL);
61     std::unique_ptr<ExifEntry> exifEntry(exif_entry_new());
62     if (exifEntry.get() ==  nullptr) {
63         return false;
64     }
65 
66     exifEntry->tag = EXIF_TAG_ORIENTATION;
67     exif_content_add_entry(exifData->ifd[EXIF_IFD_0], exifEntry.get());
68     exif_entry_initialize(exifEntry.get(), exifEntry->tag);
69     exif_set_short(exifEntry->data, EXIF_BYTE_ORDER_INTEL, exifValue);
70 
71     if (!configureCompressor(width, height, quality)) {
72         return false;
73     }
74 
75     return compressData(data, exifData.get());
76 }
77 
getCompressedData() const78 const std::vector<uint8_t>& NV12Compressor::getCompressedData() const {
79     return mDestManager.mBuffer;
80 }
81 
configureCompressor(int width,int height,int quality)82 bool NV12Compressor::configureCompressor(int width, int height, int quality) {
83     mCompressInfo.err = jpeg_std_error(&mErrorManager);
84     // NOTE! DANGER! Do not construct any non-trivial objects below setjmp!
85     // The compiler will not generate code to destroy them during the return
86     // below so they will leak. Additionally, do not place any calls to libjpeg
87     // that can fail above this line or any error will cause undefined behavior.
88     if (setjmp(mErrorManager.mJumpBuffer)) {
89         // This is where the error handler will jump in case setup fails
90         // The error manager will ALOG an appropriate error message
91         return false;
92     }
93 
94     jpeg_create_compress(&mCompressInfo);
95 
96     mCompressInfo.image_width = width;
97     mCompressInfo.image_height = height;
98     mCompressInfo.input_components = 3;
99     mCompressInfo.in_color_space = JCS_YCbCr;
100     jpeg_set_defaults(&mCompressInfo);
101 
102     jpeg_set_quality(&mCompressInfo, quality, TRUE);
103     // It may seem weird to set color space here again but this will also set
104     // other fields. These fields might be overwritten by jpeg_set_defaults
105     jpeg_set_colorspace(&mCompressInfo, JCS_YCbCr);
106     mCompressInfo.raw_data_in = TRUE;
107     mCompressInfo.dct_method = JDCT_IFAST;
108     // Set sampling factors
109     mCompressInfo.comp_info[0].h_samp_factor = 2;
110     mCompressInfo.comp_info[0].v_samp_factor = 2;
111     mCompressInfo.comp_info[1].h_samp_factor = 1;
112     mCompressInfo.comp_info[1].v_samp_factor = 1;
113     mCompressInfo.comp_info[2].h_samp_factor = 1;
114     mCompressInfo.comp_info[2].v_samp_factor = 1;
115 
116     mCompressInfo.dest = &mDestManager;
117 
118     return true;
119 }
120 
deinterleave(const uint8_t * vuPlanar,std::vector<uint8_t> & uRows,std::vector<uint8_t> & vRows,int rowIndex,int width,int height,int stride)121 static void deinterleave(const uint8_t* vuPlanar, std::vector<uint8_t>& uRows,
122         std::vector<uint8_t>& vRows, int rowIndex, int width, int height, int stride) {
123     int numRows = (height - rowIndex) / 2;
124     if (numRows > 8) numRows = 8;
125     for (int row = 0; row < numRows; ++row) {
126         int offset = ((rowIndex >> 1) + row) * stride;
127         const uint8_t* vu = vuPlanar + offset;
128         for (int i = 0; i < (width >> 1); ++i) {
129             int index = row * (width >> 1) + i;
130             uRows[index] = vu[1];
131             vRows[index] = vu[0];
132             vu += 2;
133         }
134     }
135 }
136 
compressData(const unsigned char * data,ExifData * exifData)137 bool NV12Compressor::compressData(const unsigned char* data, ExifData* exifData) {
138     const uint8_t* y[16];
139     const uint8_t* cb[8];
140     const uint8_t* cr[8];
141     const uint8_t** planes[3] = { y, cb, cr };
142 
143     int i, offset;
144     int width = mCompressInfo.image_width;
145     int height = mCompressInfo.image_height;
146     const uint8_t* yPlanar = data;
147     const uint8_t* vuPlanar = data + (width * height);
148     std::vector<uint8_t> uRows(8 * (width >> 1));
149     std::vector<uint8_t> vRows(8 * (width >> 1));
150 
151     // NOTE! DANGER! Do not construct any non-trivial objects below setjmp!
152     // The compiler will not generate code to destroy them during the return
153     // below so they will leak. Additionally, do not place any calls to libjpeg
154     // that can fail above this line or any error will cause undefined behavior.
155     if (setjmp(mErrorManager.mJumpBuffer)) {
156         // This is where the error handler will jump in case compression fails
157         // The error manager will ALOG an appropriate error message
158         return false;
159     }
160 
161     jpeg_start_compress(&mCompressInfo, TRUE);
162 
163     attachExifData(exifData);
164 
165     // process 16 lines of Y and 8 lines of U/V each time.
166     while (mCompressInfo.next_scanline < mCompressInfo.image_height) {
167         //deinterleave u and v
168         deinterleave(vuPlanar, uRows, vRows, mCompressInfo.next_scanline,
169                      width, height, width);
170 
171         // Jpeg library ignores the rows whose indices are greater than height.
172         for (i = 0; i < 16; i++) {
173             // y row
174             y[i] = yPlanar + (mCompressInfo.next_scanline + i) * width;
175 
176             // construct u row and v row
177             if ((i & 1) == 0) {
178                 // height and width are both halved because of downsampling
179                 offset = (i >> 1) * (width >> 1);
180                 cb[i/2] = &uRows[offset];
181                 cr[i/2] = &vRows[offset];
182             }
183           }
184         jpeg_write_raw_data(&mCompressInfo, const_cast<JSAMPIMAGE>(planes), 16);
185     }
186 
187     jpeg_finish_compress(&mCompressInfo);
188     jpeg_destroy_compress(&mCompressInfo);
189 
190     return true;
191 }
192 
attachExifData(ExifData * exifData)193 bool NV12Compressor::attachExifData(ExifData* exifData) {
194     if (exifData == nullptr) {
195         // This is not an error, we don't require EXIF data
196         return true;
197     }
198 
199     // Save the EXIF data to memory
200     unsigned char* rawData = nullptr;
201     unsigned int size = 0;
202     exif_data_save_data(exifData, &rawData, &size);
203     if (rawData == nullptr) {
204         ALOGE("Failed to create EXIF data block");
205         return false;
206     }
207 
208     jpeg_write_marker(&mCompressInfo, JPEG_APP0 + 1, rawData, size);
209     free(rawData);
210     return true;
211 }
212 
ErrorManager()213 NV12Compressor::ErrorManager::ErrorManager() {
214     error_exit = &onJpegError;
215 }
216 
onJpegError(j_common_ptr cinfo)217 void NV12Compressor::ErrorManager::onJpegError(j_common_ptr cinfo) {
218     // NOTE! Do not construct any non-trivial objects in this method at the top
219     // scope. Their destructors will not be called. If you do need such an
220     // object create a local scope that does not include the longjmp call,
221     // that ensures the object is destroyed before longjmp is called.
222     ErrorManager* errorManager = reinterpret_cast<ErrorManager*>(cinfo->err);
223 
224     // Format and log error message
225     char errorMessage[JMSG_LENGTH_MAX];
226     (*errorManager->format_message)(cinfo, errorMessage);
227     errorMessage[sizeof(errorMessage) - 1] = '\0';
228     ALOGE("JPEG compression error: %s", errorMessage);
229     jpeg_destroy(cinfo);
230 
231     // And through the looking glass we go
232     longjmp(errorManager->mJumpBuffer, 1);
233 }
234 
DestinationManager()235 NV12Compressor::DestinationManager::DestinationManager() {
236     init_destination = &initDestination;
237     empty_output_buffer = &emptyOutputBuffer;
238     term_destination = &termDestination;
239 }
240 
initDestination(j_compress_ptr cinfo)241 void NV12Compressor::DestinationManager::initDestination(j_compress_ptr cinfo) {
242     auto manager = reinterpret_cast<DestinationManager*>(cinfo->dest);
243 
244     // Start out with some arbitrary but not too large buffer size
245     manager->mBuffer.resize(16 * 1024);
246     manager->next_output_byte = &manager->mBuffer[0];
247     manager->free_in_buffer = manager->mBuffer.size();
248 }
249 
emptyOutputBuffer(j_compress_ptr cinfo)250 boolean NV12Compressor::DestinationManager::emptyOutputBuffer(
251         j_compress_ptr cinfo) {
252     auto manager = reinterpret_cast<DestinationManager*>(cinfo->dest);
253 
254     // Keep doubling the size of the buffer for a very low, amortized
255     // performance cost of the allocations
256     size_t oldSize = manager->mBuffer.size();
257     manager->mBuffer.resize(oldSize * 2);
258     manager->next_output_byte = &manager->mBuffer[oldSize];
259     manager->free_in_buffer = manager->mBuffer.size() - oldSize;
260     return manager->free_in_buffer != 0;
261 }
262 
termDestination(j_compress_ptr cinfo)263 void NV12Compressor::DestinationManager::termDestination(j_compress_ptr cinfo) {
264     auto manager = reinterpret_cast<DestinationManager*>(cinfo->dest);
265 
266     // Resize down to the exact size of the output, that is remove as many
267     // bytes as there are left in the buffer
268     manager->mBuffer.resize(manager->mBuffer.size() - manager->free_in_buffer);
269 }
270 
findJpegSize(uint8_t * jpegBuffer,size_t maxSize,size_t * size)271 status_t NV12Compressor::findJpegSize(uint8_t *jpegBuffer, size_t maxSize, size_t *size /*out*/) {
272     if ((size == nullptr) || (jpegBuffer == nullptr)) {
273         return BAD_VALUE;
274     }
275 
276     if (checkJpegStart(jpegBuffer) == 0) {
277         return BAD_VALUE;
278     }
279 
280     // Read JFIF segment markers, skip over segment data
281     *size = kMarkerLength; //jump to Start Of Image
282     while (*size <= maxSize - kMarkerLength) {
283         segment_t *segment = (segment_t*)(jpegBuffer + *size);
284         uint8_t type = checkJpegMarker(segment->marker);
285         if (type == 0) { // invalid marker, no more segments, begin JPEG data
286             break;
287         }
288         if (type == kEndOfImage || *size > maxSize - sizeof(segment_t)) {
289             return BAD_VALUE;
290         }
291 
292         size_t length = ntohs(segment->length);
293         *size += length + kMarkerLength;
294     }
295 
296     // Find End of Image
297     // Scan JPEG buffer until End of Image
298     bool foundEnd = false;
299     for ( ; *size <= maxSize - kMarkerLength; (*size)++) {
300         if (checkJpegEnd(jpegBuffer + *size)) {
301             foundEnd = true;
302             *size += kMarkerLength;
303             break;
304         }
305     }
306 
307     if (!foundEnd) {
308         return BAD_VALUE;
309     }
310 
311     if (*size > maxSize) {
312         *size = maxSize;
313     }
314 
315     return OK;
316 }
317 
getJpegImageDimensions(uint8_t * jpegBuffer,size_t jpegBufferSize,size_t * width,size_t * height)318 status_t NV12Compressor::getJpegImageDimensions(uint8_t *jpegBuffer,
319         size_t jpegBufferSize, size_t *width /*out*/, size_t *height /*out*/) {
320     if ((jpegBuffer == nullptr) || (width == nullptr) || (height == nullptr) ||
321             (jpegBufferSize == 0u)) {
322         return BAD_VALUE;
323     }
324 
325     // Scan JPEG buffer until Start of Frame
326     bool foundSOF = false;
327     size_t currentPos;
328     for (currentPos = 0; currentPos <= jpegBufferSize - kMarkerLength; currentPos++) {
329         if (checkStartOfFrame(jpegBuffer + currentPos)) {
330             foundSOF = true;
331             currentPos += kMarkerLength;
332             break;
333         }
334     }
335 
336     if (!foundSOF) {
337         ALOGE("%s: Start of Frame not found", __func__);
338         return BAD_VALUE;
339     }
340 
341     sof_t *startOfFrame = reinterpret_cast<sof_t *> (jpegBuffer + currentPos);
342     *width = ntohs(startOfFrame->width);
343     *height = ntohs(startOfFrame->height);
344 
345     return OK;
346 }
347 
getExifOrientation(uint8_t * jpegBuffer,size_t jpegBufferSize,ExifOrientation * exifValue)348 status_t NV12Compressor::getExifOrientation(uint8_t *jpegBuffer, size_t jpegBufferSize,
349         ExifOrientation *exifValue /*out*/) {
350     if ((jpegBuffer == nullptr) || (exifValue == nullptr) || (jpegBufferSize == 0u)) {
351         return BAD_VALUE;
352     }
353 
354     std::unique_ptr<ExifData> exifData(exif_data_new());
355     exif_data_load_data(exifData.get(), jpegBuffer, jpegBufferSize);
356     ExifEntry *orientation = exif_content_get_entry(exifData->ifd[EXIF_IFD_0],
357             EXIF_TAG_ORIENTATION);
358     if ((orientation == nullptr) || (orientation->size != sizeof(ExifShort))) {
359         return BAD_VALUE;
360     }
361 
362     auto orientationValue = exif_get_short(orientation->data,
363             exif_data_get_byte_order(exifData.get()));
364     status_t ret;
365     switch (orientationValue) {
366         case ExifOrientation::ORIENTATION_0_DEGREES:
367         case ExifOrientation::ORIENTATION_90_DEGREES:
368         case ExifOrientation::ORIENTATION_180_DEGREES:
369         case ExifOrientation::ORIENTATION_270_DEGREES:
370             *exifValue = static_cast<ExifOrientation> (orientationValue);
371             ret = OK;
372             break;
373         default:
374             ALOGE("%s: Unexpected EXIF orientation value: %u", __FUNCTION__, orientationValue);
375             ret = BAD_VALUE;
376     }
377 
378     return ret;
379 }
380