相信不少人都用過相機功能,也開發過簡單調度相機功能,可是相機採集功能。是圖像信號輸入的重要來源。android
SurfaceView和View的不一樣之處: git
相機圖像採樣,須要維持一個比較穩定的幀數來維持圖像實時性,須要頻繁刷新,建立一個子線程來進行畫面更新,會被佔用主線程效率好不少,並且雙緩衝機制能夠在畫面從前臺刷新到後臺時才佔用主線程操做,因此選用SurfaceView做繪製是最好的。 而GLSurfaceView是SurfaceView的一個子類,專用於openGL繪製,其運行效率遠高於SurfaceView是由於使用了GPU參與繪製。 這一節介紹Android攝像頭採樣,仍是採用了SurfaceView來作採樣 1.須要申請相機權限。github
<uses-permission android:name="android.permission.CAMERA" />
複製代碼
2.打開攝像頭,先檢查攝像和前置攝像頭,而後經過攝像頭Id,來返回攝像頭對象。bash
fun openCamera(cameraId:Int):Camera?{
if (!haveFeature(PackageManager.FEATURE_CAMERA)){
Log.e(TAG,"no camera!")
return null
}
if (cameraId == Camera.CameraInfo.CAMERA_FACING_FRONT && !haveFeature(PackageManager.FEATURE_CAMERA_FRONT)){
Log.e(TAG,"no front camera!")
return null
}
val camera = Camera.open(cameraId)
if (camera == null){
Log.e(TAG, "openCamera failed")
return null
}
return camera
}
複製代碼
3.設置畫面比例ide
/**
* 獲取最大的圖片大小
*/
fun getLargePictureSize(camera: Camera?): Camera.Size? {
if (camera != null) {
//獲取可選比例
val sizes = camera.parameters.supportedPictureSizes
var temp: Camera.Size = sizes[0]
for (i in 1 until sizes.size) {
val scale = sizes[i].height.toFloat() / sizes[i].width
if (temp.width < sizes[i].width && scale < 0.6f && scale > 0.5f)
temp = sizes[i]
}
return temp
}
return null
}
/**
* 獲取最大的預覽大小
*/
fun getLargePreviewSize(camera: Camera?): Camera.Size? {
if (camera != null) {
//獲取可選比例
val sizes = camera.parameters.supportedPreviewSizes
var temp: Camera.Size = sizes[0]
for (i in 1 until sizes.size) {
if (temp.width < sizes[i].width)
temp = sizes[i]
}
return temp
}
return null
}
/**
* 相機採樣參數大小
*/
fun setOptimalSize(camera:Camera,aspectRatio:Float,maxWidth:Int,maxHeight:Int){
val parameters= camera.parameters
//使用自動對焦
if (parameters.supportedFocusModes.contains(
Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
parameters.focusMode = Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE
}
val size = getLargePreviewSize(camera)
size?.let {
//設置相機預覽大小
parameters.setPreviewSize(it.width,it.height)
Log.d(TAG, "input max: (" + maxWidth + ", " + maxHeight + "), output size: ("
+ it.width + ", " + it.height + ")")
}
val pictureSize = getLargePictureSize(camera)
pictureSize?.let {
//圖片參數
parameters.setPictureSize(it.width,it.height)
Log.d(TAG, "picture max: (" + maxWidth + ", " + maxHeight + "), output size: ("
+ it.width + ", " + it.height + ")")
}
camera.parameters = parameters
}
複製代碼
3.設置相機圖像角度學習
fun setDisplayOritation(activity: Activity, camera: Camera, cameraId: Int) {
//獲取window的角度
val rotation = activity.windowManager.defaultDisplay.rotation
var degress = 0
when (rotation) {
Surface.ROTATION_0 -> degress = 0
Surface.ROTATION_90 -> degress = 90
Surface.ROTATION_180 -> degress = 180
Surface.ROTATION_270 -> degress = 270
}
val info = Camera.CameraInfo()
Camera.getCameraInfo(cameraId, info)
var result: Int
//前置攝像頭
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degress) % 360
result = (360 - result) % 360 // compensate the mirror
} else {//後置攝像頭
result = (info.orientation - degress + 360) % 360 // back-facing
}
Log.d(TAG, "window rotation: $degress, camera oritation: $result")
camera.setDisplayOrientation(result)
}
複製代碼
4.設置完攝像頭參數後,須要設置一個SurfaceHolder.CallBack。 有三個必須的方法ui
//建立時調用
override fun surfaceCreated(holder: SurfaceHolder?) { }
//當surface發生任何結構性的變化時(格式或者大小),該方法就會被當即調用。
override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) { }
//被移除時調用
override fun surfaceDestroyed(holder: SurfaceHolder?) {}
複製代碼
這裏建立相機的調用surfaceCreated,以後會馬上調用一次surfaceChanged 這裏在surfaceChanged上調用openGL的init操做spa
fun initOpenGL(surface: Surface, width: Int, height: Int){
//新開一個以後一個線程的線程池
mExecutor.execute {
//獲取紋理id
val textureId = OpenGLJniLib.magicBaseInit(surface,width,height,BaseApplication.context.assets)
if (textureId < 0){
Log.e(TAG, "surfaceCreated init OpenGL ES failed!")
return@execute
}
//須要使用surfaceTexture來作紋理裝載
mSurfaceTexture = SurfaceTexture(textureId)
//添加紋理變化回調
mSurfaceTexture?.setOnFrameAvailableListener { drawOpenGL() }
try {
//把攝像頭採樣關聯到紋理
mCamera?.setPreviewTexture(mSurfaceTexture)
//開始攝像頭採樣
doStartPreview()
}catch (e:IOException){
Log.e(TAG,e.localizedMessage)
releaseOpenGL()
}
}
}
複製代碼
5.開始預覽,並開始自動對焦。線程
fun doStartPreview(){
mCamera?.startPreview()
cameraFocus(width/2.0f,height/2.0f)
}
複製代碼
6.opengl繪製,先要強制更新紋理圖像,再更新獲取紋理矩陣,而後讓opengl繪製code
fun drawOpenGL(){
mExecutor.execute {
mSurfaceTexture?.updateTexImage()
mSurfaceTexture?.getTransformMatrix(mMatrix)
OpenGLJniLib.magicBaseDraw(mMatrix)
}
}
複製代碼
7.在destroySurfaceView的是否釋放資源
fun releaseOpenGL(){
mExecutor.execute {
mSurfaceTexture?.release()
mSurfaceTexture=null
OpenGLJniLib.magicBaseRelease()
}
}
複製代碼
介紹了Camera採樣配置和,surfaceTexture紋理獲取了和加載,下一節,將會介紹native層的opengl繪製代碼。
近來在寫一個有趣的項目,你們熟知攝像頭繪製和opengl的人應該有看過MagicCamera這個Android opengl2.0的開源工程,可是已經不少年沒人維護了,這邊正在將其重構爲opengl3.0的的版本。命名爲MagicCamera3以供你們學習,如今還在重構當中,致敬做者,也但願能夠有機會和做者多交流,若是有認識的能夠告知我一聲,謝謝。 開源地址:MagicCamera3,這節的用例在CameraActivity中