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 <cstdio>
19
20 #include <cstring>
21 #include <unistd.h>
22 #include <sys/ioctl.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <sys/mman.h>
26 #include <fcntl.h>
27
28 #include <linux/videodev2.h>
29
30 #include <exynos-hwjpeg.h>
31 #include <hwjpeglib-exynos.h>
32
33 #include "hwjpeg-internal.h"
34
35 #define ALOGERR(fmt, args...) ((void)ALOG(LOG_ERROR, LOG_TAG, fmt " [%s]", ##args, strerror(errno)))
36
37 #define ROUND_DOWN(val, denom) ((val) & ~((denom) - 1))
38 #define ROUND_UP(val, denom) ROUND_DOWN((val) + (denom) - 1, denom)
39 #define TO_MASK(val) ((val) - 1)
40
41 class CJpegStreamParser {
42 private:
43 unsigned char *m_pStreamBase;
44 size_t m_nStreamSize;
45
46 unsigned char m_nComponents;
47 unsigned short m_nWidth;
48 unsigned short m_nHeight;
49
50 void Initialize();
51 size_t GetLength(unsigned char *addr);
52 bool ParseFrame(unsigned char *addr);
53
GetOffset(unsigned char * addr)54 off_t GetOffset(unsigned char *addr) {
55 unsigned long beg = reinterpret_cast<unsigned long>(m_pStreamBase);
56 unsigned long cur = reinterpret_cast<unsigned long>(addr);
57 return static_cast<off_t>(cur - beg);
58 }
59
60 public:
61 unsigned char m_iHorizontalFactor;
62 unsigned char m_iVerticalFactor;
63
CJpegStreamParser()64 CJpegStreamParser() : m_pStreamBase(NULL), m_nStreamSize(0) { }
~CJpegStreamParser()65 ~CJpegStreamParser() { }
66
67 bool Parse(unsigned char *streambase, size_t length);
68
69 int GetImageFormat();
GetWidth()70 unsigned int GetWidth() { return m_nWidth; }
GetHeight()71 unsigned int GetHeight() { return m_nHeight; }
GetNumComponents()72 unsigned int GetNumComponents() { return m_nComponents; }
73 };
74
Initialize()75 void CJpegStreamParser::Initialize()
76 {
77 m_nComponents = 0;
78 m_nWidth = 0;
79 m_nHeight = 0;
80 m_iHorizontalFactor = 1;
81 m_iVerticalFactor = 1;
82 }
83
GetLength(unsigned char * addr)84 size_t CJpegStreamParser::GetLength(unsigned char *addr)
85 {
86 size_t len = static_cast<size_t>(*addr++) * 0x100;
87 return len + *addr;
88 }
89
Parse(unsigned char * streambase,size_t length)90 bool CJpegStreamParser::Parse(unsigned char *streambase, size_t length)
91 {
92 Initialize();
93
94 m_pStreamBase = streambase;
95 m_nStreamSize = length;
96
97 unsigned char *addr = m_pStreamBase;
98 size_t filelen = m_nStreamSize;
99
100 // Finding SOI (xFFD8)
101 if ((filelen < 2) || (addr[0] != 0xFF) || (addr[1] != 0xD8)) {
102 ALOGE("Not a valid JPEG stream (len %zu, marker %02x%02x", filelen, addr[0], addr[1]);
103 return false;
104 }
105 addr += 2;
106 filelen -= 2;
107
108 while (true) { // DHT, DQT, SOF, SOS
109 if (filelen < 2) {
110 ALOGE("Incomplete JPEG Stream");
111 return false;
112 }
113
114 if (*addr++ != 0xFF) {
115 ALOGE("Corrupted JPEG stream");
116 return false;
117 }
118
119 unsigned char marker = *addr++;
120
121 if ((marker != 0xC4) && ((marker & 0xF0) == 0xC0)) { // SOFn
122 if (marker != 0xC0) {
123 ALOGE("SOF%d is not supported (offset %zu)", marker & 0xF, m_nStreamSize - filelen);
124 return false;
125 }
126
127 if (filelen < GetLength(addr)) {
128 ALOGE("Too small SOF0 segment");
129 return false;
130 }
131
132 if (!ParseFrame(addr))
133 return false;
134
135 return true; // this is the successful exit point
136 } else if (marker == 0xD9) { // EOI
137 // This will not meet.
138 ALOGE("Unexpected EOI found at %lu\n", GetOffset(addr - 2));
139 return false;
140 } else {
141 if ((marker == 0xCC) || (marker == 0xDC)) { // DAC and DNL
142 ALOGE("Unsupported JPEG stream: found marker 0xFF%02X", marker);
143 return false;
144 }
145
146 if (filelen < GetLength(addr)) {
147 ALOGE("Corrupted JPEG stream");
148 return false;
149 }
150 }
151
152 if (GetLength(addr) == 0) {
153 ALOGE("Invalid length 0 is read at offset %lu", GetOffset(addr));
154 return false;
155 }
156
157 filelen -= GetLength(addr);
158 addr += GetLength(addr);
159 }
160
161 // NEVER REACH HERE
162
163 ALOGE("Unable to find the frame header");
164
165 return false;
166 }
167
ParseFrame(unsigned char * addr)168 bool CJpegStreamParser::ParseFrame(unsigned char *addr)
169 { // 2 bytes of length
170 // 1 byte of bits per sample
171 // 2 bytes of height
172 // 2 bytes of width
173 // 1 byte of number of components
174 // n * 3 byte component specifications
175 if (GetLength(addr) < 17) {
176 ALOGE("SOF0 should include all three components");
177 return false;
178 }
179 addr += 2; // skip length
180
181 if (*addr != 8) { // bits per sample
182 ALOGE("Bits Per Sample should be 8 but it is %d", *addr);
183 return false;
184 }
185 addr++;
186
187 m_nHeight = static_cast<unsigned short>(GetLength(addr));
188 if ((m_nHeight < 8) || (m_nHeight > 16383)) {
189 ALOGE("Height %d is not supported", m_nHeight);
190 return false;
191 }
192 addr += 2;
193
194 m_nWidth = static_cast<unsigned short>(GetLength(addr));
195 if ((m_nWidth < 8) || (m_nWidth > 16383)) {
196 ALOGE("Width %d is not supported", m_nWidth);
197 return false;
198 }
199 addr += 2;
200
201 m_nComponents = *addr;
202 if (m_nComponents != 3) {
203 ALOGE("Number of components should be 3 but it is %d", m_nComponents);
204 return false;
205 }
206 addr++;
207
208 // Only the first component is needed to find chroma subsampling factor
209 addr++; // skip component identifier
210 if ((*addr != 0x11) && (*addr != 0x21) && (*addr != 0x12) && (*addr != 0x22)) {
211 ALOGE("Invalid Luma sampling factor %#02x", *addr);
212 return false;
213 }
214 m_iHorizontalFactor = *addr >> 4;
215 m_iVerticalFactor = *addr & 0xF;
216
217 return true;
218 }
219
220 class CLibhwjpegDecompressor: public hwjpeg_decompressor_struct {
221 enum {
222 HWJPG_FLAG_NEED_MUNMAP = 1,
223 };
224
225 unsigned int m_flags;
226 bool m_bPrepared;
227 CHWJpegDecompressor *m_hwjpeg;
228
229 unsigned char *m_pStreamBuffer;
230 size_t m_nStreamLength;
231 size_t m_nDummyBytes;
232
233 CJpegStreamParser m_jpegStreamParser;
234 public:
CLibhwjpegDecompressor()235 CLibhwjpegDecompressor() : m_flags(0) {
236 // members of hwjpeg_decompressor_struct
237 image_width = 0;
238 image_height = 0;
239 num_components = 3;
240 chroma_h_samp_factor = 1;
241 chroma_v_samp_factor = 1;
242 scale_factor = 1;
243 output_width = 0;
244 output_height = 0;
245 m_bPrepared = false;
246 m_pStreamBuffer = NULL;
247
248 output_format = V4L2_PIX_FMT_RGB32;
249
250 // members of this
251 m_nStreamLength = 0;
252 m_nDummyBytes = 0;
253
254 m_hwjpeg = new CHWJpegV4L2Decompressor;
255 if (!m_hwjpeg || !*m_hwjpeg) {
256 ALOGE("Failed to create HWJPEG decompressor");
257 delete m_hwjpeg;
258 }
259 }
260
~CLibhwjpegDecompressor()261 ~CLibhwjpegDecompressor() {
262 delete m_hwjpeg;
263
264 if (!!(m_flags & HWJPG_FLAG_NEED_MUNMAP))
265 munmap(m_pStreamBuffer, m_nStreamLength + m_nDummyBytes);
266 }
267
SetStreamPath(const char * path)268 bool SetStreamPath(const char *path) {
269 if ((m_pStreamBuffer != NULL) && !!(m_flags & HWJPG_FLAG_NEED_MUNMAP)) {
270 munmap(m_pStreamBuffer, m_nStreamLength + m_nDummyBytes);
271 m_flags &= ~HWJPG_FLAG_NEED_MUNMAP;
272 m_pStreamBuffer = NULL;
273 m_nStreamLength = 0;
274 }
275
276 int fd = open(path, O_RDONLY);
277 if (fd < 0) {
278 ALOGERR("Failed to open '%s' for decompression", path);
279 return false;
280 }
281
282 struct stat st;
283 if (fstat(fd, &st) < 0) {
284 ALOGERR("Failed to read size of '%s'", path);
285 close(fd);
286 return false;
287 }
288
289 m_nStreamLength = st.st_size;
290 m_nDummyBytes = 0;
291
292 m_pStreamBuffer = reinterpret_cast<unsigned char *>(
293 mmap(NULL, m_nStreamLength,
294 PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0));
295 if (m_pStreamBuffer == MAP_FAILED) {
296 m_pStreamBuffer = NULL;
297 close(fd);
298 ALOGERR("Failed to mmap %zu bytes of '%s'", m_nStreamLength, path);
299 return false;
300 }
301
302 m_bPrepared = false;
303
304 m_flags |= HWJPG_FLAG_NEED_MUNMAP;
305
306 close(fd);
307 return true;
308 }
309
SetStreamBuffer(unsigned char * buffer,size_t len,size_t dummybytes)310 bool SetStreamBuffer(unsigned char *buffer, size_t len, size_t dummybytes) {
311 if ((m_pStreamBuffer != NULL) && !!(m_flags & HWJPG_FLAG_NEED_MUNMAP)) {
312 munmap(m_pStreamBuffer, m_nStreamLength + m_nDummyBytes);
313 m_flags &= ~HWJPG_FLAG_NEED_MUNMAP;
314 }
315
316 m_pStreamBuffer = buffer;
317 m_nStreamLength = len;
318 m_nDummyBytes = dummybytes;
319
320 m_bPrepared = false;
321
322 return true;
323 }
324
SetStreamBuffer(int buffer,size_t len,size_t dummybytes)325 bool SetStreamBuffer(int buffer, size_t len, size_t dummybytes) {
326 if ((m_pStreamBuffer != NULL) && !!(m_flags & HWJPG_FLAG_NEED_MUNMAP)) {
327 munmap(m_pStreamBuffer, m_nStreamLength + m_nDummyBytes);
328 m_flags &= ~HWJPG_FLAG_NEED_MUNMAP;
329 }
330
331 m_nStreamLength = len;
332 m_nDummyBytes = dummybytes;
333
334 m_pStreamBuffer = reinterpret_cast<unsigned char *>(
335 mmap(NULL, m_nStreamLength + m_nDummyBytes,
336 PROT_READ | PROT_WRITE, MAP_SHARED, buffer, 0));
337 if (m_pStreamBuffer == MAP_FAILED) {
338 m_pStreamBuffer = NULL;
339 ALOGERR("Failed to mmap %zu bytes of dmabuf fd %d", m_nStreamLength, buffer);
340 return false;
341 }
342
343 m_flags |= HWJPG_FLAG_NEED_MUNMAP;
344
345 m_bPrepared = false;
346
347 return true;
348 }
349
SetImageBuffer(unsigned char * buffer[3],size_t len[3],unsigned int num_bufs)350 bool SetImageBuffer(unsigned char *buffer[3], size_t len[3], unsigned int num_bufs) {
351 if (num_bufs != 1) {
352 ALOGE("multi-planar image is not supported(%u planes)", num_bufs);
353 return false;
354 }
355
356 return m_hwjpeg->SetImageBuffer(reinterpret_cast<char *>(buffer[0]), len[0]);
357 }
358
SetImageBuffer(int buffer[3],size_t len[3],unsigned int num_bufs)359 bool SetImageBuffer(int buffer[3], size_t len[3], unsigned int num_bufs) {
360 if (num_bufs != 1) {
361 ALOGE("multi-planar image is not supported(%u planes)", num_bufs);
362 return false;
363 }
364
365 return m_hwjpeg->SetImageBuffer(buffer[0], len[0]);
366 }
367
SetDownscaleFactor(unsigned int factor)368 void SetDownscaleFactor(unsigned int factor) { scale_factor = factor; }
369
370 bool PrepareDecompression();
371 bool Decompress();
372
IsEnoughStreamBuffer()373 bool IsEnoughStreamBuffer() { return true; }
374 };
375
PrepareDecompression()376 bool CLibhwjpegDecompressor::PrepareDecompression()
377 {
378 if (!m_hwjpeg) {
379 ALOGE("device node is not opened!");
380 return false;
381 }
382
383 if ((scale_factor != 1) && (scale_factor != 2) &&
384 (scale_factor != 4) && (scale_factor != 8)) {
385 ALOGE("Invalid downscaling factor %d", scale_factor);
386 return false;
387 }
388
389 if (m_pStreamBuffer == NULL) {
390 ALOGE("No stream buffer is configured");
391 return false;
392 }
393
394 if (!m_jpegStreamParser.Parse(m_pStreamBuffer, m_nStreamLength))
395 return false;
396
397 image_width = m_jpegStreamParser.GetWidth();
398 image_height = m_jpegStreamParser.GetHeight();
399 num_components = m_jpegStreamParser.GetNumComponents();
400 chroma_h_samp_factor = m_jpegStreamParser.m_iHorizontalFactor;
401 chroma_v_samp_factor = m_jpegStreamParser.m_iVerticalFactor;
402
403 if (((image_width % (chroma_h_samp_factor * scale_factor)) != 0) ||
404 ((image_height % (chroma_v_samp_factor * scale_factor)) != 0)) {
405 ALOGE("Downscaling by factor %d of compressed image size %dx%d(chroma %d:%d) is not supported",
406 scale_factor, image_width, image_height, chroma_h_samp_factor, chroma_v_samp_factor);
407 return false;
408 }
409
410 output_width = image_width / scale_factor;
411 output_height = image_height / scale_factor;
412
413 if (!m_hwjpeg->SetStreamPixelSize(image_width, image_height)) {
414 ALOGE("Failed to configure stream pixel size (%ux%u)", image_width, image_height);
415 return false;
416 }
417
418 if (!m_hwjpeg->SetImageFormat(output_format, output_width, output_height)) {
419 ALOGE("Failed to configure image format (%ux%u/%08X)", output_width, output_height, output_format);
420 return false;
421 }
422
423 m_bPrepared = true;
424
425 return true;
426 }
427
Decompress()428 bool CLibhwjpegDecompressor::Decompress()
429 {
430 if (!m_bPrepared) {
431 ALOGE("JPEG header is not parsed");
432 return false;
433 }
434
435 if (!IsEnoughStreamBuffer()) {
436 ALOGE("Not enough buffer length for HWJPEG");
437 return false;
438 }
439
440 m_bPrepared = false;
441
442 if (!m_hwjpeg->Decompress(reinterpret_cast<char *>(m_pStreamBuffer), m_nStreamLength)) {
443 ALOGE("Failed to decompress");
444 return false;
445 }
446
447 return true;
448 }
449
hwjpeg_create_decompress()450 hwjpeg_decompress_ptr hwjpeg_create_decompress()
451 {
452 hwjpeg_decompress_ptr p = new CLibhwjpegDecompressor();
453 if (!p)
454 ALOGE("Failed to create decompress struct");
455 return p;
456 }
457
hwjpeg_file_src(hwjpeg_decompress_ptr cinfo,const char * path)458 bool hwjpeg_file_src(hwjpeg_decompress_ptr cinfo, const char *path)
459 {
460 CLibhwjpegDecompressor *decomp = reinterpret_cast<CLibhwjpegDecompressor *>(cinfo);
461 return decomp->SetStreamPath(path);
462 }
463
hwjpeg_config_image_format(hwjpeg_decompress_ptr cinfo,__u32 v4l2_pix_fmt)464 void hwjpeg_config_image_format(hwjpeg_decompress_ptr cinfo, __u32 v4l2_pix_fmt)
465 {
466 cinfo->output_format = v4l2_pix_fmt;
467 }
468
hwjpeg_dmabuf_src(hwjpeg_decompress_ptr cinfo,int infd,size_t insize,size_t dummybytes)469 bool hwjpeg_dmabuf_src(hwjpeg_decompress_ptr cinfo, int infd, size_t insize, size_t dummybytes)
470 {
471 CLibhwjpegDecompressor *decomp = reinterpret_cast<CLibhwjpegDecompressor *>(cinfo);
472 return decomp->SetStreamBuffer(infd, insize, dummybytes);
473 }
474
hwjpeg_mem_src(hwjpeg_decompress_ptr cinfo,unsigned char * inbuffer,size_t insize,size_t dummybytes)475 bool hwjpeg_mem_src(hwjpeg_decompress_ptr cinfo,
476 unsigned char *inbuffer, size_t insize, size_t dummybytes)
477 {
478 CLibhwjpegDecompressor *decomp = reinterpret_cast<CLibhwjpegDecompressor *>(cinfo);
479 return decomp->SetStreamBuffer(inbuffer, insize, dummybytes);
480 }
481
hwjpeg_mem_dst(hwjpeg_decompress_ptr cinfo,unsigned char * outbuffer[],size_t outsize[],unsigned int num_buffers)482 bool hwjpeg_mem_dst(hwjpeg_decompress_ptr cinfo,
483 unsigned char *outbuffer[], size_t outsize[], unsigned int num_buffers)
484 {
485 CLibhwjpegDecompressor *decomp = reinterpret_cast<CLibhwjpegDecompressor *>(cinfo);
486 return decomp->SetImageBuffer(outbuffer, outsize, num_buffers);
487 }
488
hwjpeg_dmabuf_dst(hwjpeg_decompress_ptr cinfo,int outfd[],size_t outsize[],unsigned int num_buffers)489 bool hwjpeg_dmabuf_dst(hwjpeg_decompress_ptr cinfo,
490 int outfd[], size_t outsize[], unsigned int num_buffers)
491 {
492 CLibhwjpegDecompressor *decomp = reinterpret_cast<CLibhwjpegDecompressor *>(cinfo);
493 return decomp->SetImageBuffer(outfd, outsize, num_buffers);
494 }
495
hwjpeg_set_downscale_factor(hwjpeg_decompress_ptr cinfo,unsigned int factor)496 void hwjpeg_set_downscale_factor(hwjpeg_decompress_ptr cinfo, unsigned int factor)
497 {
498 cinfo->scale_factor = factor;
499 }
500
hwjpeg_read_header(hwjpeg_decompress_ptr cinfo)501 bool hwjpeg_read_header(hwjpeg_decompress_ptr cinfo)
502 {
503 CLibhwjpegDecompressor *decomp = reinterpret_cast<CLibhwjpegDecompressor *>(cinfo);
504 return decomp->PrepareDecompression();
505 }
506
hwjpeg_start_decompress(hwjpeg_decompress_ptr cinfo)507 bool hwjpeg_start_decompress(hwjpeg_decompress_ptr cinfo)
508 {
509 CLibhwjpegDecompressor *decomp = reinterpret_cast<CLibhwjpegDecompressor *>(cinfo);
510 return decomp->Decompress();
511 }
512
hwjpeg_destroy_decompress(hwjpeg_decompress_ptr cinfo)513 void hwjpeg_destroy_decompress(hwjpeg_decompress_ptr cinfo)
514 {
515 CLibhwjpegDecompressor *decomp = reinterpret_cast<CLibhwjpegDecompressor *>(cinfo);
516 delete decomp;
517 }
518
hwjpeg_has_enough_stream_buffer(hwjpeg_decompress_ptr cinfo)519 bool hwjpeg_has_enough_stream_buffer(hwjpeg_decompress_ptr cinfo)
520 {
521 CLibhwjpegDecompressor *decomp = reinterpret_cast<CLibhwjpegDecompressor *>(cinfo);
522 return decomp->IsEnoughStreamBuffer();
523 }
524