一、问题提出
目前遇到项目问题,Camera预览图像是反的,于是考虑设置180度反转以便正常。
通过如下两种方式:
params.setRotation(180); //java部分
p.set(CameraParameters::KEY_ROTATION,180); //C部分
发现应用部分takepicture出来数据都没有变化。
代码片段如下:
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); /* Matrix matrix = new Matrix(); matrix.setRotate(180); int width = bitmap.getWidth(); int height = bitmap.getHeight(); bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true); */ File out = new File("/data/"); if (!out.exists()) { out.mkdirs(); } out = new File("/data/", "1.jpg"); try { FileOutputStream outStream = new FileOutputStream(out); bitmap.compress(CompressFormat.JPEG, 100, outStream); outStream.close(); } catch (Exception e) { e.printStackTrace(); }上边屏蔽部分打开后ok,屏蔽后有问题;屏蔽部分完成反转功能,证明图片本身有问题。
二、逐层排查
1.frameworks/av/services/camera/libcameraservice/CameraClient.cpp
void CameraClient::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, camera_frame_metadata_t *metadata, void* user) { int takepicture_fd; takepicture_fd = open("/data/tankai.jpg",O_WRONLY|O_CREAT,00700); ALOGD("TK---------->>>>JPEG,dataPtr->size() is %d,takepicture_fd is %d\n",dataPtr->size(),takepicture_fd); ALOGD("TK---------->>>>JPEG,dataPtr->size() is 0x%x\n",dataPtr->pointer()); int write_len = write(takepicture_fd, dataPtr->pointer(), dataPtr->size()); close(takepicture_fd); ALOGD("TK--------_>>>>>>ok\n"); }2.frameworks/av/camera/Camera.cpp
void Camera::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, camera_frame_metadata_t *metadata) { int takepicture_fd; takepicture_fd = open("/data/tankai_CPP.jpg",O_WRONLY|O_CREAT,00700); ALOGD("TK---CPP------->>>>JPEG,dataPtr->size() is %d,takepicture_fd is %d\n",dataPtr->size(),takepicture_fd); ALOGD("TK->>>>CPP---->>>>>dataPtr->pointer() is 0x%x",dataPtr->pointer()); int write_len = write(takepicture_fd, dataPtr->pointer(), dataPtr->size()); close(takepicture_fd); ALOGD("TK---CPP-----_>>>>>>ok\n"); }3.frameworks/base/core/jni/android_hardware_Camera.cpp
void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType) { jbyteArray obj = NULL; obj = env->NewByteArray(size); //add by tankai int takepicture_fd; takepicture_fd = open("/data/tankai_jni2.jpg",O_WRONLY|O_CREAT,00700); ALOGD("TK-------JNI2--->>>>JPEG,size is %d,takepicture_fd is %d\n",size,takepicture_fd); ALOGD("TK----->>>>JNI2------>>>>>>data is 0x%x",data); int write_len = write(takepicture_fd, data, size); close(takepicture_fd); ALOGD("TK----JNI2----_>>>>>>ok\n"); //end tankai env->SetByteArrayRegion(obj, 0, size, data); //add by tankai takepicture_fd = open("/data/tankai_jni3.jpg",O_WRONLY|O_CREAT,00700); ALOGD("TK-------JNI3--->>>>JPEG,size is %d,takepicture_fd is %d\n",size,takepicture_fd); jbyte * olddata = (jbyte*)env->GetByteArrayElements(obj, 0); char* bytearr = (char*)olddata; ALOGD("TK----->>>>JNI3------>>>>>>obj is 0x%x,bytearr is 0x%x",obj,bytearr); write_len = write(takepicture_fd, bytearr, size); close(takepicture_fd); ALOGD("TK----JNI3----_>>>>>>ok\n"); //end tankai //调用mCameraJClass的fields.post_event方法,参数为后边的5个 env->CallStaticVoidMethod(mCameraJClass, fields.post_event, mCameraJObjectWeak, msgType, 0, 0, obj); }4.frameworks/base/core/java/android/hardware/Camera.java
public void handleMessage(Message msg) { case CAMERA_MSG_COMPRESSED_IMAGE: //add by tankai Log.d(TAG,"TK-------->>>>>>java callback"); Bitmap bitmap = BitmapFactory.decodeByteArray(((byte[])msg.obj), 0, ((byte[])msg.obj).length); Log.d(TAG,"TK----->>>>((byte[])msg.obj) is 0x%x" + ((byte[])msg.obj)); String path = "/data/"; String fileName = "tankai_java.jpg"; File out = new File(path); if (!out.exists()) { out.mkdirs(); } out = new File(path, fileName); try { FileOutputStream outStream = new FileOutputStream( out); bitmap.compress(CompressFormat.JPEG, 100,outStream); outStream.close(); Log.d(TAG,"TK-------->>>>>>java callback>>>>ok"); } catch (Exception e) { Log.d(TAG,"TK-------->>>>>>java callback>>>>fail"); e.printStackTrace(); } //end tankai }结果,当设置180度反转后,1-3中所存图片都为正;4为反。
三、原因
1.java部分使用bitmap存文件,没有直接保存;可能是这个原因。
2.修改应用程序中存JPEG文件的方式:
File ret = null; BufferedOutputStream stream = null; try { ret = new File("/data/1.jpg"); FileOutputStream fstream = new FileOutputStream(ret); stream = new BufferedOutputStream(fstream); stream.write(data); } catch (Exception e) { // log.error("helper:get file from byte process error!"); e.printStackTrace(); } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { // log.error("helper:get file from byte process error!"); e.printStackTrace(); } } }结果,图片变正。
3.结论
问题出在bitmap转换上。
四、读取JPEG文件EXIF信息的ORIENTATION字段
1.源码
private int getBitmapDegree(String path) { int degree = 0; try { // 从指定路径下读取图片,并获取其EXIF信息 ExifInterface exifInterface = new ExifInterface(path); // 获取图片的旋转信息 int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); switch (orientation) { case ExifInterface.ORIENTATION_ROTATE_90: degree = 90; break; case ExifInterface.ORIENTATION_ROTATE_180: degree = 180; break; case ExifInterface.ORIENTATION_ROTATE_270: degree = 270; break; } } catch (IOException e) { e.printStackTrace(); } return degree; } result = getBitmapDegree("/data/tankai_CPP.jpg"); Log.d("TKTK","TK------>>>>>>>/data/tankai_CPP is " + result); result = getBitmapDegree("/data/tankai_jni.jpg"); Log.d("TKTK","TK------>>>>>>>/data/tankai_jni1 is " + result); result = getBitmapDegree("/data/tankai_jni2.jpg"); Log.d("TKTK","TK------>>>>>>>/data/tankai_jni2 is " + result); result = getBitmapDegree("/data/tankai_jni3.jpg"); Log.d("TKTK","TK------>>>>>>>/data/tankai_jni3 is " + result); result = getBitmapDegree("/data/tankai_java.jpg"); Log.d("TKTK","TK------>>>>>>>/data/tankai_java is " + result); result = getBitmapDegree("/data/1.jpg"); Log.d("TKTK","TK------>>>>>>>/data/1.jpg is " + result);2.结果
正常显示时该字段为180,没有变正的该字段为0。
五、结论
Android系统中Camera拍照时setRotation在各个平台功能不统一:有些平台会直接操作图片的存储地址,将数据反写;但绝大多数平台只是修改了JPEG编码时EXIF头部的ORIENTATION字段。