概述:
  做过Android Camera图像采集和处理的朋友们应该都知道,Android手机相机采集的原始帧(RawFrame)默认是横屏格式的,而官方API有没有提供一个设置Camera采集图像的方向的方法,导致我们拿到原始帧后还需要再次对其进行转换为对应需求的数据,例如YUV的格式,图像的方向等(旋转多少度合适),下面我就粗略的介绍一下大致的流程,理解浅薄,大神请勿喷。
     注意:当前还都是基于API<21的内容,如果压根不用android.hardware.Camera的话可能有区别,还没研究过Camera2是什么原理,这里先不介绍了。
onPreviewFrame的原理:
     1. 原理及一般使用流程:
         大致流程:
         Camera — 取数据(onPreviewFrame(Byte[] rawFrameData, Camera camera)) —> 原始帧处理(Rotate/Scale:使用Libyuv/FFmpeg等工具库)—> 编码器编码得到相应的h24数据 —> 发送给流媒体服务器 
 
原始帧的采集:
     1. 方向问题:
         这里说明一下采集得到的帧的方向问题:Android默认是左横屏状态取景,也就意味着摄像头采集的数据默认就是横屏的,如图1,而且没有对应的方法来设置传感器的采集方向,所以想要实现竖直拍摄取景就得经过Rotate(旋转90°或者270°)得到图2那样的效果。

图 1
 

 
图 2
 
原始帧的分析:类型、YUV格式:
      之前写了一篇博客,关于YUV的区别的,感兴趣的朋友可以看看:http://www.cnblogs.com/raomengyang/p/4924787.html
 
处理原始帧:
  废话不多说了,上代码吧。
  MainActivity.java: 将Camera采集的图像显示到屏幕,并且图像经过90度的旋转成竖屏。
package com.dreamy.cameraframedemo;

import android.app.Activity;
import android.graphics.ImageFormat;
import android.hardware.Camera;
import android.os.Bundle;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View; import com.dreamy.cameraframedemo.util.ImageUtils; import java.io.IOException; public class MainActivity extends Activity implements SurfaceHolder.Callback,
Camera.PreviewCallback, View.OnClickListener {
// raw frame resolution: 1280x720, image format is: YV12
// you need get all resolution that supported on your devices;
// my phone is HUAWEI honor 6Plus, most devices can use 1280x720
private static final int SRC_FRAME_WIDTH = 1280;
private static final int SRC_FRAME_HEIGHT = 720;
private static final int IMAGE_FORMAT = ImageFormat.YV12; private Camera mCamera;
private Camera.Parameters mParams;
private SurfaceView mSurfaceView;
private SurfaceHolder mSurfaceHolder; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
setListener();
} private void initView() {
mSurfaceView = (SurfaceView) findViewById(R.id.sv_recording);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.setFixedSize(SRC_FRAME_WIDTH, SRC_FRAME_HEIGHT);
mSurfaceHolder.addCallback(this);
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
} private void setListener() {
// set Listener if you want, eg: onClickListener
} @Override
public void onClick(View v) {
} @Override
public void onPreviewFrame(byte[] data, Camera camera) {
ImageUtils.saveImageData(data);
camera.addCallbackBuffer(data);
} private void openCamera(SurfaceHolder holder) {
releaseCamera(); // release Camera, if not release camera before call camera, it will be locked
mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
mParams = mCamera.getParameters();
setCameraDisplayOrientation(this, Camera.CameraInfo.CAMERA_FACING_BACK, mCamera);
mParams.setPreviewSize(SRC_FRAME_WIDTH, SRC_FRAME_HEIGHT);
mParams.setPreviewFormat(IMAGE_FORMAT); // setting preview format:YV12
mParams.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
mCamera.setParameters(mParams); // setting camera parameters
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException ioe) {
ioe.printStackTrace();
}
mCamera.setPreviewCallback(this);
mCamera.startPreview();
} private synchronized void releaseCamera() {
if (mCamera != null) {
try {
mCamera.setPreviewCallback(null);
} catch (Exception e) {
e.printStackTrace();
}
try {
mCamera.stopPreview();
} catch (Exception e) {
e.printStackTrace();
}
try {
mCamera.release();
} catch (Exception e) {
e.printStackTrace();
}
mCamera = null;
}
} /**
* Android API: Display Orientation Setting
* Just change screen display orientation,
* the rawFrame data never be changed.
*/
private 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 displayDegree;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
displayDegree = (info.orientation + degrees) % 360;
displayDegree = (360 - displayDegree) % 360; // compensate the mirror
} else {
displayDegree = (info.orientation - degrees + 360) % 360;
}
camera.setDisplayOrientation(displayDegree);
} @Override
public void surfaceCreated(SurfaceHolder holder) {
openCamera(holder); // open camera
} @Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
}
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"> <SurfaceView
android:id="@+id/sv_recording"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>

别忘了在AndroidManifest中打开Camera的权限:

<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-feature android:name="android.hardware.camera" />
保存原始帧为图片格式:
package com.dreamy.cameraframedemo.util;

import android.os.Environment;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date; /**
* Created by raomengyang on 4/25/16.
*/
public class ImageUtils {
public static final int MEDIA_TYPE_IMAGE = 1;
public static final int MEDIA_TYPE_VIDEO = 2; // save image to sdcard path: Pictures/MyTestImage/
public static void saveImageData(byte[] imageData) {
File imageFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
if (imageFile == null) return;
try {
FileOutputStream fos = new FileOutputStream(imageFile);
fos.write(imageData);
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
} public static File getOutputMediaFile(int type) {
File imageFileDir =
new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyTestImage");
if (!imageFileDir.exists()) {
if (!imageFileDir.mkdirs()) {
return null;
}
}
// Create a media file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File imageFile;
if (type == MEDIA_TYPE_IMAGE) {
imageFile = new File(imageFileDir.getPath() + File.separator +
"IMG_" + timeStamp + ".jpg");
} else if (type == MEDIA_TYPE_VIDEO) {
imageFile = new File(imageFileDir.getPath() + File.separator +
"VID_" + timeStamp + ".mp4");
} else return null;
return imageFile;
}
}   

通过javah -classpath ./ com.dreamy.jni.LibyuvUtils 生成jni的h头文件LibyuvUtils.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_dreamy_jni_LibyuvUtils */ #ifndef _Included_com_dreamy_jni_LibyuvUtils
#define _Included_com_dreamy_jni_LibyuvUtils
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_dreamy_jni_LibyuvUtils
* Method: initRotateNV21
* Signature: (II)V
*/
JNIEXPORT void JNICALL Java_com_dreamy_jni_LibyuvUtils_initRotateNV21
(JNIEnv *, jclass, jint, jint); /*
* Class: com_dreamy_jni_LibyuvUtils
* Method: ScaleYV12ToI420
* Signature: ([B[BIIIII)V
*/
JNIEXPORT void JNICALL Java_com_dreamy_jni_LibyuvUtils_ScaleYV12ToI420
(JNIEnv *, jclass, jbyteArray, jbyteArray, jint, jint, jint, jint, jint); /*
* Class: com_dreamy_jni_LibyuvUtils
* Method: ReleaseRotateNV21
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_dreamy_jni_LibyuvUtils_ReleaseRotateNV21
(JNIEnv *, jclass); #ifdef __cplusplus
}
#endif
#endif 

资源下载:

  YUV格式查看工具:RawViewerhttp://download.csdn.net/detail/zxccxzzxz/9508288

     项目地址:https://github.com/eterrao/BlogExamples.git

   名称:CameraFrameDemo
 
  接下来会写Libyuv对原始帧进行Rotate和Scale的使用方法。

  

 

【Android】Android Camera原始帧格式转换 —— 获取Camera图像(一)的更多相关文章

  1. android 中如何获取camera当前状态

    /** * 测试当前摄像头能否被使用 * * @return */ public static boolean isCameraCanUse() { boolean canUse = true; Ca ...

  2. [Android]android.graphics.Camera实现图像的旋转、缩放,配合Matrix实现图像的倾斜

    android.graphics.Camera可以对图像执行一些比较复杂的操作,诸如旋转与绽放,与Matrix可实现图像的倾斜. 个人总结Camera与Matrix的一些区别如下: Camera的ro ...

  3. Android 音视频开发(四):使用 Camera API 采集视频数据

    本文主要将的是:使用 Camera API 采集视频数据并保存到文件,分别使用 SurfaceView.TextureView 来预览 Camera 数据,取到 NV21 的数据回调. 注: 需要权限 ...

  4. 【转】Android Camera(五)使用Camera功能 AREA的理解

    http://blog.csdn.net/think_soft/article/details/7998478 使用Camera功能 大多数的Camera功能都是使用Camera.Parameters ...

  5. Android tabhost下的activity怎样获取传来的值

    android tabhost下的activity怎样获取传来的值,具体解决方案如下: 解决方案: 其他activity设置intent:Intent intent=new Intent(); int ...

  6. Android 4.0以后正确的获取外部sd卡存储目录

    刚解决这个棘手的问题 找了很久,随笔记下. 网上搜索 android 获取外部sd卡存储目录 普遍都是: 1) Environment.getExternalStorageDirectory() 这个 ...

  7. Android如何在初始化的时候获取加载的布局的宽高

    在自定义ListView中,需要将下拉刷新的View在初始化的时候设置padding隐藏起来,这时就要在初始化的时候获得要加载的布局View的高度. private View headView; he ...

  8. Android中从SD卡中获取歌词并与歌曲同步

    先看看效果图吧,再看代码 转换文件的编码格式 package com.xm; import java.io.BufferedInputStream; import java.io.BufferedRe ...

  9. Android ADB工具-操作手机和获取手设备信息(四)

    Android ADB工具-操作手机和获取手设备信息(四) 标签(空格分隔): Android ADB 6. 其它命令 命令 功能 adb shell input text <content&g ...

随机推荐

  1. WM8978和VS1053B的区别

    WM8978是DAC和ADC芯片,不具备编解码的功能.不能把MP3格式或者WAV格式的文件解码出来.有播放和录音功能. 录音如果需要保存为MP3或者WAV格式,那么需要进行软件编码. VS1053B是 ...

  2. (转载)JAVA敏捷开发环境搭建

    整个软件项目分为四个环境 开发本地环境.开发环境.测试环境.IDC环境.和传统C++开发不一样的模式是多了第一个开发本地环境.这是为什么呢,因为目前大部分开发人员还是比较熟悉windows下开发.对于 ...

  3. poj 1099

    http://poj.org/problem?id=1099 #include<stdio.h> #include<string.h> #include <iostrea ...

  4. ado.net 向sql中插入新数据的同时获取自增重的id值

    两种方法都可以实现: 要获取的自增长列为phonebookID 方法一: sql = "insert into phonebook (mobile,peoplename) output in ...

  5. OC中的自动引用计数

    目录: 1,自动引用计数的定义 2,强引用和弱引用 3,类比手动引用 4,循环引用 5,CoreFoundation 内容: 自动引用计数的定义: (Automatic Reference Count ...

  6. Blend 2015 教程 (四)控件模板

    前一篇讲述了修改ListBox样式的方法,本篇将修改性别显示区域的样式. 1. 选择ListBox控件,编辑ItemTemplate的当前项,选择CheckBox控件,在美工板导航栏中点击CheckB ...

  7. 可在广域网部署运行的QQ高仿版 -- GG叽叽V3.6,增加语音消息、语音留言等功能

    自从微信出来后,语音消息和语音留言变得非常流行,按下一个键说话,比打字要方便多了.GG在V3.6版本增加了对语音消息和语音留言(或称为离线语音消息)的支持.这两个功能的实现已经很完整,只是比较遗憾的一 ...

  8. Hadoop日记Day17---计数器、map规约、分区学习

    一.Hadoop计数器 1.1 什么是Hadoop计数器 Haoop是处理大数据的,不适合处理小数据,有些大数据问题是小数据程序是处理不了的,他是一个高延迟的任务,有时处理一个大数据需要花费好几个小时 ...

  9. GLFW初体验

    GLFW - 很遗憾,没有找到FW的确切含义,Wiki上没有,GLFW主页也没有.猜测F表示for,W表示Window GLFW是干啥用的? 一个轻量级的,开源的,跨平台的library.支持Open ...

  10. 微软专家推荐11个Chrome 插件

    Web开发人员,需要长时间使用浏览器,尽管Windows10 Edge浏览器启动非常快速,且支持110多种设备,Edge支持基于JS 扩展,但也删除了很多旧功能像Active-X等插件.多数情况下,插 ...