• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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