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