summaryrefslogtreecommitdiff
path: root/jpegenc_hw/jpegenc.cpp (plain)
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
46static struct timeval start_test, end_test;
47#endif
48
49static 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
88static 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
137static 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
145static 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
235static 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
288size_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);
361EXIT:
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