• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 #include <array>
18 #include <cstring>
19 #include <cstdio>
20 #include <inttypes.h>
21 #include <memory.h>
22 #include <vector>
23 
24 #include <setjmp.h>
25 
26 #include <android/hardware/camera/device/3.2/types.h>
27 
28 #include "core_jni_helpers.h"
29 #include "jni.h"
30 #include <nativehelper/JNIHelp.h>
31 
32 #define CAMERA_PROCESSOR_CLASS_NAME "android/hardware/camera2/impl/CameraExtensionJpegProcessor"
33 
34 extern "C" {
35 #include "jpeglib.h"
36 }
37 
38 using namespace std;
39 using namespace android;
40 
41 using android::hardware::camera::device::V3_2::CameraBlob;
42 using android::hardware::camera::device::V3_2::CameraBlobId;
43 
44 class Transform;
45 struct Plane;
46 
sgn(int val)47 inline int sgn(int val) { return (0 < val) - (val < 0); }
48 
min(int a,int b)49 inline int min(int a, int b) { return a < b ? a : b; }
50 
max(int a,int b)51 inline int max(int a, int b) { return a > b ? a : b; }
52 
53 /**
54  * Represents a combined cropping and rotation transformation.
55  *
56  * The transformation maps the coordinates (mOrigX, mOrigY) and (mOneX, mOneY)
57  * in the input image to the origin and (mOutputWidth, mOutputHeight)
58  * respectively.
59  */
60 class Transform {
61     public:
62         Transform(int origX, int origY, int oneX, int oneY);
63 
64         static Transform forCropFollowedByRotation(int cropLeft, int cropTop,
65                 int cropRight, int cropBottom, int rot90);
66 
getOutputWidth() const67         inline int getOutputWidth() const { return mOutputWidth; }
68 
getOutputHeight() const69         inline int getOutputHeight() const { return mOutputHeight; }
70 
71         bool operator==(const Transform& other) const;
72 
73         /**
74          * Transforms the input coordinates.  Coordinates outside the cropped region
75          * are clamped to valid values.
76          */
77         void map(int x, int y, int* outX, int* outY) const;
78 
79     private:
80         int mOutputWidth;
81         int mOutputHeight;
82 
83         // The coordinates of the point to map the origin to.
84         const int mOrigX, mOrigY;
85         // The coordinates of the point to map the point (getOutputWidth(),
86         // getOutputHeight()) to.
87         const int mOneX, mOneY;
88 
89         // A matrix for the rotational component.
90         int mMat00, mMat01;
91         int mMat10, mMat11;
92 };
93 
94 /**
95  * Represents a model for accessing pixel data for a single plane of an image.
96  * Note that the actual data is not owned by this class, and the underlying
97  * data does not need to be stored in separate planes.
98  */
99 struct Plane {
100     // The dimensions of this plane of the image
101     int width;
102     int height;
103 
104     // A pointer to raw pixel data
105     const unsigned char* data;
106     // The difference in address between consecutive pixels in the same row
107     int pixelStride;
108     // The difference in address between the start of consecutive rows
109     int rowStride;
110 };
111 
112 /**
113  * Provides an interface for simultaneously reading a certain number of rows of
114  * an image plane as contiguous arrays, suitable for use with libjpeg.
115  */
116 template <unsigned int ROWS>
117 class RowIterator {
118     public:
119         /**
120          * Creates a new RowIterator which will crop and rotate with the given
121          * transform.
122          *
123          * @param plane the plane to iterate over
124          * @param transform the transformation to map output values into the
125          * coordinate space of the plane
126          * @param rowLength the length of the rows returned via LoadAt().  If this is
127          * longer than the width of the output (after applying the transform), then
128          * the right-most value is repeated.
129          */
130         inline RowIterator(Plane plane, Transform transform, int rowLength);
131 
132         /**
133          * Returns an array of pointers into consecutive rows of contiguous image
134          * data starting at y.  That is, samples within each row are contiguous.
135          * However, the individual arrays pointed-to may be separate.
136          * When the end of the image is reached, the last row of the image is
137          * repeated.
138          * The returned pointers are valid until the next call to loadAt().
139          */
140         inline const std::array<unsigned char*, ROWS> loadAt(int baseY);
141 
142     private:
143         Plane mPlane;
144         Transform mTransform;
145         // The length of a row, with padding to the next multiple of 64.
146         int mPaddedRowLength;
147         std::vector<unsigned char> mBuffer;
148 };
149 
150 template <unsigned int ROWS>
RowIterator(Plane plane,Transform transform,int rowLength)151 RowIterator<ROWS>::RowIterator(Plane plane, Transform transform,
152                                          int rowLength)
153         : mPlane(plane), mTransform(transform) {
154     mPaddedRowLength = rowLength;
155     mBuffer = std::vector<unsigned char>(rowLength * ROWS);
156 }
157 
158 template <unsigned int ROWS>
loadAt(int baseY)159 const std::array<unsigned char*, ROWS> RowIterator<ROWS>::loadAt(int baseY) {
160     std::array<unsigned char*, ROWS> bufPtrs;
161     for (unsigned int i = 0; i < ROWS; i++) {
162         bufPtrs[i] = &mBuffer[mPaddedRowLength * i];
163     }
164 
165     if (mPlane.width == 0 || mPlane.height == 0) {
166         return bufPtrs;
167     }
168 
169     for (unsigned int i = 0; i < ROWS; i++) {
170         int y = i + baseY;
171         y = min(y, mTransform.getOutputHeight() - 1);
172 
173         int output_width = mPaddedRowLength;
174         output_width = min(output_width, mTransform.getOutputWidth());
175         output_width = min(output_width, mPlane.width);
176 
177         // Each row in the output image will be copied into buf_ by gathering pixels
178         // along an axis-aligned line in the plane.
179         // The line is defined by (startX, startY) -> (endX, endY), computed via the
180         // current Transform.
181         int startX;
182         int startY;
183         mTransform.map(0, y, &startX, &startY);
184 
185         int endX;
186         int endY;
187         mTransform.map(output_width - 1, y, &endX, &endY);
188 
189         // Clamp (startX, startY) and (endX, endY) to the valid bounds of the plane.
190         startX = min(startX, mPlane.width - 1);
191         startY = min(startY, mPlane.height - 1);
192         endX = min(endX, mPlane.width - 1);
193         endY = min(endY, mPlane.height - 1);
194         startX = max(startX, 0);
195         startY = max(startY, 0);
196         endX = max(endX, 0);
197         endY = max(endY, 0);
198 
199         // To reduce work inside the copy-loop, precompute the start, end, and
200         // stride relating the values to be gathered from mPlane into buf
201         // for this particular scan-line.
202         int dx = sgn(endX - startX);
203         int dy = sgn(endY - startY);
204         if (!(dx == 0 || dy == 0)) {
205             ALOGE("%s: Unexpected bounds: %dx%d %dx%d!", __FUNCTION__, startX, endX, startY, endY);
206             return bufPtrs;
207         }
208 
209         // The index into mPlane.data of (startX, startY)
210         int plane_start = startX * mPlane.pixelStride + startY * mPlane.rowStride;
211         // The index into mPlane.data of (endX, endY)
212         int plane_end = endX * mPlane.pixelStride + endY * mPlane.rowStride;
213         // The stride, in terms of indices in plane_data, required to enumerate the
214         // samples between the start and end points.
215         int stride = dx * mPlane.pixelStride + dy * mPlane.rowStride;
216         // In the degenerate-case of a 1x1 plane, startX and endX are equal, so
217         // stride would be 0, resulting in an infinite-loop.  To avoid this case,
218         // use a stride of at-least 1.
219         if (stride == 0) {
220             stride = 1;
221         }
222 
223         int outX = 0;
224         for (int idx = plane_start; idx >= min(plane_start, plane_end) &&
225                 idx <= max(plane_start, plane_end); idx += stride) {
226             bufPtrs[i][outX] = mPlane.data[idx];
227             outX++;
228         }
229 
230         // Fill the remaining right-edge of the buffer by extending the last
231         // value.
232         unsigned char right_padding_value = bufPtrs[i][outX - 1];
233         for (; outX < mPaddedRowLength; outX++) {
234             bufPtrs[i][outX] = right_padding_value;
235         }
236     }
237 
238     return bufPtrs;
239 }
240 
241 template <typename T>
safeDelete(T & t)242 void safeDelete(T& t) {
243     delete t;
244     t = nullptr;
245 }
246 
247 template <typename T>
safeDeleteArray(T & t)248 void safeDeleteArray(T& t) {
249     delete[] t;
250     t = nullptr;
251 }
252 
Transform(int origX,int origY,int oneX,int oneY)253 Transform::Transform(int origX, int origY, int oneX, int oneY)
254     : mOrigX(origX), mOrigY(origY), mOneX(oneX), mOneY(oneY) {
255     if (origX == oneX || origY == oneY) {
256         // Handle the degenerate case of cropping to a 0x0 rectangle.
257         mMat00 = 0;
258         mMat01 = 0;
259         mMat10 = 0;
260         mMat11 = 0;
261         return;
262     }
263 
264     if (oneX > origX && oneY > origY) {
265         // 0-degree rotation
266         mMat00 = 1;
267         mMat01 = 0;
268         mMat10 = 0;
269         mMat11 = 1;
270         mOutputWidth = abs(oneX - origX);
271         mOutputHeight = abs(oneY - origY);
272     } else if (oneX < origX && oneY > origY) {
273         // 90-degree CCW rotation
274         mMat00 = 0;
275         mMat01 = -1;
276         mMat10 = 1;
277         mMat11 = 0;
278         mOutputWidth = abs(oneY - origY);
279         mOutputHeight = abs(oneX - origX);
280     } else if (oneX > origX && oneY < origY) {
281         // 270-degree CCW rotation
282         mMat00 = 0;
283         mMat01 = 1;
284         mMat10 = -1;
285         mMat11 = 0;
286         mOutputWidth = abs(oneY - origY);
287         mOutputHeight = abs(oneX - origX);;
288     } else if (oneX < origX && oneY < origY) {
289         // 180-degree CCW rotation
290         mMat00 = -1;
291         mMat01 = 0;
292         mMat10 = 0;
293         mMat11 = -1;
294         mOutputWidth = abs(oneX - origX);
295         mOutputHeight = abs(oneY - origY);
296     }
297 }
298 
forCropFollowedByRotation(int cropLeft,int cropTop,int cropRight,int cropBottom,int rot90)299 Transform Transform::forCropFollowedByRotation(int cropLeft, int cropTop, int cropRight,
300         int cropBottom, int rot90) {
301     // The input crop-region excludes cropRight and cropBottom, so transform the
302     // crop rect such that it defines the entire valid region of pixels
303     // inclusively.
304     cropRight -= 1;
305     cropBottom -= 1;
306 
307     int cropXLow = min(cropLeft, cropRight);
308     int cropYLow = min(cropTop, cropBottom);
309     int cropXHigh = max(cropLeft, cropRight);
310     int cropYHigh = max(cropTop, cropBottom);
311     rot90 %= 4;
312     if (rot90 == 0) {
313         return Transform(cropXLow, cropYLow, cropXHigh + 1, cropYHigh + 1);
314     } else if (rot90 == 1) {
315         return Transform(cropXHigh, cropYLow, cropXLow - 1, cropYHigh + 1);
316     } else if (rot90 == 2) {
317         return Transform(cropXHigh, cropYHigh, cropXLow - 1, cropYLow - 1);
318     } else if (rot90 == 3) {
319         return Transform(cropXLow, cropYHigh, cropXHigh + 1, cropYLow - 1);
320     }
321     // Impossible case.
322     return Transform(cropXLow, cropYLow, cropXHigh + 1, cropYHigh + 1);
323 }
324 
operator ==(const Transform & other) const325 bool Transform::operator==(const Transform& other) const {
326     return other.mOrigX == mOrigX &&  //
327            other.mOrigY == mOrigY &&  //
328            other.mOneX == mOneX &&    //
329            other.mOneY == mOneY;
330 }
331 
332 /**
333  * Transforms the input coordinates.  Coordinates outside the cropped region
334  * are clamped to valid values.
335  */
map(int x,int y,int * outX,int * outY) const336 void Transform::map(int x, int y, int* outX, int* outY) const {
337     x = max(x, 0);
338     y = max(y, 0);
339     x = min(x, getOutputWidth() - 1);
340     y = min(y, getOutputHeight() - 1);
341     *outX = x * mMat00 + y * mMat01 + mOrigX;
342     *outY = x * mMat10 + y * mMat11 + mOrigY;
343 }
344 
compress(int img_width,int img_height,RowIterator<16> & y_row_generator,RowIterator<8> & cb_row_generator,RowIterator<8> & cr_row_generator,unsigned char * out_buf,size_t out_buf_capacity,std::function<void (size_t)> flush,int quality)345 int compress(int img_width, int img_height, RowIterator<16>& y_row_generator,
346         RowIterator<8>& cb_row_generator, RowIterator<8>& cr_row_generator,
347         unsigned char* out_buf, size_t out_buf_capacity, std::function<void(size_t)> flush,
348         int quality) {
349     // libjpeg requires the use of setjmp/longjmp to recover from errors.  Since
350     // this doesn't play well with RAII, we must use pointers and manually call
351     // delete. See POSIX documentation for longjmp() for details on why the
352     // volatile keyword is necessary.
353     volatile jpeg_compress_struct cinfov;
354 
355     jpeg_compress_struct& cinfo =
356             *const_cast<struct jpeg_compress_struct*>(&cinfov);
357 
358     JSAMPROW* volatile yArr = nullptr;
359     JSAMPROW* volatile cbArr = nullptr;
360     JSAMPROW* volatile crArr = nullptr;
361 
362     JSAMPARRAY imgArr[3];
363 
364     // Error handling
365 
366     struct my_error_mgr {
367         struct jpeg_error_mgr pub;
368         jmp_buf setjmp_buffer;
369     } err;
370 
371     cinfo.err = jpeg_std_error(&err.pub);
372 
373     // Default error_exit will call exit(), so override
374     // to return control via setjmp/longjmp.
375     err.pub.error_exit = [](j_common_ptr cinfo) {
376         my_error_mgr* myerr = reinterpret_cast<my_error_mgr*>(cinfo->err);
377 
378         (*cinfo->err->output_message)(cinfo);
379 
380         // Return control to the setjmp point (see call to setjmp()).
381         longjmp(myerr->setjmp_buffer, 1);
382     };
383 
384     cinfo.err = (struct jpeg_error_mgr*)&err;
385 
386     // Set the setjmp point to return to in case of error.
387     if (setjmp(err.setjmp_buffer)) {
388         // If libjpeg hits an error, control will jump to this point (see call to
389         // longjmp()).
390         jpeg_destroy_compress(&cinfo);
391 
392         safeDeleteArray(yArr);
393         safeDeleteArray(cbArr);
394         safeDeleteArray(crArr);
395 
396         return -1;
397     }
398 
399     // Create jpeg compression context
400     jpeg_create_compress(&cinfo);
401 
402     // Stores data needed by our c-style callbacks into libjpeg
403     struct ClientData {
404         unsigned char* out_buf;
405         size_t out_buf_capacity;
406         std::function<void(size_t)> flush;
407         int totalOutputBytes;
408     } clientData{out_buf, out_buf_capacity, flush, 0};
409 
410     cinfo.client_data = &clientData;
411 
412     // Initialize destination manager
413     jpeg_destination_mgr dest;
414 
415     dest.init_destination = [](j_compress_ptr cinfo) {
416         ClientData& cdata = *reinterpret_cast<ClientData*>(cinfo->client_data);
417 
418         cinfo->dest->next_output_byte = cdata.out_buf;
419         cinfo->dest->free_in_buffer = cdata.out_buf_capacity;
420     };
421 
422     dest.empty_output_buffer = [](j_compress_ptr cinfo) -> boolean {
423         ClientData& cdata = *reinterpret_cast<ClientData*>(cinfo->client_data);
424 
425         size_t numBytesInBuffer = cdata.out_buf_capacity;
426         cdata.flush(numBytesInBuffer);
427         cdata.totalOutputBytes += numBytesInBuffer;
428 
429         // Reset the buffer
430         cinfo->dest->next_output_byte = cdata.out_buf;
431         cinfo->dest->free_in_buffer = cdata.out_buf_capacity;
432 
433         return true;
434     };
435 
436     dest.term_destination = [](j_compress_ptr cinfo __unused) {
437         // do nothing to terminate the output buffer
438     };
439 
440     cinfo.dest = &dest;
441 
442     // Set jpeg parameters
443     cinfo.image_width = img_width;
444     cinfo.image_height = img_height;
445     cinfo.input_components = 3;
446 
447     // Set defaults based on the above values
448     jpeg_set_defaults(&cinfo);
449 
450     jpeg_set_quality(&cinfo, quality, true);
451 
452     cinfo.dct_method = JDCT_IFAST;
453 
454     cinfo.raw_data_in = true;
455 
456     jpeg_set_colorspace(&cinfo, JCS_YCbCr);
457 
458     cinfo.comp_info[0].h_samp_factor = 2;
459     cinfo.comp_info[0].v_samp_factor = 2;
460     cinfo.comp_info[1].h_samp_factor = 1;
461     cinfo.comp_info[1].v_samp_factor = 1;
462     cinfo.comp_info[2].h_samp_factor = 1;
463     cinfo.comp_info[2].v_samp_factor = 1;
464 
465     jpeg_start_compress(&cinfo, true);
466 
467     yArr = new JSAMPROW[cinfo.comp_info[0].v_samp_factor * DCTSIZE];
468     cbArr = new JSAMPROW[cinfo.comp_info[1].v_samp_factor * DCTSIZE];
469     crArr = new JSAMPROW[cinfo.comp_info[2].v_samp_factor * DCTSIZE];
470 
471     imgArr[0] = const_cast<JSAMPARRAY>(yArr);
472     imgArr[1] = const_cast<JSAMPARRAY>(cbArr);
473     imgArr[2] = const_cast<JSAMPARRAY>(crArr);
474 
475     for (int y = 0; y < img_height; y += DCTSIZE * 2) {
476         std::array<unsigned char*, 16> yData = y_row_generator.loadAt(y);
477         std::array<unsigned char*, 8> cbData = cb_row_generator.loadAt(y / 2);
478         std::array<unsigned char*, 8> crData = cr_row_generator.loadAt(y / 2);
479 
480         for (int row = 0; row < DCTSIZE * 2; row++) {
481             yArr[row] = yData[row];
482         }
483         for (int row = 0; row < DCTSIZE; row++) {
484             cbArr[row] = cbData[row];
485             crArr[row] = crData[row];
486         }
487 
488         jpeg_write_raw_data(&cinfo, imgArr, DCTSIZE * 2);
489     }
490 
491     jpeg_finish_compress(&cinfo);
492 
493     int numBytesInBuffer = cinfo.dest->next_output_byte - out_buf;
494 
495     flush(numBytesInBuffer);
496 
497     clientData.totalOutputBytes += numBytesInBuffer;
498 
499     safeDeleteArray(yArr);
500     safeDeleteArray(cbArr);
501     safeDeleteArray(crArr);
502 
503     jpeg_destroy_compress(&cinfo);
504 
505     return clientData.totalOutputBytes;
506 }
507 
compress(int width,int height,unsigned char * yBuf,int yPStride,int yRStride,unsigned char * cbBuf,int cbPStride,int cbRStride,unsigned char * crBuf,int crPStride,int crRStride,unsigned char * outBuf,size_t outBufCapacity,int quality,int cropLeft,int cropTop,int cropRight,int cropBottom,int rot90)508 int compress(
509         /** Input image dimensions */
510         int width, int height,
511         /** Y Plane */
512         unsigned char* yBuf, int yPStride, int yRStride,
513         /** Cb Plane */
514         unsigned char* cbBuf, int cbPStride, int cbRStride,
515         /** Cr Plane */
516         unsigned char* crBuf, int crPStride, int crRStride,
517         /** Output */
518         unsigned char* outBuf, size_t outBufCapacity,
519         /** Jpeg compression parameters */
520         int quality,
521         /** Crop */
522         int cropLeft, int cropTop, int cropRight, int cropBottom,
523         /** Rotation (multiple of 90).  For example, rot90 = 1 implies a 90 degree
524          * rotation. */
525         int rot90) {
526     int finalWidth;
527     int finalHeight;
528     finalWidth = cropRight - cropLeft;
529     finalHeight = cropBottom - cropTop;
530 
531     rot90 %= 4;
532     // for 90 and 270-degree rotations, flip the final width and height
533     if (rot90 == 1) {
534         finalWidth = cropBottom - cropTop;
535         finalHeight = cropRight - cropLeft;
536     } else if (rot90 == 3) {
537         finalWidth = cropBottom - cropTop;
538         finalHeight = cropRight - cropLeft;
539     }
540 
541     const Plane yP = {width, height, yBuf, yPStride, yRStride};
542     const Plane cbP = {width / 2, height / 2, cbBuf, cbPStride, cbRStride};
543     const Plane crP = {width / 2, height / 2, crBuf, crPStride, crRStride};
544 
545     auto flush = [](size_t numBytes __unused) {
546         // do nothing
547     };
548 
549     // Round up to the nearest multiple of 64.
550     int y_row_length = (finalWidth + 16 + 63) & ~63;
551     int cb_row_length = (finalWidth / 2 + 16 + 63) & ~63;
552     int cr_row_length = (finalWidth / 2 + 16 + 63) & ~63;
553 
554     Transform yTrans = Transform::forCropFollowedByRotation(
555             cropLeft, cropTop, cropRight, cropBottom, rot90);
556 
557     Transform chromaTrans = Transform::forCropFollowedByRotation(
558             cropLeft / 2, cropTop / 2, cropRight / 2, cropBottom / 2, rot90);
559 
560     RowIterator<16> yIter(yP, yTrans, y_row_length);
561     RowIterator<8> cbIter(cbP, chromaTrans, cb_row_length);
562     RowIterator<8> crIter(crP, chromaTrans, cr_row_length);
563 
564     return compress(finalWidth, finalHeight, yIter, cbIter, crIter, outBuf, outBufCapacity, flush,
565             quality);
566 }
567 
568 extern "C" {
569 
CameraExtensionJpegProcessor_compressJpegFromYUV420p(JNIEnv * env,jclass clazz __unused,jint width,jint height,jobject yBuf,jint yPStride,jint yRStride,jobject cbBuf,jint cbPStride,jint cbRStride,jobject crBuf,jint crPStride,jint crRStride,jobject outBuf,jint outBufCapacity,jint quality,jint cropLeft,jint cropTop,jint cropRight,jint cropBottom,jint rot90)570 static jint CameraExtensionJpegProcessor_compressJpegFromYUV420p(
571         JNIEnv* env, jclass clazz __unused,
572         /** Input image dimensions */
573         jint width, jint height,
574         /** Y Plane */
575         jobject yBuf, jint yPStride, jint yRStride,
576         /** Cb Plane */
577         jobject cbBuf, jint cbPStride, jint cbRStride,
578         /** Cr Plane */
579         jobject crBuf, jint crPStride, jint crRStride,
580         /** Output */
581         jobject outBuf, jint outBufCapacity,
582         /** Jpeg compression parameters */
583         jint quality,
584         /** Crop */
585         jint cropLeft, jint cropTop, jint cropRight, jint cropBottom,
586         /** Rotation (multiple of 90).  For example, rot90 = 1 implies a 90 degree
587          * rotation. */
588         jint rot90) {
589     jbyte* y = (jbyte*)env->GetDirectBufferAddress(yBuf);
590     jbyte* cb = (jbyte*)env->GetDirectBufferAddress(cbBuf);
591     jbyte* cr = (jbyte*)env->GetDirectBufferAddress(crBuf);
592     jbyte* out = (jbyte*)env->GetDirectBufferAddress(outBuf);
593 
594     size_t actualJpegSize = compress(width, height,
595             (unsigned char*)y, yPStride, yRStride,
596             (unsigned char*)cb, cbPStride, cbRStride,
597             (unsigned char*)cr, crPStride, crRStride,
598             (unsigned char*)out, (size_t)outBufCapacity,
599             quality, cropLeft, cropTop, cropRight, cropBottom, rot90);
600 
601     size_t finalJpegSize = actualJpegSize + sizeof(CameraBlob);
602     if (finalJpegSize > outBufCapacity) {
603         ALOGE("%s: Final jpeg buffer %zu not large enough for the jpeg blob header with "\
604                 "capacity %d", __FUNCTION__, finalJpegSize, outBufCapacity);
605         return actualJpegSize;
606     }
607 
608     int8_t* header = static_cast<int8_t *> (out) +
609             (outBufCapacity - sizeof(CameraBlob));
610     CameraBlob *blob = reinterpret_cast<CameraBlob *> (header);
611     blob->blobId = CameraBlobId::JPEG;
612     blob->blobSize = actualJpegSize;
613 
614     return actualJpegSize;
615 }
616 
617 } // extern "C"
618 
619 static const JNINativeMethod gCameraExtensionJpegProcessorMethods[] = {
620     {"compressJpegFromYUV420pNative",
621     "(IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IILjava/nio/ByteBuffer;IIIIIII)I",
622     (void*)CameraExtensionJpegProcessor_compressJpegFromYUV420p}};
623 
624 // Get all the required offsets in java class and register native functions
register_android_hardware_camera2_impl_CameraExtensionJpegProcessor(JNIEnv * env)625 int register_android_hardware_camera2_impl_CameraExtensionJpegProcessor(JNIEnv* env) {
626     // Register native functions
627     return RegisterMethodsOrDie(env, CAMERA_PROCESSOR_CLASS_NAME,
628             gCameraExtensionJpegProcessorMethods, NELEM(gCameraExtensionJpegProcessorMethods));
629 }
630