Android开发中经常需要重写相机,由此会导致一些旋转的情况(不同的设备摄像头角度是不一样的),此处按照解决思路给出解决方案:

情形一:只需要旋转摄像头方向以及最终的照片,注意两者需要保持一致

1. 获取当前相机摄像头的角度,并进行相应的旋转,方法如下:

此处获取到的摄像头角度可以保存下来,在后面情形二中会用到,这里存到静态变量 orientationDegree 中。

public static int orientationDegree = 0;

   /**
* 适配相机旋转
*
* @param activity
* @param cameraId
* @param camera
*/
public void setCameraDisplayOrientation(Activity activity, int cameraId, Camera camera) {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(cameraId, info);
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result;
//前置
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360;
}
//后置
else {
result = (info.orientation - degrees + 360) % 360;
}
orientationDegree = result;
camera.setDisplayOrientation(result);
}

2. 上述方法中涉及到的参数,第一个Activity,可以传入当前相机Activity的Context,随后进行强转;第二个参数为相机ID(一般设备有多个摄像头,前置,后置等等),下面将给出获取相机ID的方法;

第三个参数为camera对象,当调用open方法的时候就可以获取到 mCamera = Camera.open();

获取相机ID的方法如下:

   /**
* 获取摄像头ID
*
* @return
*/
private int getDefaultCameraId() {
int defaultId = -1;
int numberOfCameras = Camera.getNumberOfCameras(); Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
for (int i = 0; i < numberOfCameras; i++) {
Camera.getCameraInfo(i, cameraInfo);
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
defaultId = i;
}
}
if (defaultId == -1) {
if (numberOfCameras > 0) {
//没有后置摄像头
defaultId = 0;
} else {
Logger.e("没有摄像头");
}
}
return defaultId;
}

情形二:在情形一的基础上,需要旋转拍摄过程中的视频帧,与其他平台对接,涉及到JNI编程。此处参考了stackoverflow上的解答,链接如下:

https://stackoverflow.com/questions/14167976/rotate-an-yuv-byte-array-on-android

拍照回调过程中会产生图片数据,格式为NV21.类型是字节数据。如果需要将此数据传输到其他平台或者保存到本地(本地视频),则也需要进行相应的旋转。

public class CameraPreviewCallback implements Camera.PreviewCallback {
public CameraPreviewCallback(CameraPreviewSend previewSend) {
this.mCameraPreviewSend = previewSend;
} /**
* 在相机预览时每产生一帧时回调
*
* @param data 图片数据,格式 NV21
* @param camera 相机对象
*/
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
//todo:业务逻辑
}
}

情形一中获取到的相机旋转角度,此处根据角度来进行相应的旋转。具体方法如下:

switch (orientationDegree) {
case :
localFrameSave(data);
break;
case :
localFrameSave(picRotate90(data, previewWidth, previewHeight));
break;
case :
localFrameSave(picRotate180(data, previewWidth, previewHeight));
break;
case :
localFrameSave(picRotate270(data, previewWidth, previewHeight));
break;
}
    /**
* 旋转图片,顺时针旋转90度
*
* @param data nv21格式的图片
* @param width 图片宽度
* @param height 图片高度
* @return 转换后的图片数据
*/
public native byte[] picRotate90(byte[] data, int width, int height); static {
System.loadLibrary("native-lib");
}

cpp部分:

/**
* 将nv21格式的图片旋转90度
*/
extern "C"
jbyteArray
Java_com_XXX_io_CameraPreviewSend_picRotate90(
JNIEnv *env, jobject /*this*/, jbyteArray data, jint width, jint height) {
jbyte *pBuffer = env->GetByteArrayElements(data, 0);
int length = env->GetArrayLength(data);
jbyte newData[length];//返回的图片数据
//根据原数据pBuffer生成新数据newData
doRotate(pBuffer, newData, width, height); jbyteArray array = env->NewByteArray(length);
env->SetByteArrayRegion(array, 0, length, newData);
env->ReleaseByteArrayElements(data, pBuffer, 0);
return array;
} 
/**
* 将nv21格式的图片旋转90度
* @param data 原图片数据
* @param newData 转换所得图片数据
* @param width 转换后的图片的宽度
* @param height 转换后的图片的高度
*/
void doRotate(jbyte *data, jbyte *newData, jint width, jint height) {
int i = 0;
for (int x = 0; x < width; x++) {
for (int y = height - 1; y >= 0; y--) {
newData[i] = data[y * width + x];
i++;
}
}
i = width * height * 3 / 2 - 1;
for (int x = width - 1; x > 0; x = x - 2) {
for (int y = 0; y < height / 2; y++) {
newData[i] = data[(width * height) + (y * width) + x];
i--;
newData[i] = data[(width * height) + (y * width) + (x - 1)];
i--;
}
}
}

旋转180度的情况:

/**
* 将nv21格式的图片旋转180度
* @param data
* @param newData
* @param width
* @param height
*/
void doRotate180(jbyte *data, jbyte *newData, jint width, jint height) {
int i = ;
int count = ;
for (i = width * height - ; i >= ; i--) {
newData[count] = data[i];
count++;
}
for (i = width * height * / - ; i >= width * height; i -= ) {
newData[count++] = data[i - ];
newData[count++] = data[i];
}
} /**
* 将nv21格式的图片旋转180度
*/
extern "C"
jbyteArray
Java_com_XXX_io_CameraPreviewSend_picRotate180(
JNIEnv *env, jobject /*this*/, jbyteArray data, jint width, jint height) {
jbyte *pBuffer = env->GetByteArrayElements(data, );
int length = env->GetArrayLength(data);
jbyte newData[length];//返回的图片数据
//根据原数据pBuffer生成新数据newData
doRotate180(pBuffer, newData, width, height); jbyteArray array = env->NewByteArray(length);
env->SetByteArrayRegion(array, , length, newData);
env->ReleaseByteArrayElements(data, pBuffer, );
return array;
}

旋转270度的情况:

/**
* 将nv21格式的图片旋转270度
* @param data
* @param newData
* @param width
* @param height
*/
void doRotate270(jbyte *data, jbyte *newData, jint width, jint height) {
// Rotate the Y
int i = ;
for (int x = width - ; x >= ; x--) {
for (int y = ; y < height; y++) {
newData[i] = data[y * width + x];
i++;
}
}// Rotate the U and V color components
i = width * height;
for (int x = width - ; x > ; x = x - ) {
for (int y = ; y < height / ; y++) {
newData[i] = data[(width * height) + (y * width) + (x - )];
i++;
newData[i] = data[(width * height) + (y * width) + x];
i++;
}
}
} /**
* 将nv21格式的图片旋转270度
*/
extern "C"
jbyteArray
Java_com_XXX_io_CameraPreviewSend_picRotate270(
JNIEnv *env, jobject /*this*/, jbyteArray data, jint width, jint height) {
jbyte *pBuffer = env->GetByteArrayElements(data, );
int length = env->GetArrayLength(data);
jbyte newData[length];//返回的图片数据
//根据原数据pBuffer生成新数据newData
doRotate270(pBuffer, newData, width, height); jbyteArray array = env->NewByteArray(length);
env->SetByteArrayRegion(array, , length, newData);
env->ReleaseByteArrayElements(data, pBuffer, );
return array;
}

完结,很感谢stackoverflow上的解答。

重写Android相机适配不同的设备,对于相机旋转角度问题解决方案的更多相关文章

  1. Android重写getResources规避用户调整系统字体大小影响Android屏幕适配

    Android屏幕适配一直是一个头疼的问题.除此之外还要考虑APP在实际应用场景中,用户千奇百怪的设置,最常见的用户设置行为就是设置手机的字体大小,比如把字体设置成超大或者超小,这对屏幕适配又带来额外 ...

  2. Android适配不同的设备

    感谢原作者的整理: http://blog.csdn.net/chenyjays/article/details/41308887 适配不同的语言 把UI中的字符串存储在外部文件,通过代码提取. 创建 ...

  3. android Activity生命周期(设备旋转、数据恢复等)与启动模式

    1.Activity生命周期     接下来将介绍 Android Activity(四大组件之一) 的生命周期, 包含运行.暂停和停止三种状态,onCreate.onStart.onResume.o ...

  4. 【收藏】Android屏幕适配全攻略(最权威的Google官方适配指导)

    来源:http://blog.csdn.net/zhaokaiqiang1992 更多:Android AutoLayout全新的适配方式, 堪称适配终结者 Android的屏幕适配一直以来都在折磨着 ...

  5. Android屏幕适配全攻略(最权威的官方适配指导)(转),共大家分享。

    Android的屏幕适配一直以来都在折磨着我们这些开发者,本篇文章以Google的官方文档为基础,全面而深入的讲解了Android屏幕适配的原因.重要概念.解决方案及最佳实践,我相信如果你能认真的学习 ...

  6. Android屏幕适配全攻略(最权威的官方适配指导) (转)

    招聘信息: Cocos2d-X 前端主程 [新浪微博]手机客户端iOS研发工程师 20k-40k iOS 开发工程师 iOS高级开发工程师(中国排名第一的企业级移动互联网云计算公司 和创科技 红圈营销 ...

  7. 腾讯优测-优社区干货精选 |  那些年,我们在Android机型适配上遇到的坑之Camera拍照时快门咔嚓声

    文/腾讯优测研发工程师 吴宇焕 优测小优有话说: android机型适配的坑自然是不少,不想掉坑快来优测优社区~ 现在Android手机一般都会带有照相功能,有很多朋友就发现手机照相时快门声音很响,想 ...

  8. 腾讯优测| 让Android屏幕适配开发更简单-Google百分比布

    文/腾讯优测工程师 吴宇焕 腾讯优测优社区干货精选~ 相信开发同学都被安卓设备碎片化的问题折磨过,市面上安卓手机的主流屏幕尺寸种类繁多,给适配造成很大的困难.就算搞定了屏幕尺寸问题,各种分辨率又让人眼 ...

  9. Android 屏幕适配(一)百分比布局库(percent-support-lib) 解析与扩展

    转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/46695347: 本文出自:[张鸿洋的博客] 一.概述 周末游戏打得过猛,于是周 ...

随机推荐

  1. Java开发.gitignore文件包含.iml,.log的看法

    有一个开源项目https://github.com/github/gitignore 主要用来规范所有开发项目的.gitignore文件的编写,基本涵盖了所有的开发语言.开发环境等.今日我向JetBr ...

  2. .NET中微软实体框架的数据访问方法

    介绍 本文的目的是解释微软的实体框架提供的三种数据访问方法.网上有好几篇关于这个话题的好文章,但是我想以一个教程的形式更详细地介绍这个话题,这个教程对于开始学习实体框架及其方法的人来说是个入门.我们将 ...

  3. orale 10g和11g中的自动统计任务

    orale 10g和11g中的自动统计任务 博客分类:  数据库相关/oracle   1)  先来看下oracle 10g中的自动统计任务的问题. 从Oracle Database 10g开始,Or ...

  4. 使用nsis开发自定义安装包使用心得,以及遇到坑

    因为新公司需要开发pc应用的自定义安装包,开始时候计划使用nsis开发,论坛上面有很多不错的例子,而且完成度很强, 随便拿来修改使用,但是后续的开发过程中遇到的问题就逐个出现. 首先说一下nsis的优 ...

  5. C++二维数组动态申请内存

    好久没用C++刷题了,今天早上刷了几条题,感觉很陌生了.怪我,大二下实在太颓废了,没啥作为. 今天更新个关于c++二维数组内存申请的问题,当初作为菜鸟初学指针的时候,还是在这方面有点搞不通的.今天用到 ...

  6. POJ:3977-Subset(双向搜索)

    Subset Time Limit: 30000MS Memory Limit: 65536K Total Submissions: 5961 Accepted: 1129 Description G ...

  7. [BZOJ3172 ][Tjoi2013]单词(AC自动机)

    Description 不稳定的传送门 某人读论文,一篇论文是由许多单词组成.但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次.单词个数<=200,单词总长度< ...

  8. ABAP News for Release 7.51 – ABAP CDS Client Handling

    Open SQLは自動的クライアント処理をサポートしています. Open SQLでクライアント依存のデータソースにアクセスする時.デフォルトでは現在のクライアントのデータだけが考慮されます. クライア ...

  9. Java 的单元测试

    有点需要注意,当 JUnit 主线程退出,子线程也会跟着退出,需要使用子线程的 join() 方法使主线程等待 Maven 依赖 <dependency> <groupId>j ...

  10. 不同级域名中的 Cookie 共享

    HTTP 响应头中 Set-Cookie 行未指定 domain 时则设置访问的域名 seliote.com 可以设置 seliote.com(也可以写成 .seliote.com 意思一样) 与 w ...