1 /*
2 * Copyright Samsung Electronics Co.,LTD.
3 * Copyright (C) 2015 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #include <ExynosJpegEncoderForCamera.h>
19 #include <hardware/exynos/ion.h>
20 #include <linux/videodev2.h>
21 #include <sys/mman.h>
22 #include <sys/types.h>
23 #include <system/graphics.h>
24
25 #include "AppMarkerWriter.h"
26 #include "ThumbnailScaler.h"
27 #include "hwjpeg-internal.h"
28
29 // Data length written by H/W without the scan data.
30 #define NECESSARY_JPEG_LENGTH (0x24B + 2 * JPEG_MARKER_SIZE)
31
GetImageLength(unsigned int width,unsigned int height,int v4l2Format)32 static size_t GetImageLength(unsigned int width, unsigned int height, int v4l2Format) {
33 size_t size = width * height;
34
35 switch (v4l2Format) {
36 case V4L2_PIX_FMT_YUYV:
37 case V4L2_PIX_FMT_YVYU:
38 case V4L2_PIX_FMT_UYVY:
39 case V4L2_PIX_FMT_VYUY:
40 case V4L2_PIX_FMT_NV16:
41 case V4L2_PIX_FMT_NV61:
42 case V4L2_PIX_FMT_YUV422P:
43 return size * 2;
44 case V4L2_PIX_FMT_NV12:
45 case V4L2_PIX_FMT_NV21:
46 case V4L2_PIX_FMT_NV12M:
47 case V4L2_PIX_FMT_NV21M:
48 case V4L2_PIX_FMT_YUV420:
49 return size + (size / 4) * 2;
50 }
51
52 return 0;
53 }
54
GetThumbnailFormat(int v4l2Format)55 static int GetThumbnailFormat(int v4l2Format) {
56 if (v4l2Format == V4L2_PIX_FMT_NV12M)
57 return V4L2_PIX_FMT_NV12;
58 else if (v4l2Format == V4L2_PIX_FMT_NV21M)
59 return V4L2_PIX_FMT_NV21;
60 else
61 return v4l2Format;
62 }
63
ExynosJpegEncoderForCamera(bool bBTBComp)64 ExynosJpegEncoderForCamera::ExynosJpegEncoderForCamera(bool bBTBComp)
65 : m_phwjpeg4thumb(NULL),
66 m_fdIONClient(-1),
67 m_fdIONThumbImgBuffer(-1),
68 m_pIONThumbImgBuffer(NULL),
69 m_szIONThumbImgBuffer(0),
70 m_pIONThumbJpegBuffer(NULL),
71 m_fdIONThumbJpegBuffer(-1),
72 m_szIONThumbJpegBuffer(0),
73 m_nThumbWidth(0),
74 m_nThumbHeight(0),
75 m_nThumbQuality(0),
76 m_pStreamBase(NULL),
77 m_fThumbBufferType(0) {
78 m_pAppWriter = new CAppMarkerWriter();
79 if (!m_pAppWriter) {
80 ALOGE("Failed to allocated an instance of CAppMarkerWriter");
81 return;
82 }
83
84 m_phwjpeg4thumb = new CHWJpegV4L2Compressor();
85 if (!m_phwjpeg4thumb) {
86 ALOGE("Failed to create thumbnail compressor!");
87 return;
88 }
89
90 if (!m_phwjpeg4thumb->SetChromaSampFactor(2, 2)) {
91 ALOGE("Failed to configure chroma subsampling factor to YUV420 for thumbnail compression");
92 }
93
94 m_fdIONClient = exynos_ion_open();
95 if (m_fdIONClient < 0) {
96 ALOGERR("Failed to create ION client for thumbnail conversion");
97 }
98
99 if (!bBTBComp) SetState(STATE_NO_BTBCOMP);
100
101 // STATE_THUMBSIZE_CHANGED is to know if thumbnail image size need to be
102 // configured to HWJPEG. If HWJPEG does not support for back-to-back
103 // compression, it should not be configured.
104 if (IsBTBCompressionSupported()) SetState(STATE_THUMBSIZE_CHANGED);
105
106 m_extraInfo.appInfo = m_appInfo;
107
108 mThumbnailScaler.reset(ThumbnailScaler::createInstance());
109 if (!mThumbnailScaler->available()) ALOGW("Thumbnail scaler is not available.");
110
111 ALOGD("ExynosJpegEncoderForCamera Created: %p, ION %d", this, m_fdIONClient);
112 }
113
~ExynosJpegEncoderForCamera()114 ExynosJpegEncoderForCamera::~ExynosJpegEncoderForCamera() {
115 GetCompressor().Release();
116
117 delete m_pAppWriter;
118 delete m_phwjpeg4thumb;
119
120 if (m_pIONThumbImgBuffer != NULL) munmap(m_pIONThumbImgBuffer, m_szIONThumbImgBuffer);
121
122 if (m_fdIONThumbImgBuffer >= 0) close(m_fdIONThumbImgBuffer);
123
124 if (m_pIONThumbJpegBuffer) munmap(m_pIONThumbJpegBuffer, m_szIONThumbJpegBuffer);
125
126 if (m_fdIONThumbJpegBuffer >= 0) close(m_fdIONThumbJpegBuffer);
127
128 if (m_fdIONClient >= 0) exynos_ion_close(m_fdIONClient);
129
130 ALOGD("ExynosJpegEncoderForCamera Destroyed: %p, ION %d, ThumIMG %d", this, m_fdIONClient,
131 m_fdIONThumbImgBuffer);
132 }
133
setThumbnailSize(int w,int h)134 int ExynosJpegEncoderForCamera::setThumbnailSize(int w, int h) {
135 if ((m_nThumbWidth == w) && (m_nThumbHeight == h)) return 0;
136
137 // w == 0 and h == 0 resets thumbnail configuration
138 if (((w | h) != 0) && ((w < 16) || (h < 16))) {
139 ALOGE("Too small thumbnail image size %dx%d", w, h);
140 return -1;
141 }
142
143 m_nThumbWidth = w;
144 m_nThumbHeight = h;
145
146 if (IsBTBCompressionSupported()) SetState(STATE_THUMBSIZE_CHANGED);
147
148 return 0;
149 }
150
setThumbnailQuality(int quality)151 int ExynosJpegEncoderForCamera::setThumbnailQuality(int quality) {
152 if (m_nThumbQuality == quality) return 0;
153
154 if ((quality > 100) || (quality < 1)) {
155 ALOGE("Invalid quality factor %d for thumbnail image", quality);
156 return -1;
157 }
158
159 m_nThumbQuality = quality;
160
161 return GetCompressor().SetQuality(0, m_nThumbQuality) ? 0 : -1;
162 }
163
setThumbnailPadding(const unsigned char * padding,unsigned int num_planes)164 int ExynosJpegEncoderForCamera::setThumbnailPadding(const unsigned char* padding,
165 unsigned int num_planes) {
166 return GetCompressor().SetPadding2(padding, num_planes) ? 0 : -1;
167 }
168
EnsureFormatIsApplied()169 bool ExynosJpegEncoderForCamera::EnsureFormatIsApplied() {
170 if (TestStateEither(STATE_PIXFMT_CHANGED | STATE_SIZE_CHANGED | STATE_THUMBSIZE_CHANGED)) {
171 int thumb_width = m_nThumbWidth;
172 int thumb_height = m_nThumbHeight;
173 int width = 0;
174 int height = 0;
175
176 if (IsThumbGenerationNeeded() || !IsBTBCompressionSupported()) {
177 thumb_width = 0;
178 thumb_height = 0;
179 }
180
181 getSize(&width, &height);
182 if (!GetCompressor().SetImageFormat(getColorFormat(), width, height, thumb_width,
183 thumb_height))
184 return false;
185
186 ClearState(STATE_PIXFMT_CHANGED | STATE_SIZE_CHANGED | STATE_THUMBSIZE_CHANGED);
187 }
188
189 return true;
190 }
191
RemoveTrailingDummies(char * base,size_t len)192 size_t ExynosJpegEncoderForCamera::RemoveTrailingDummies(char* base, size_t len) {
193 ALOG_ASSERT(len > 4);
194 ALOG_ASSERT((base[0] == 0xFF) && (base[1] == 0xD8)); // SOI marker
195
196 size_t riter = len - 2;
197
198 while (riter > 0) {
199 if ((base[riter] == 0xFF) && (base[riter + 1] == 0xD9)) { // EOI marker
200 ALOGI_IF(riter < (len - 2), "Found %zu dummies after EOI", len - riter - 2);
201 return riter + 2;
202 }
203 riter--;
204 }
205
206 ALOGE("EOI is not found!");
207 ALOG_ASSERT(true);
208
209 return 0;
210 }
211
tCompressThumbnail(void * p)212 void* ExynosJpegEncoderForCamera::tCompressThumbnail(void* p) {
213 ExynosJpegEncoderForCamera* encoder = reinterpret_cast<ExynosJpegEncoderForCamera*>(p);
214
215 size_t thumblen = encoder->CompressThumbnail();
216 return reinterpret_cast<void*>(thumblen);
217 }
218
ProcessExif(char * base,size_t limit,exif_attribute_t * exifInfo,extra_appinfo_t * extra)219 bool ExynosJpegEncoderForCamera::ProcessExif(char* base, size_t limit, exif_attribute_t* exifInfo,
220 extra_appinfo_t* extra) {
221 // PREREQUISITES: The main and the thumbnail image size should be configured before.
222
223 // Sanity chck
224 uint32_t width = 0;
225 uint32_t height = 0;
226
227 getSize(reinterpret_cast<int*>(&width), reinterpret_cast<int*>(&height));
228
229 if (exifInfo) {
230 if ((exifInfo->width != width) || (exifInfo->height != height)) {
231 ALOGE("Inconsistent image dimension: Exif %dx%d, Thumb %dx%d", exifInfo->width,
232 exifInfo->height, width, height);
233 return false;
234 }
235
236 if (exifInfo->enableThumb) {
237 if ((exifInfo->widthThumb != static_cast<uint32_t>(m_nThumbWidth)) ||
238 (exifInfo->heightThumb != static_cast<uint32_t>(m_nThumbHeight))) {
239 ALOGE("Inconsistent thumbnail information: Exif %dx%d, Thumb %dx%d",
240 exifInfo->widthThumb, exifInfo->heightThumb, m_nThumbWidth, m_nThumbHeight);
241 return false;
242 }
243 }
244 }
245
246 // Giving appwriter the address beyond SOS marker
247 // because it is handled by this class
248 size_t align = 16;
249 if (!!(GetDeviceCapabilities() & V4L2_CAP_EXYNOS_JPEG_NO_STREAMBASE_ALIGN)) align = 1;
250
251 m_pAppWriter->PrepareAppWriter(base + JPEG_MARKER_SIZE, exifInfo, extra);
252
253 if (limit <= (m_pAppWriter->CalculateAPPSize(0) + NECESSARY_JPEG_LENGTH)) {
254 ALOGE("Too small JPEG stream buffer size, %zu bytes", limit);
255 return false;
256 }
257
258 bool reserve_thumbspace = true;
259
260 // If the length of the given stream buffer is too small, and thumbnail
261 // compression is also required, the compressed stream data of the main
262 // image is appeneded after the end of the fields if IFD1. The place is
263 // actually reserved for the embedded thumbnail but the main JPEG stream
264 // is written in this case because it is unknown how the compressed data
265 // of the thumbnail image will be.
266 // After the main and the thumbnail image compressions are completed,
267 // the compressed data of the main image is shifted by the length of the
268 // compressed data of the thumbnail image. Then the compressed data of
269 // the thumbnail image is copied to the place for it.
270 if (!exifInfo || !exifInfo->enableThumb || (limit < (JPEG_MAX_SEGMENT_SIZE * 10)))
271 reserve_thumbspace = false;
272
273 m_pAppWriter->Write(reserve_thumbspace, JPEG_MARKER_SIZE, align, TestState(STATE_HWFC_ENABLED));
274
275 ALOGD("Image compression starts from offset %zu (APPx size %zu, HWFC? %d, NBTB? %d)",
276 PTR_DIFF(base, m_pAppWriter->GetMainStreamBase()), m_pAppWriter->CalculateAPPSize(),
277 TestState(STATE_HWFC_ENABLED), TestState(STATE_NO_BTBCOMP));
278
279 return true;
280 }
281
PrepareCompression(bool thumbnail)282 bool ExynosJpegEncoderForCamera::PrepareCompression(bool thumbnail) {
283 if (!thumbnail) return true;
284
285 if (IsThumbGenerationNeeded()) {
286 if (pthread_create(&m_threadWorker, NULL, tCompressThumbnail,
287 reinterpret_cast<void*>(this)) != 0) {
288 ALOGERR("Failed to create thumbnail generation thread");
289 return false;
290 }
291 } else {
292 // allocate temporary thumbnail stream buffer
293 // to prevent overflow of the compressed stream
294 if (!AllocThumbJpegBuffer()) {
295 return false;
296 }
297 }
298
299 if (!TestState(STATE_NO_BTBCOMP) && IsBTBCompressionSupported()) {
300 if (checkOutBufType() == JPEG_BUF_TYPE_USER_PTR) {
301 if (!GetCompressor().SetJpegBuffer2(m_pIONThumbJpegBuffer, m_szIONThumbJpegBuffer)) {
302 ALOGE("Failed to configure thumbnail buffer @ %p(size %zu)", m_pIONThumbJpegBuffer,
303 m_szIONThumbJpegBuffer);
304 return false;
305 }
306 } else {
307 if (!GetCompressor().SetJpegBuffer2(m_fdIONThumbJpegBuffer, m_szIONThumbJpegBuffer)) {
308 ALOGE("Failed to configure thumbnail buffer @ %d(size %zu)", m_fdIONThumbJpegBuffer,
309 m_szIONThumbJpegBuffer);
310 return false;
311 }
312 }
313 }
314
315 return true;
316 }
317
encode(int * size,exif_attribute_t * exifInfo,char ** pcJpegBuffer,debug_attribute_t * debugInfo)318 int ExynosJpegEncoderForCamera::encode(int* size, exif_attribute_t* exifInfo, char** pcJpegBuffer,
319 debug_attribute_t* debugInfo) {
320 return encode(size, exifInfo, -1, pcJpegBuffer, debugInfo);
321 }
322
encode(int * size,exif_attribute_t * exifInfo,int fdJpegBuffer,char ** pcJpegBuffer,debug_attribute_t * debugInfo)323 int ExynosJpegEncoderForCamera::encode(int* size, exif_attribute_t* exifInfo, int fdJpegBuffer,
324 char** pcJpegBuffer, debug_attribute_t* debugInfo) {
325 if ((!debugInfo) || (debugInfo->num_of_appmarker == 0)) {
326 extra_appinfo_t* extra = NULL;
327 return encode(size, exifInfo, fdJpegBuffer, pcJpegBuffer, extra);
328 }
329
330 m_extraInfo.num_of_appmarker = 0;
331 memset(m_appInfo, 0, sizeof(m_appInfo));
332
333 ExtractDebugAttributeInfo(debugInfo, &m_extraInfo);
334
335 return encode(size, exifInfo, fdJpegBuffer, pcJpegBuffer, &m_extraInfo);
336 }
337
encode(int * size,exif_attribute_t * exifInfo,int fdJpegBuffer,char ** pcJpegBuffer,extra_appinfo_t * appInfo)338 int ExynosJpegEncoderForCamera::encode(int* size, exif_attribute_t* exifInfo, int fdJpegBuffer,
339 char** pcJpegBuffer, extra_appinfo_t* appInfo) {
340 if (!(*pcJpegBuffer)) {
341 ALOGE("Target stream buffer is not specified");
342 return -1;
343 }
344
345 if (*size <= 0) {
346 ALOGE("Too small stram buffer length %d bytes", *size);
347 return -1;
348 }
349
350 m_pStreamBase = *pcJpegBuffer;
351 m_nStreamSize = *size; // contains max buffer length until the compression finishes
352
353 char* jpeg_base = m_pStreamBase;
354
355 ALOGI_IF(!exifInfo, "Exif is not specified. Skipping writing APP1 marker");
356 ALOGI_IF(!appInfo, "Debugging information is not specified. Skipping writing APP4 marker");
357 ALOGD("Given stream buffer size: %d bytes", *size);
358
359 CStopWatch stopwatch(true);
360
361 if (!ProcessExif(jpeg_base, m_nStreamSize, exifInfo, appInfo)) return -1;
362
363 int offset = PTR_DIFF(m_pStreamBase, m_pAppWriter->GetMainStreamBase());
364 int buffsize = static_cast<int>(m_nStreamSize - offset);
365 if ((fdJpegBuffer < 0) ||
366 !(GetDeviceCapabilities() & V4L2_CAP_EXYNOS_JPEG_DMABUF_OFFSET)) { // JPEG_BUF_TYPE_USER_PTR
367 if (setOutBuf(m_pAppWriter->GetMainStreamBase(), buffsize) < 0) {
368 ALOGE("Failed to configure stream buffer : fd %d, addr %p, streamSize %d", fdJpegBuffer,
369 m_pAppWriter->GetMainStreamBase(), buffsize);
370 return -1;
371 }
372 } else { // JPEG_BUF_TYPE_DMA_BUF
373 if (setOutBuf(fdJpegBuffer, buffsize, offset) < 0) {
374 ALOGE("Failed to configure stream buffer : fd %d, addr %p, streamSize %d", fdJpegBuffer,
375 m_pAppWriter->GetMainStreamBase(), buffsize);
376 return -1;
377 }
378 }
379
380 bool block_mode = !TestState(STATE_HWFC_ENABLED);
381 bool thumbenc = m_pAppWriter->GetThumbStreamBase() != NULL;
382 size_t thumblen = 0;
383
384 // THUMB REQ? | THUMB IMG GIVEN? | B2B COMP? | HWFC(NONBLOCKING)?
385 // CASE1: O | X | - | X
386 // CASE2: O | X | - | O
387 // CASE3: O | O | X | X
388 // CASE4: O | O | O | X
389 // CASE5: O | O | O | O
390 // CASE6: X | - | - | -
391 // CASE7: O | O | X | O
392 //
393 // CASE1 = thumbenc && IsThumbGenerationNeeded() && block_mode
394 // CASE2 = thumbenc && IsThumbGenerationNeeded() && !block_mode
395 // CASE3 = thumbenc && !IsThumbGenerationNeeded() && !IsBTBCompressionSupported() && !block_mode
396 // CASE4 = thumbenc && !IsThumbGenerationNeeded() && !STATE_NO_BTBCOMP &&
397 // IsBTBCompressionSupported() && !block_mode CASE5 = thumbenc && !IsThumbGenerationNeeded() &&
398 // !STATE_NO_BTBCOMP && IsBTBCompressionSupported() && block_mode CASE6 = !thumbenc CASE7 =
399 // thumbenc && !IsThumbGenerationNeeded() && STATE_NO_BTBCOMP && block_mode
400
401 if (!thumbenc) {
402 // Confirm that no thumbnail information is transferred to HWJPEG
403 setThumbnailSize(0, 0);
404 } else if (!IsThumbGenerationNeeded() && IsBTBCompressionSupported() &&
405 (m_fThumbBufferType != checkInBufType())) {
406 ALOGE("Buffer types of thumbnail(%d) and main(%d) images should be the same",
407 m_fThumbBufferType, checkInBufType());
408 return -1;
409 } else if (!IsThumbGenerationNeeded() && (m_fThumbBufferType == 0)) {
410 // Thumbnail buffer configuration failed but the client forces to compress with thumbnail
411 ThumbGenerationNeeded();
412 SetState(STATE_THUMBSIZE_CHANGED);
413 }
414
415 if (!EnsureFormatIsApplied()) {
416 ALOGE("Failed to confirm format");
417 return -1;
418 }
419
420 if (!PrepareCompression(thumbenc)) {
421 ALOGE("Failed to prepare compression");
422 return -1;
423 }
424
425 ssize_t mainlen = GetCompressor().Compress(&thumblen, block_mode);
426 if (mainlen < 0) {
427 ALOGE("Error occured while JPEG compression: %zd", mainlen);
428 return -1;
429 }
430
431 if (mainlen == 0) { /* non-blocking compression */
432 ALOGD("Waiting for MCSC run");
433 return 0;
434 }
435
436 *size = static_cast<int>(FinishCompression(mainlen, thumblen));
437 if (*size < 0) return -1;
438
439 ALOGD("....compression delay(usec.): HW %u, Total %lu)", GetHWDelay(), stopwatch.GetElapsed());
440
441 return 0;
442 }
443
FinishCompression(size_t mainlen,size_t thumblen)444 ssize_t ExynosJpegEncoderForCamera::FinishCompression(size_t mainlen, size_t thumblen) {
445 bool btb = false;
446 size_t max_streamsize = m_nStreamSize;
447 char* mainbase = m_pAppWriter->GetMainStreamBase();
448 char* thumbbase = m_pAppWriter->GetThumbStreamBase();
449
450 m_nStreamSize = 0;
451
452 mainlen = RemoveTrailingDummies(mainbase, mainlen);
453
454 // Clearing SOI of the main image written by H/W
455 m_pAppWriter->GetMainStreamBase()[0] = 0;
456 m_pAppWriter->GetMainStreamBase()[1] = 0;
457
458 if (thumbbase) {
459 if (IsThumbGenerationNeeded()) {
460 void* len;
461 int ret = pthread_join(m_threadWorker, &len);
462 if (ret != 0) {
463 ALOGERR("Failed to wait thumbnail thread(%d)", ret);
464 return -1;
465 }
466
467 if (len == NULL)
468 ALOGE("Error occurred during thumbnail creation: no thumbnail is embedded");
469
470 thumblen = reinterpret_cast<size_t>(len);
471 } else if (TestState(STATE_NO_BTBCOMP) || !IsBTBCompressionSupported()) {
472 thumblen = CompressThumbnailOnly(m_pAppWriter->GetMaxThumbnailSize(), m_nThumbQuality,
473 getColorFormat(), checkInBufType());
474 } else {
475 btb = true;
476 }
477
478 size_t max_thumb = min(m_pAppWriter->GetMaxThumbnailSize(),
479 max_streamsize - m_pAppWriter->CalculateAPPSize(0) - mainlen);
480
481 if (thumblen > max_thumb) {
482 ALOGI("Too large thumbnail (%dx%d) stream size %zu (max: %zu, quality factor %d)",
483 m_nThumbWidth, m_nThumbHeight, thumblen, max_thumb, m_nThumbQuality);
484 ALOGI("Retrying thumbnail compression with quality factor 50");
485 thumblen = CompressThumbnailOnly(max_thumb, 50, getColorFormat(), checkInBufType());
486 if (thumblen == 0) return -1;
487 }
488
489 if (!m_pAppWriter->IsThumbSpaceReserved()) {
490 if (PTR_TO_ULONG(m_pStreamBase + max_streamsize) <
491 PTR_TO_ULONG(mainbase + mainlen + thumblen - JPEG_MARKER_SIZE)) {
492 ALOGE("Too small JPEG buffer length %zu (APP %zu, Main %zu, Thumb %zu)",
493 max_streamsize, m_pAppWriter->CalculateAPPSize(thumblen), mainlen, thumblen);
494 return -1;
495 }
496
497 // the SOI of the stream of the main image is stored after the APP4 or APP11 segment if
498 // they exist.
499 memmove(m_pAppWriter->GetApp1End() + thumblen, m_pAppWriter->GetApp1End(),
500 mainlen +
501 PTR_DIFF(m_pAppWriter->GetApp1End(),
502 m_pAppWriter->GetMainStreamBase()));
503 m_pAppWriter->UpdateApp1Size(thumblen);
504
505 // m_nAppLength has the value of appwriter.GetExactAPPSize()
506 // Therefore m_nStreamSize should be initialized with thumbnail stream length;
507 }
508
509 if (thumblen > 0) {
510 memcpy(m_pAppWriter->GetThumbStreamBase(), m_pIONThumbJpegBuffer, thumblen);
511 m_pAppWriter->Finalize(thumblen);
512 }
513
514 if (m_pAppWriter->IsThumbSpaceReserved()) {
515 // clear the possible stale data in the dummy area after the thumbnail stream
516 memset(m_pAppWriter->GetThumbStreamBase() + thumblen, 0,
517 m_pAppWriter->GetMaxThumbnailSize() - thumblen +
518 m_pAppWriter->GetAPP1ResrevedSize());
519 }
520 } else {
521 thumblen = 0;
522 }
523
524 m_nStreamSize += m_pAppWriter->CalculateAPPSize(thumblen) + mainlen;
525
526 /*
527 * m_nAppLength: The size of APP1 segment and APP4 segment including markers
528 * getJpegSize(): size of the compressed stream of the main image
529 * Note that 2 byte(size of SOI marker) is included in APP1 segment size.
530 * Thus the size of SOI marker in front of the stream is not added.
531 */
532 ALOGD("Completed image compression (%zd(thumb %zu) bytes, HWFC? %d, BTB? %d)", mainlen,
533 thumblen, TestState(STATE_HWFC_ENABLED), btb);
534
535 m_pStreamBase[0] = 0xFF;
536 m_pStreamBase[1] = 0xD8;
537
538 return m_nStreamSize;
539 }
540
541 /* The logic in WaitForHWFC() is the same with encode() */
WaitForCompression()542 ssize_t ExynosJpegEncoderForCamera::WaitForCompression() {
543 if (!TestState(STATE_HWFC_ENABLED)) return m_nStreamSize;
544
545 size_t thumblen = 0;
546 ssize_t streamlen = GetCompressor().WaitForCompression(&thumblen);
547 if (streamlen < 0) return streamlen;
548
549 return FinishCompression(streamlen, thumblen);
550 }
551
GenerateThumbnailImage()552 bool ExynosJpegEncoderForCamera::GenerateThumbnailImage() {
553 int main_width, main_height;
554 if (getSize(&main_width, &main_height) < 0) {
555 ALOGE("Failed to get main image size");
556 return false;
557 }
558
559 int v4l2Format = getColorFormat();
560
561 if (!AllocThumbBuffer(v4l2Format)) return false;
562
563 ALOGD("Generating thumbnail image: %dx%d -> %dx%d", main_width, main_height, m_nThumbWidth,
564 m_nThumbHeight);
565
566 if (!mThumbnailScaler) {
567 ALOGE("Thumbnail scaler is not prepared");
568 return false;
569 }
570
571 if (!mThumbnailScaler->SetSrcImage(main_width, main_height, v4l2Format)) {
572 ALOGE("Failed to configure the main image to the thumbnail scaler");
573 return false;
574 }
575
576 if (!mThumbnailScaler->SetDstImage(m_nThumbWidth, m_nThumbHeight,
577 GetThumbnailFormat(v4l2Format))) {
578 ALOGE("Failed to configure the target image to the thumbnail scaler");
579 return false;
580 }
581
582 bool okay = false;
583
584 if (checkInBufType() == JPEG_BUF_TYPE_USER_PTR) {
585 char* bufs[ThumbnailScaler::SCALER_MAX_PLANES];
586 int len_srcbufs[ThumbnailScaler::SCALER_MAX_PLANES];
587
588 if (getInBuf(bufs, len_srcbufs, ThumbnailScaler::SCALER_MAX_PLANES) < 0) {
589 ALOGE("Failed to retrieve the main image buffers");
590 return false;
591 }
592
593 okay = mThumbnailScaler->RunStream(bufs, len_srcbufs, m_fdIONThumbImgBuffer,
594 m_szIONThumbImgBuffer);
595 } else { // mainbuftype == JPEG_BUF_TYPE_DMA_BUF
596 int bufs[ThumbnailScaler::SCALER_MAX_PLANES];
597 int len_srcbufs[ThumbnailScaler::SCALER_MAX_PLANES];
598
599 if (getInBuf(bufs, len_srcbufs, ThumbnailScaler::SCALER_MAX_PLANES) < 0) {
600 ALOGE("Failed to retrieve the main image buffers");
601 return false;
602 }
603 okay = mThumbnailScaler->RunStream(bufs, len_srcbufs, m_fdIONThumbImgBuffer,
604 m_szIONThumbImgBuffer);
605 }
606
607 if (!okay) {
608 ALOGE("Failed to convert the main image to thumbnail with the thumbnail scaler");
609 return false;
610 }
611
612 return true;
613 }
614
CompressThumbnail()615 size_t ExynosJpegEncoderForCamera::CompressThumbnail() {
616 unsigned int v4l2Format = getColorFormat();
617 int buftype = checkInBufType();
618
619 if (IsThumbGenerationNeeded()) {
620 if (!GenerateThumbnailImage()) return 0;
621
622 // libcsc output configured by this class is always NV21.
623 v4l2Format = GetThumbnailFormat(getColorFormat());
624
625 buftype = JPEG_BUF_TYPE_DMA_BUF;
626 // reduced setInBuf2()
627 m_fdThumbnailImageBuffer[0] = m_fdIONThumbImgBuffer;
628 m_szThumbnailImageLen[0] = m_szIONThumbImgBuffer;
629 }
630
631 return CompressThumbnailOnly(m_pAppWriter->GetMaxThumbnailSize(), m_nThumbQuality, v4l2Format,
632 buftype);
633 }
634
AllocThumbBuffer(int v4l2Format)635 bool ExynosJpegEncoderForCamera::AllocThumbBuffer(int v4l2Format) {
636 if (m_fdIONClient < 0) {
637 ALOGE("ION client is not created");
638 return false;
639 }
640
641 size_t thumbbufsize = GetImageLength(m_nThumbWidth, m_nThumbHeight, v4l2Format);
642 if (thumbbufsize == 0) {
643 ALOGE("Unsupported V4L2 format %#X for thumbnail", v4l2Format);
644 return false;
645 }
646
647 if (m_fdIONThumbImgBuffer >= 0) {
648 if (m_szIONThumbImgBuffer >= thumbbufsize) return true;
649
650 if (m_pIONThumbImgBuffer != NULL) munmap(m_pIONThumbImgBuffer, m_szIONThumbImgBuffer);
651
652 close(m_fdIONThumbImgBuffer);
653
654 m_fdIONThumbImgBuffer = -1;
655 m_pIONThumbImgBuffer = NULL;
656 m_szIONThumbImgBuffer = 0;
657 }
658
659 m_fdIONThumbImgBuffer =
660 exynos_ion_alloc(m_fdIONClient, thumbbufsize, EXYNOS_ION_HEAP_SYSTEM_MASK, 0);
661 if (m_fdIONThumbImgBuffer < 0) {
662 ALOGERR("Failed to allocate %zu bytes for NV12 %ux%u", thumbbufsize, m_nThumbHeight,
663 m_nThumbWidth);
664 m_fdIONThumbImgBuffer = -1;
665 return false;
666 }
667
668 m_szIONThumbImgBuffer = thumbbufsize;
669
670 return AllocThumbJpegBuffer();
671 }
672
AllocThumbJpegBuffer()673 bool ExynosJpegEncoderForCamera::AllocThumbJpegBuffer() {
674 if (m_fdIONClient < 0) {
675 ALOGE("ION client is not created");
676 return false;
677 }
678
679 size_t thumbbufsize = m_nThumbHeight * m_nThumbWidth * 3;
680
681 if (m_pIONThumbJpegBuffer) {
682 if (m_szIONThumbJpegBuffer >= thumbbufsize) return true;
683
684 munmap(m_pIONThumbJpegBuffer, m_szIONThumbJpegBuffer);
685 close(m_fdIONThumbJpegBuffer);
686
687 m_szIONThumbJpegBuffer = 0;
688 m_pIONThumbJpegBuffer = NULL;
689 m_fdIONThumbJpegBuffer = -1;
690 }
691
692 m_fdIONThumbJpegBuffer =
693 exynos_ion_alloc(m_fdIONClient, thumbbufsize, EXYNOS_ION_HEAP_SYSTEM_MASK,
694 ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC);
695 if (m_fdIONThumbJpegBuffer < 0) {
696 ALOGERR("Failed to allocate %zu bytes for thumbnail stream buffer of %ux%u", thumbbufsize,
697 m_nThumbHeight, m_nThumbWidth);
698 return false;
699 }
700
701 m_pIONThumbJpegBuffer = reinterpret_cast<char*>(mmap(NULL, thumbbufsize, PROT_READ | PROT_WRITE,
702 MAP_SHARED, m_fdIONThumbJpegBuffer, 0));
703 if (m_pIONThumbJpegBuffer == MAP_FAILED) {
704 ALOGERR("Failed to map thumbnail stream buffer (%zu bytes)", thumbbufsize);
705
706 m_pIONThumbJpegBuffer = NULL;
707 } else {
708 m_szIONThumbJpegBuffer = thumbbufsize;
709 }
710
711 return m_pIONThumbJpegBuffer != NULL;
712 }
713
CompressThumbnailOnly(size_t limit,int quality,unsigned int v4l2Format,int src_buftype)714 size_t ExynosJpegEncoderForCamera::CompressThumbnailOnly(size_t limit, int quality,
715 unsigned int v4l2Format, int src_buftype) {
716 if (!m_phwjpeg4thumb->SetImageFormat(v4l2Format, m_nThumbWidth, m_nThumbHeight)) {
717 ALOGE("Failed to configure thumbnail source image format to %#010x, %ux%u", v4l2Format,
718 m_nThumbWidth, m_nThumbHeight);
719 return 0;
720 }
721
722 unsigned int num_buffers = 1;
723 switch (v4l2Format) {
724 case V4L2_PIX_FMT_YUV420M:
725 [[fallthrough]];
726 case V4L2_PIX_FMT_YVU420M:
727 num_buffers++;
728 [[fallthrough]];
729 case V4L2_PIX_FMT_NV12M:
730 [[fallthrough]];
731 case V4L2_PIX_FMT_NV21M:
732 num_buffers++;
733 break;
734 }
735
736 if (src_buftype == JPEG_BUF_TYPE_USER_PTR) {
737 if (!m_phwjpeg4thumb->SetImageBuffer(m_pThumbnailImageBuffer, m_szThumbnailImageLen,
738 num_buffers)) {
739 ALOGE("Failed to configure thumbnail buffers(userptr) for thumbnail");
740 return 0;
741 }
742 } else { // JPEG_BUF_TYPE_DMA_BUF
743 if (!m_phwjpeg4thumb->SetImageBuffer(m_fdThumbnailImageBuffer, m_szThumbnailImageLen,
744 num_buffers)) {
745 ALOGE("Failed to configure thumbnail buffers(dmabuf) for thumbnail");
746 return 0;
747 }
748 }
749
750 if (!m_phwjpeg4thumb->SetJpegBuffer(m_fdIONThumbJpegBuffer, m_szIONThumbJpegBuffer)) {
751 ALOGE("Failed to configure thumbnail stream buffer (fd %d, size %zu)",
752 m_fdIONThumbJpegBuffer, m_szIONThumbJpegBuffer);
753 return 0;
754 }
755
756 // Since the compressed stream of the thumbnail image is to be embedded in
757 // APP1 segment, at the end of Exif metadata, the length of the stream should
758 // not exceed the maximum length of a segment, 64KB minus the length of Exif
759 // metadata. If the stream length is too large, repeat the compression until
760 // the length become proper to embed.
761 do {
762 if (!m_phwjpeg4thumb->SetQuality(quality)) {
763 ALOGE("Failed to configure thumbnail quality factor %u", quality);
764 return 0;
765 }
766
767 ssize_t thumbsize = m_phwjpeg4thumb->Compress();
768 if (thumbsize < 0) {
769 ALOGE("Failed to compress thumbnail");
770 return 0;
771 }
772
773 thumbsize = RemoveTrailingDummies(m_pIONThumbJpegBuffer, thumbsize);
774 if (static_cast<size_t>(thumbsize) > limit) {
775 quality = min(50, quality - 10);
776 ALOGI_IF(quality >= 20,
777 "Too large thumbnail stream size %zu. Retrying with quality factor %d...",
778 thumbsize, quality);
779 } else {
780 return thumbsize;
781 }
782 } while (quality >= 20);
783
784 ALOG_ASSERT(false, "It should never reach here");
785 ALOGE("Thumbnail compression finally failed");
786
787 return 0;
788 }
789
setInBuf2(int * piBuf,int * iSize)790 int ExynosJpegEncoderForCamera::setInBuf2(int* piBuf, int* iSize) {
791 NoThumbGenerationNeeded();
792
793 if (!EnsureFormatIsApplied()) return -1;
794
795 CHWJpegCompressor& hwjpeg = GetCompressor();
796 unsigned int num_buffers = 3;
797 if (!hwjpeg.GetImageBufferSizes(m_szThumbnailImageLen, &num_buffers)) {
798 ALOGE("Failed to get image buffer sizes");
799 return -1;
800 }
801
802 for (unsigned int i = 0; i < num_buffers; i++) {
803 m_szThumbnailImageLen[i] = iSize[i];
804 m_fdThumbnailImageBuffer[i] = piBuf[i];
805 }
806
807 if (IsBTBCompressionSupported() &&
808 !hwjpeg.SetImageBuffer2(m_fdThumbnailImageBuffer, m_szThumbnailImageLen, num_buffers)) {
809 ALOGE("Failed to configure thumbnail buffers");
810 return -1;
811 }
812
813 m_fThumbBufferType = JPEG_BUF_TYPE_DMA_BUF;
814
815 return 0;
816 }
817
setInBuf2(char ** pcBuf,int * iSize)818 int ExynosJpegEncoderForCamera::setInBuf2(char** pcBuf, int* iSize) {
819 NoThumbGenerationNeeded();
820
821 if (!EnsureFormatIsApplied()) return -1;
822
823 CHWJpegCompressor& hwjpeg = GetCompressor();
824 unsigned int num_buffers = 3;
825 if (!hwjpeg.GetImageBufferSizes(m_szThumbnailImageLen, &num_buffers)) {
826 ALOGE("Failed to get image buffer sizes");
827 return -1;
828 }
829
830 for (unsigned int i = 0; i < num_buffers; i++) {
831 m_szThumbnailImageLen[i] = iSize[i];
832 m_pThumbnailImageBuffer[i] = pcBuf[i];
833 }
834
835 if (IsBTBCompressionSupported() &&
836 !hwjpeg.SetImageBuffer2(m_pThumbnailImageBuffer, m_szThumbnailImageLen, num_buffers)) {
837 ALOGE("Failed to configure thumbnail buffers");
838 return -1;
839 }
840
841 m_fThumbBufferType = JPEG_BUF_TYPE_USER_PTR;
842
843 return 0;
844 }
845
GetThumbnailImage(char * buffer,size_t buflen)846 size_t ExynosJpegEncoderForCamera::GetThumbnailImage(char* buffer, size_t buflen) {
847 if (m_fdIONThumbImgBuffer < 0) {
848 ALOGE("No internal thumbnail buffer is allocated");
849 return 0;
850 }
851
852 size_t thumbbufsize = GetImageLength(m_nThumbWidth, m_nThumbHeight, getColorFormat());
853 if (buflen < thumbbufsize) {
854 ALOGE("Too small buffer %zu (thumbnail image size %zu)", buflen, thumbbufsize);
855 return 0;
856 }
857
858 ALOG_ASSERT(m_szIONThumbImgBuffer >= thumbbufsize,
859 "m_szIONThumbImgBuffer(%zu) is smaller than the thumbnail (%zu)",
860 m_szIONThumbImgBuffer, thumbbufsize);
861 if (m_pIONThumbImgBuffer == NULL) {
862 m_pIONThumbImgBuffer = reinterpret_cast<char*>(
863 mmap(NULL, m_szIONThumbImgBuffer, PROT_READ, MAP_SHARED, m_fdIONThumbImgBuffer, 0));
864 if (m_pIONThumbImgBuffer == MAP_FAILED) {
865 m_pIONThumbImgBuffer = NULL;
866 ALOGERR("Failed to map thumbnail image buffer (%zu bytes)", m_szIONThumbImgBuffer);
867 return 0;
868 }
869 }
870
871 memcpy(buffer, m_pIONThumbImgBuffer, thumbbufsize);
872
873 ALOGD("Copied thumbnail image to %p (%zu bytes)", buffer, thumbbufsize);
874
875 return m_szIONThumbImgBuffer;
876 }
877