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