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