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