blob: e9bb53538f91486c5e6e4a84373f787d45cfa65b
1 | /* |
2 | * Copyright (C) 2010 The Android Open Source Project |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | #define LOG_TAG "HW_JPEGENC" |
18 | |
19 | #include "CameraHal.h" |
20 | |
21 | #include <signal.h> |
22 | #include <stdio.h> |
23 | #include <unistd.h> |
24 | #include <fcntl.h> |
25 | #include <sys/ioctl.h> |
26 | #include <errno.h> |
27 | #include <string.h> |
28 | #include <sys/mman.h> |
29 | |
30 | #include <stdio.h> |
31 | #include <assert.h> |
32 | #include <limits.h> |
33 | #include <unistd.h> |
34 | #include <fcntl.h> |
35 | #include <sched.h> |
36 | #include <sys/types.h> |
37 | #include <sys/stat.h> |
38 | #include <sys/poll.h> |
39 | |
40 | #include "jpegenc.h" |
41 | |
42 | #define ENCODE_DONE_TIMEOUT 5000 |
43 | |
44 | //#define DEBUG_TIME |
45 | #ifdef DEBUG_TIME |
46 | static struct timeval start_test, end_test; |
47 | #endif |
48 | |
49 | static int RGBX32_To_RGB24Plane_NEON(unsigned char *src, unsigned char *dest, int width, int height) |
50 | { |
51 | unsigned char *R; |
52 | unsigned char *G; |
53 | unsigned char *B; |
54 | int canvas_w = ((width+31)>>5)<<5; |
55 | int i, j; |
56 | int aligned = canvas_w - width; |
57 | |
58 | if( !src || !dest ) |
59 | return -1; |
60 | |
61 | R = dest; |
62 | G = R + canvas_w * height; |
63 | B = G + canvas_w * height; |
64 | |
65 | for( i = 0; i < height; i += 1 ){ |
66 | for( j = 0; j < width; j += 8 ){ |
67 | asm volatile ( |
68 | "vld4.8 {d0, d1, d2, d3}, [%[src]]! \n" // load 8 more ABGR pixels. |
69 | "vst1.8 {d0}, [%[R]]! \n" // store R. |
70 | "vst1.8 {d1}, [%[G]]! \n" // store G. |
71 | "vst1.8 {d2}, [%[B]]! \n" // store B. |
72 | |
73 | : [src] "+r" (src), [R] "+r" (R), |
74 | [G] "+r" (G), [B] "+r" (B) |
75 | : |
76 | : "cc", "memory", "d0", "d1", "d2", "d3" |
77 | ); |
78 | } |
79 | if(aligned){ |
80 | R+=aligned; |
81 | G+=aligned; |
82 | B+=aligned; |
83 | } |
84 | } |
85 | return canvas_w*height*3; |
86 | } |
87 | |
88 | static int RGB24_To_RGB24Plane_NEON(unsigned char *src, unsigned char *dest, int width, int height) |
89 | { |
90 | unsigned char *R; |
91 | unsigned char *G; |
92 | unsigned char *B; |
93 | int i, j; |
94 | int canvas_w = ((width+31)>>5)<<5; |
95 | int aligned = canvas_w - width; |
96 | |
97 | if( !src || !dest ) |
98 | return -1; |
99 | |
100 | #ifdef DEBUG_TIME |
101 | unsigned total_time = 0; |
102 | struct timeval start_test1, end_test1; |
103 | gettimeofday(&start_test1, NULL); |
104 | #endif |
105 | R = dest; |
106 | G = R + canvas_w * height; |
107 | B = G + canvas_w * height; |
108 | |
109 | for( i = 0; i < height; i += 1 ){ |
110 | for( j = 0; j < width; j += 8 ){ |
111 | asm volatile ( |
112 | "vld3.8 {d0, d1, d2}, [%[src]]! \n" // load 8 more BGR pixels. |
113 | "vst1.8 {d0}, [%[R]]! \n" // store R. |
114 | "vst1.8 {d1}, [%[G]]! \n" // store G. |
115 | "vst1.8 {d2}, [%[B]]! \n" // store B. |
116 | |
117 | : [src] "+r" (src), [R] "+r" (R), |
118 | [G] "+r" (G), [B] "+r" (B) |
119 | : |
120 | : "cc", "memory", "d0", "d1", "d2" |
121 | ); |
122 | } |
123 | if(aligned){ |
124 | R+=aligned; |
125 | G+=aligned; |
126 | B+=aligned; |
127 | } |
128 | } |
129 | #ifdef DEBUG_TIME |
130 | gettimeofday(&end_test1, NULL); |
131 | total_time = (end_test1.tv_sec - start_test1.tv_sec)*1000000 + end_test1.tv_usec -start_test1.tv_usec; |
132 | ALOGD("RGB24_To_RGB24Plane_NEON: need time: %d us",total_time); |
133 | #endif |
134 | return canvas_w*height*3; |
135 | } |
136 | |
137 | static int encode_poll(int fd, int timeout) |
138 | { |
139 | struct pollfd poll_fd[1]; |
140 | poll_fd[0].fd = fd; |
141 | poll_fd[0].events = POLLIN |POLLERR; |
142 | return poll(poll_fd, 1, timeout); |
143 | } |
144 | |
145 | static unsigned copy_to_local(hw_jpegenc_t* hw_info) |
146 | { |
147 | unsigned offset = 0; |
148 | int bytes_per_line = 0, active = 0;; |
149 | unsigned i = 0; |
150 | unsigned total_size = 0; |
151 | unsigned char* src = NULL; |
152 | unsigned char* dst = NULL; |
153 | int plane_num = 1; |
154 | |
155 | if((hw_info->bpp !=12)&&(hw_info->in_format != FMT_YUV444_PLANE)&&(hw_info->in_format != FMT_RGB888_PLANE)) |
156 | bytes_per_line = hw_info->width*hw_info->bpp/8; |
157 | else |
158 | bytes_per_line = hw_info->width; |
159 | |
160 | if((hw_info->in_format == FMT_YUV420)||(hw_info->in_format == FMT_YUV444_PLANE)||(hw_info->in_format == FMT_RGB888_PLANE)) |
161 | plane_num = 3; |
162 | else if ((hw_info->in_format == FMT_NV12)||(hw_info->in_format == FMT_NV21)) |
163 | plane_num = 2; |
164 | |
165 | active = bytes_per_line; |
166 | |
167 | if(hw_info->in_format == FMT_YUV420) |
168 | bytes_per_line = ((bytes_per_line+63)>>6)<<6; |
169 | else if((hw_info->in_format == FMT_NV12)||(hw_info->in_format == FMT_NV21)||(hw_info->in_format == FMT_RGB888) |
170 | ||(hw_info->in_format == FMT_YUV422_SINGLE)||(hw_info->in_format == FMT_YUV444_SINGLE)||(hw_info->in_format == FMT_YUV444_PLANE) |
171 | ||(hw_info->in_format == FMT_RGB888_PLANE)) |
172 | bytes_per_line = ((bytes_per_line+31)>>5)<<5; |
173 | |
174 | src = (unsigned char*)hw_info->src; |
175 | dst = hw_info->input_buf.addr; |
176 | if(bytes_per_line != active){ |
177 | for(i =0; i<hw_info->height; i++){ |
178 | memcpy(dst, src,active); |
179 | dst+=bytes_per_line; |
180 | src+=active; |
181 | } |
182 | }else{ |
183 | memcpy(dst, src,hw_info->height*bytes_per_line); |
184 | } |
185 | |
186 | if(plane_num == 2){ |
187 | offset = hw_info->height*bytes_per_line; |
188 | src = (unsigned char*)(hw_info->src + hw_info->height*active); |
189 | dst = (unsigned char*)(hw_info->input_buf.addr+offset); |
190 | if(bytes_per_line != active){ |
191 | for(i =0; i<hw_info->height/2; i++){ |
192 | memcpy(dst, src,active); |
193 | dst+=bytes_per_line; |
194 | src+=active; |
195 | } |
196 | }else{ |
197 | memcpy(dst, src,hw_info->height*bytes_per_line/2); |
198 | } |
199 | }else if(plane_num == 3){ |
200 | unsigned temp_active = ((hw_info->in_format == FMT_YUV444_PLANE)||(hw_info->in_format == FMT_RGB888_PLANE))?active:active/2; |
201 | unsigned temp_h = ((hw_info->in_format == FMT_YUV444_PLANE)||(hw_info->in_format == FMT_RGB888_PLANE))?hw_info->height:hw_info->height/2; |
202 | unsigned temp_bytes = ((hw_info->in_format == FMT_YUV444_PLANE)||(hw_info->in_format == FMT_RGB888_PLANE))?bytes_per_line:bytes_per_line/2; |
203 | offset = hw_info->height*bytes_per_line; |
204 | src = (unsigned char*)(hw_info->src + hw_info->height*active); |
205 | dst = (unsigned char*)(hw_info->input_buf.addr+offset); |
206 | if(bytes_per_line != active){ |
207 | for(i =0; i<temp_h; i++){ |
208 | memcpy(dst, src,temp_active); |
209 | dst+=temp_bytes; |
210 | src+=temp_active; |
211 | } |
212 | }else{ |
213 | memcpy(dst, src,temp_bytes*temp_h); |
214 | } |
215 | offset = temp_h*temp_bytes+hw_info->height*bytes_per_line; |
216 | src = (unsigned char*)(hw_info->src + hw_info->height*active +temp_h*temp_active); |
217 | dst = (unsigned char*)(hw_info->input_buf.addr+offset); |
218 | if(bytes_per_line != active){ |
219 | for(i =0; i<temp_h; i++){ |
220 | memcpy(dst, src,temp_active); |
221 | dst+=temp_bytes; |
222 | src+=temp_active; |
223 | } |
224 | }else{ |
225 | memcpy(dst, src,temp_bytes*temp_h); |
226 | } |
227 | } |
228 | if((hw_info->bpp !=12)&&(hw_info->in_format != FMT_YUV444_PLANE)&&(hw_info->in_format != FMT_RGB888_PLANE)) |
229 | total_size = bytes_per_line*hw_info->height; |
230 | else |
231 | total_size = bytes_per_line*hw_info->height*hw_info->bpp/8; |
232 | return total_size; |
233 | } |
234 | |
235 | static size_t start_encoder(hw_jpegenc_t* hw_info) |
236 | { |
237 | int i; |
238 | int bpp; |
239 | unsigned size = 0; |
240 | unsigned cmd[7] , status; |
241 | unsigned in_format = hw_info->in_format; |
242 | if(hw_info->type == LOCAL_BUFF){ |
243 | if((hw_info->in_format != FMT_RGB888)&&(hw_info->in_format != FMT_RGBA8888)){ |
244 | cmd[5] = copy_to_local(hw_info); |
245 | }else if(hw_info->in_format == FMT_RGB888){ |
246 | cmd[5] = RGB24_To_RGB24Plane_NEON(hw_info->src, hw_info->input_buf.addr, hw_info->width, hw_info->height); |
247 | in_format = FMT_RGB888_PLANE; |
248 | }else{ |
249 | cmd[5] = RGBX32_To_RGB24Plane_NEON(hw_info->src, hw_info->input_buf.addr, hw_info->width, hw_info->height); |
250 | in_format = FMT_RGB888_PLANE; |
251 | } |
252 | }else{ |
253 | cmd[5] = hw_info->width*hw_info->height*hw_info->bpp/8; |
254 | } |
255 | |
256 | cmd[0] = hw_info->type; //input buffer type |
257 | cmd[1] = in_format; |
258 | cmd[2] = hw_info->out_format; |
259 | cmd[3] = (unsigned)hw_info->input_buf.addr; |
260 | cmd[4] = 0; |
261 | //cmd[5] = hw_info->width*hw_info->height*bpp/8; |
262 | cmd[6] = 1; |
263 | |
264 | ioctl(hw_info->fd, JPEGENC_IOC_NEW_CMD, cmd); |
265 | if(encode_poll(hw_info->fd, ENCODE_DONE_TIMEOUT)<=0){ |
266 | ALOGE("hw_encode: poll fail"); |
267 | return 0; |
268 | } |
269 | |
270 | ioctl(hw_info->fd, JPEGENC_IOC_GET_STAGE, &status); |
271 | if(status == ENCODER_DONE){ |
272 | ioctl(hw_info->fd, JPEGENC_IOC_GET_OUTPUT_SIZE, &size); |
273 | if((size < hw_info->output_buf.size)&&(size>0)&&(size<=hw_info->dst_size)){ |
274 | cmd[0] = JPEGENC_BUFFER_OUTPUT; |
275 | cmd[1] = 0 ; |
276 | cmd[2] = size ; |
277 | ioctl(hw_info->fd, JPEGENC_IOC_FLUSH_DMA ,cmd); |
278 | memcpy(hw_info->dst,hw_info->output_buf.addr,size); |
279 | ALOGV("hw_encode: done size: %d ",size); |
280 | }else{ |
281 | ALOGE("hw_encode: output buffer size error: bitstream buffer size: %d, jpeg size: %d, output buffer size: %d",hw_info->output_buf.size, size, hw_info->dst_size); |
282 | size = 0; |
283 | } |
284 | } |
285 | return size; |
286 | } |
287 | |
288 | size_t hw_encode(hw_jpegenc_t* hw_info) |
289 | { |
290 | unsigned buff_info[5]; |
291 | int ret; |
292 | unsigned encoder_width = hw_info->width; |
293 | unsigned encoder_height = hw_info->height; |
294 | |
295 | #ifdef DEBUG_TIME |
296 | unsigned total_time = 0; |
297 | gettimeofday(&start_test, NULL); |
298 | #endif |
299 | hw_info->jpeg_size = 0; |
300 | hw_info->fd = open(ENCODER_PATH, O_RDWR); |
301 | if(hw_info->fd < 0){ |
302 | ALOGD("hw_encode open device fail"); |
303 | goto EXIT; |
304 | } |
305 | |
306 | memset(buff_info,0,sizeof(buff_info)); |
307 | ret = ioctl(hw_info->fd, JPEGENC_IOC_GET_BUFFINFO,&buff_info[0]); |
308 | if((ret)||(buff_info[0]==0)){ |
309 | ALOGE("hw_encode -- no buffer information!"); |
310 | goto EXIT; |
311 | } |
312 | hw_info->mmap_buff.addr = (unsigned char*)mmap(0,buff_info[0], PROT_READ|PROT_WRITE , MAP_SHARED ,hw_info->fd, 0); |
313 | if (hw_info->mmap_buff.addr == MAP_FAILED) { |
314 | ALOGE("hw_encode Error: failed to map framebuffer device to memory.\n"); |
315 | goto EXIT; |
316 | } |
317 | |
318 | hw_info->mmap_buff.size = buff_info[0]; |
319 | hw_info->input_buf.addr = hw_info->mmap_buff.addr+buff_info[1]; |
320 | hw_info->input_buf.size = buff_info[3]-buff_info[1]; |
321 | hw_info->output_buf.addr = hw_info->mmap_buff.addr +buff_info[3]; |
322 | hw_info->output_buf.size = buff_info[4]; |
323 | |
324 | hw_info->qtbl_id = 0; |
325 | |
326 | switch(hw_info->in_format){ |
327 | case FMT_NV21: |
328 | hw_info->bpp = 12; |
329 | break; |
330 | case FMT_RGB888: |
331 | hw_info->bpp = 24; |
332 | break; |
333 | case FMT_YUV422_SINGLE: |
334 | hw_info->bpp = 16; |
335 | break; |
336 | case FMT_YUV444_PLANE: |
337 | case FMT_RGB888_PLANE: |
338 | hw_info->bpp = 24; |
339 | break; |
340 | default: |
341 | hw_info->bpp = 12; |
342 | } |
343 | |
344 | ioctl(hw_info->fd, JPEGENC_IOC_SET_ENCODER_WIDTH ,&encoder_width); |
345 | ioctl(hw_info->fd, JPEGENC_IOC_SET_ENCODER_HEIGHT ,&encoder_height); |
346 | |
347 | if((hw_info->width!= encoder_width)||(hw_info->height!= encoder_height)){ |
348 | ALOGE("hw_encode: set encode size fail. set as %dx%d, but max size is %dx%d.",hw_info->width,hw_info->height,encoder_width,encoder_height); |
349 | goto EXIT; |
350 | } |
351 | |
352 | ioctl(hw_info->fd, JPEGENC_IOC_SET_QUALITY ,&hw_info->quality); |
353 | ioctl(hw_info->fd, JPEGENC_IOC_SEL_QUANT_TABLE ,&hw_info->qtbl_id); |
354 | //ioctl(hw_info->fd, JPEGENC_IOC_SET_EXT_QUANT_TABLE ,NULL); |
355 | |
356 | ioctl(hw_info->fd, JPEGENC_IOC_CONFIG_INIT,NULL); |
357 | |
358 | hw_info->type = LOCAL_BUFF; |
359 | hw_info->out_format = FMT_YUV420; //FMT_YUV422_SINGLE |
360 | hw_info->jpeg_size = start_encoder(hw_info); |
361 | EXIT: |
362 | if(hw_info->mmap_buff.addr){ |
363 | munmap(hw_info->mmap_buff.addr ,hw_info->mmap_buff.size); |
364 | } |
365 | close(hw_info->fd); |
366 | hw_info->fd = -1; |
367 | #ifdef DEBUG_TIME |
368 | gettimeofday(&end_test, NULL); |
369 | total_time = (end_test.tv_sec - start_test.tv_sec)*1000000 + end_test.tv_usec -start_test.tv_usec; |
370 | ALOGD("hw_encode: need time: %d us, jpeg size:%d",total_time,hw_info->jpeg_size); |
371 | #endif |
372 | return hw_info->jpeg_size; |
373 | } |
374 | |
375 |