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