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