blob: e0c93dc8ce2352321e064cc8d64e21d938953a40 [file] [log] [blame]
Parker Schuh44f86922017-01-03 23:59:50 -08001#include "aos/vision/image/jpeg_routines.h"
2
3#include <errno.h>
4#include <setjmp.h>
5#include <stdint.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <sys/mman.h>
10#include <unistd.h>
11#include <cstring>
12
13#include "aos/common/logging/logging.h"
14#include "third_party/libjpeg/jpeglib.h"
15
16namespace aos {
17namespace vision {
18
19namespace {
20
21void decompress_add_huff_table(j_decompress_ptr cinfo, JHUFF_TBL **htblptr,
22 const UINT8 *bits, const UINT8 *val);
23
24void standard_huff_tables(j_decompress_ptr cinfo);
25
26// Error handling form libjpeg
27struct JpegErrorManager {
28 /// "public" fields
29 struct jpeg_error_mgr pub;
30 // for return to caller
31 jmp_buf setjmp_buffer;
32};
33
34char JpegLastErrorMsg[JMSG_LENGTH_MAX];
35
36// TODO(parker): Error handling needs to be investigated bettter.
37void JpegErrorExit(j_common_ptr cinfo) {
38 JpegErrorManager myerr;
39 // cinfo->err actually points to a JpegErrorManager struct
40 ::std::memcpy(&myerr, cinfo->err, sizeof(myerr));
41 // JpegErrorManager* myerr = (JpegErrorManager*) cinfo->err;
42 // note : *(cinfo->err) is now equivalent to myerr->pub
43
44 // output_message is a method to print an error message
45 //(* (cinfo->err->output_message) ) (cinfo);
46
47 // Create the message
48 (*(cinfo->err->format_message))(cinfo, JpegLastErrorMsg);
49
50 // Jump to the setjmp point
51 longjmp(myerr.setjmp_buffer, 1);
52}
53
54// This is also adapted from libjpeg to be used on decompression tables rather
55// than compression tables as it was originally intended.
56void decompress_add_huff_table(j_decompress_ptr cinfo, JHUFF_TBL **htblptr,
57 const UINT8 *bits, const UINT8 *val) {
58 if (*htblptr == NULL) *htblptr = jpeg_alloc_huff_table((j_common_ptr)cinfo);
59
60 // Copy the number-of-symbols-of-each-code-length counts.
61 memcpy((*htblptr)->bits, bits, sizeof((*htblptr)->bits));
62
63 // Validate the counts. We do this here mainly so we can copy the right
64 // number of symbols from the val[] array, without risking marching off
65 // the end of memory. jchuff.c will do a more thorough test later.
66 int nsymbols = 0;
67 for (int len = 1; len <= 16; len++) nsymbols += bits[len];
68 if (nsymbols < 1 || nsymbols > 256) {
69 LOG(FATAL, "%s:%d: Error, bad huffman table", __FILE__, __LINE__);
70 }
71
72 memcpy((*htblptr)->huffval, val, nsymbols * sizeof(uint8_t));
73}
74
75// standard_huff_tables is taken from libjpeg compression stuff
76// and is here used to set up the same tables in the decompression structure.
77// Set up the standard Huffman tables (cf. JPEG standard section K.3)
78// IMPORTANT: these are only valid for 8-bit data precision!
79void standard_huff_tables(j_decompress_ptr cinfo) {
80 /* 0-base on first 0, */
81 static const UINT8 bits_dc_luminance[17] = {0, 0, 1, 5, 1, 1, 1, 1, 1,
82 1, 0, 0, 0, 0, 0, 0, 0};
83 static const UINT8 val_dc_luminance[] = {0, 1, 2, 3, 4, 5,
84 6, 7, 8, 9, 10, 11};
85
86 /* 0-base on first 0 */
87 static const UINT8 bits_dc_chrominance[17] = {0, 0, 3, 1, 1, 1, 1, 1, 1,
88 1, 1, 1, 0, 0, 0, 0, 0};
89 static const UINT8 val_dc_chrominance[] = {0, 1, 2, 3, 4, 5,
90 6, 7, 8, 9, 10, 11};
91
92 /* 0-base on first 0 */
93 static const UINT8 bits_ac_luminance[17] = {0, 0, 2, 1, 3, 3, 2, 4, 3,
94 5, 5, 4, 4, 0, 0, 1, 0x7d};
95 static const UINT8 val_ac_luminance[] = {
96 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06,
97 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
98 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72,
99 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
100 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45,
101 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
102 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75,
103 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
104 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3,
105 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
106 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9,
107 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
108 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4,
109 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa};
110
111 /* 0-base on first 0 */
112 static const UINT8 bits_ac_chrominance[17] = {0, 0, 2, 1, 2, 4, 4, 3, 4,
113 7, 5, 4, 4, 0, 1, 2, 0x77};
114 static const UINT8 val_ac_chrominance[] = {
115 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41,
116 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
117 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1,
118 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
119 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44,
120 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
121 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74,
122 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
123 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a,
124 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
125 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
126 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
127 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4,
128 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa};
129
130 decompress_add_huff_table(cinfo, &cinfo->dc_huff_tbl_ptrs[0],
131 bits_dc_luminance, val_dc_luminance);
132 decompress_add_huff_table(cinfo, &cinfo->ac_huff_tbl_ptrs[0],
133 bits_ac_luminance, val_ac_luminance);
134 decompress_add_huff_table(cinfo, &cinfo->dc_huff_tbl_ptrs[1],
135 bits_dc_chrominance, val_dc_chrominance);
136 decompress_add_huff_table(cinfo, &cinfo->ac_huff_tbl_ptrs[1],
137 bits_ac_chrominance, val_ac_chrominance);
138}
139
140void local_emit_message(jpeg_common_struct * /*cinfo*/, int /*msg_level*/) {
141 return;
142}
143
144} // namespace
145
146ImageFormat GetFmt(DataRef data) {
147 ImageFormat fmt;
148 struct jpeg_decompress_struct cinfo;
149 struct jpeg_error_mgr jerr;
150
151 cinfo.err = jpeg_std_error(&jerr);
152 jerr.emit_message = local_emit_message;
153
154 cinfo.out_color_space = JCS_RGB;
155 jpeg_create_decompress(&cinfo);
156
157 jpeg_mem_src(&cinfo, reinterpret_cast<unsigned char *>(
158 const_cast<char *>(data.data())),
159 data.size());
160
161 jpeg_read_header(&cinfo, TRUE);
162 fmt.w = cinfo.image_width;
163 fmt.h = cinfo.image_height;
164 jpeg_destroy_decompress(&cinfo);
165 return fmt;
166}
167
168// Returns true if successful false if an error was encountered.
169bool ProcessJpeg(DataRef data, PixelRef *out) {
170 /*
171 TODO(parker): sort of error handling.
172 struct jpeg_decompress_struct cinfo;
173 struct jpeg_error_mgr jerr;
174
175 cinfo.err = jpeg_std_error( &jerr );
176 jerr.emit_message = local_emit_message;
177
178 cinfo.out_color_space = JCS_RGB;
179 jpeg_create_decompress( &cinfo );
180 */
181
182 static bool lost_camera_connect = false;
183 struct jpeg_decompress_struct cinfo;
184
185 // We set up the normal JPEG error routines, then override error_exit.
186 JpegErrorManager jerr;
187 cinfo.err = jpeg_std_error(&jerr.pub);
188 jerr.pub.emit_message = local_emit_message;
189 jerr.pub.error_exit = JpegErrorExit;
190 // Establish the setjmp return context for my_error_exit to use.
191 if (setjmp(jerr.setjmp_buffer)) {
192 // If we get here, the JPEG code has signaled an error.
193 if (!lost_camera_connect) {
194 printf(
195 "Lost camera connection in process_jpeg.\nLooking for reconnect "
196 "...\n");
197 fflush(stdout);
198 lost_camera_connect = true;
199 }
200 jpeg_destroy_decompress(&cinfo);
201 return false;
202 }
203
204 cinfo.out_color_space = JCS_RGB;
205 jpeg_create_decompress(&cinfo);
206
207 jpeg_mem_src(&cinfo, reinterpret_cast<unsigned char *>(
208 const_cast<char *>(data.data())),
209 data.size());
210
211 jpeg_read_header(&cinfo, TRUE);
212 standard_huff_tables(&cinfo);
213
214 /*printf( "JPEG File Information: \n" );
215 printf( "Image width and height: %d pixels and %d pixels.\n",
216 cinfo.image_width, cinfo.image_height );
217 printf( "Color components per pixel: %d.\n", cinfo.num_components );
218 printf( "Color space: %d.\n", cinfo.jpeg_color_space );
219 printf("JpegDecompressed\n");*/
220
221 jpeg_start_decompress(&cinfo);
222
223 int offset = 0;
224 int step = cinfo.num_components * cinfo.image_width;
225 unsigned char *buffers[cinfo.image_height];
226 for (size_t i = 0; i < cinfo.image_height; ++i) {
227 buffers[i] = reinterpret_cast<unsigned char *>(&out[offset]);
228 offset += step;
229 }
230
231 while (cinfo.output_scanline < cinfo.image_height) {
232 jpeg_read_scanlines(&cinfo, &buffers[cinfo.output_scanline],
233 cinfo.image_height - cinfo.output_scanline);
234 }
235
236 jpeg_finish_decompress(&cinfo);
237 jpeg_destroy_decompress(&cinfo);
238
239 if (lost_camera_connect) {
240 printf("Camera connection restablished.\n");
241 fflush(stdout);
242 lost_camera_connect = false;
243 }
244
245 return true;
246}
247
248} // namespace vision
249} // namespace aos