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