SurfaceTexture是从Android3.0(API 11)加入的一个新类。这个类跟SurfaceView很像,可以从camera preview或者video decode里面获取图像流(image stream)。但是,和SurfaceView不同的是,SurfaceTexture在接收图像流之后,不需要显示出来。有做过Android camera开发的人都知道,比较头疼的一个问题就是,从camera读取到的预览(preview)图像流一定要输出到一个可见的(Visible)SurfaceView上,然后通过Camera.PreviewCallback的public void onPreviewFrame(byte[] data, Camera camera)函数来获得图像帧数据的拷贝。这就存在一个问题,比如我希望隐藏摄像头的预览图像或者对每一帧进行一些处理再显示到手机显示屏上,那么在Android3.0之前是没有办法做到的,或者说你需要用一些小技巧,比如用其他控件把SurfaceView给挡住,注意这个显示原始camera图像流的SurfaceView其实是依然存在的,也就是说被挡住的SurfaceView依然在接收从camera传过来的图像,而且一直按照一定帧率去刷新,这是消耗cpu的,而且如果一些参数设置的不恰当,后面隐藏的SurfaceView有可能会露出来,因此这些小技巧并不是好办法。但是,有了SurfaceTexture之后,就好办多了,因为SurfaceTexture不需要显示到屏幕上,因此我们可以用SurfaceTexture接收来自camera的图像流,然后从SurfaceTexture中取得图像帧的拷贝进行处理,处理完毕后再送给另一个SurfaceView用于显示即可。
在我的应用场景中,我需要从camera读取图像流,然后对其处理,最后将处理结果显示到手机屏幕上。因此我写了一个GameDisplay类用于处理以上所有事务,而在MainActivity中,只需要创建一个GameDisplay类的实例并且初始化即可。
1 package com.song.camgame; 2 3 import java.io.IOException; 4 import java.util.List; 5 import java.util.Timer; 6 import java.util.TimerTask; 7 8 import android.app.Activity; 9 import android.content.Context; 10 import android.graphics.Bitmap; 11 import android.graphics.Canvas; 12 import android.graphics.ImageFormat; 13 import android.graphics.Rect; 14 import android.graphics.SurfaceTexture; 15 import android.hardware.Camera; 16 import android.hardware.Camera.Size; 17 import android.util.Log; 18 import android.view.SurfaceHolder; 19 import android.view.SurfaceView; 20 21 public class GameDisplay extends SurfaceView implements SurfaceHolder.Callback, 22 Camera.PreviewCallback{ 23 public final static String TAG="GameDisplay"; 24 25 private static final int MAGIC_TEXTURE_ID = 10; 26 public static final int DEFAULT_WIDTH=800; 27 public static final int DEFAULT_HEIGHT=480; 28 public static final int BLUR = 0; 29 public static final int CLEAR = BLUR + 1; 30 //public static final int PAUSE = PLAY + 1; 31 //public static final int EXIT = PAUSE + 1; 32 public SurfaceHolder gHolder; 33 public SurfaceTexture gSurfaceTexture; 34 public Camera gCamera; 35 public byte gBuffer[]; 36 public int textureBuffer[]; 37 public ProcessThread gProcessThread; 38 private int bufferSize; 39 private Camera.Parameters parameters; 40 public int previewWidth, previewHeight; 41 public int screenWidth, screenHeight; 42 public Bitmap gBitmap; 43 private Rect gRect; 44 // timer 45 private Timer sampleTimer; 46 private TimerTask sampleTask; 47 48 public GameDisplay(Context context,int screenWidth,int screenHeight) { 49 super(context); 50 gHolder=this.getHolder(); 51 gHolder.addCallback(this); 52 gHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 53 gSurfaceTexture=new SurfaceTexture(MAGIC_TEXTURE_ID); 54 this.screenWidth=screenWidth; 55 this.screenHeight=screenHeight; 56 gRect=new Rect(0,0,screenWidth,screenHeight); 57 Log.v(TAG, "GameDisplay initialization completed"); 58 } 59 60 @Override 61 public void surfaceChanged(SurfaceHolder holder, int format, int width, 62 int height) { 63 Log.v(TAG, "GameDisplay surfaceChanged"); 64 parameters = gCamera.getParameters(); 65 List<Size> preSize = parameters.getSupportedPreviewSizes(); 66 previewWidth = preSize.get(0).width; 67 previewHeight = preSize.get(0).height; 68 for (int i = 1; i < preSize.size(); i++) { 69 double similarity = Math 70 .abs(((double) preSize.get(i).height / screenHeight) 71 - ((double) preSize.get(i).width / screenWidth)); 72 if (similarity < Math.abs(((double) previewHeight / screenHeight) 73 - ((double) previewWidth / screenWidth))) { 74 previewWidth = preSize.get(i).width; 75 previewHeight = preSize.get(i).height; 76 } 77 } 78 gBitmap= Bitmap.createBitmap(previewWidth, previewHeight, Bitmap.Config.ARGB_8888); 79 parameters.setPreviewSize(previewWidth, previewHeight); 80 gCamera.setParameters(parameters); 81 bufferSize = previewWidth * previewHeight; 82 textureBuffer=new int[bufferSize]; 83 bufferSize = bufferSize * ImageFormat.getBitsPerPixel(parameters.getPreviewFormat()) / 8; 84 gBuffer = new byte[bufferSize]; 85 gCamera.addCallbackBuffer(gBuffer); 86 gCamera.setPreviewCallbackWithBuffer(this); 87 gCamera.startPreview(); 88 //gProcessThread = new ProcessThread(surfaceView,handler,null,previewWidth,previewHeight); 89 //processThread.start(); 90 } 91 92 @Override 93 public void surfaceCreated(SurfaceHolder holder) { 94 Log.v(TAG, "GameDisplay surfaceCreated"); 95 if (gCamera == null) { 96 gCamera = Camera.open(); 97 } 98 gCamera.setPreviewTexture(gSurfaceTexture); 99 //sampleStart(); 100 } 101 102 @Override 103 public void surfaceDestroyed(SurfaceHolder holder) { 104 Log.v(TAG, "GameDisplay surfaceDestroyed"); 105 //gProcessThread.isRunning=false; 106 //sampleTimer.cancel(); 107 //sampleTimer = null; 108 //sampleTask.cancel(); 109 //sampleTask = null; 110 gCamera.stopPreview(); 111 gCamera.release(); 112 } 113 114 @Override 115 public void onPreviewFrame(byte[] data, Camera camera) { 116 Log.v(TAG, "GameDisplay onPreviewFrame"); 117 //gProcessThread.raw_data=data; 118 camera.addCallbackBuffer(gBuffer); 119 for(int i=0;i<textureBuffer.length;i++) 120 textureBuffer[i]=0xff000000|data[i]; 121 gBitmap.setPixels(textureBuffer, 0, previewWidth, 0, 0, previewWidth, previewHeight); 122 synchronized (gHolder) 123 { 124 Canvas canvas = this.getHolder().lockCanvas(); 125 canvas.drawBitmap(gBitmap, null,gRect, null); 126 //canvas.drawBitmap(textureBuffer, 0, screenWidth, 0, 0, screenWidth, screenHeight, false, null); 127 this.getHolder().unlockCanvasAndPost(canvas); 128 } 129 130 } 131 132 public void sampleStart() { 133 Log.v(TAG, "GameDisplay sampleStart"); 134 sampleTimer = new Timer(false); 135 sampleTask = new TimerTask() { 136 @Override 137 public void run() { 138 gProcessThread.timer=true; 139 } 140 }; 141 sampleTimer.schedule(sampleTask,0, 80); 142 } 143 }
以上程序中115-130行是最重要的部分,data是从SurfaceTexture获得的camera图像帧的拷贝,119-121行对其进行了简单的处理,122-128行将处理过的图像数据传递给负责显示的SurfaceView并显示出来。
MainActivity对GameDisplay的调用如下:
1 //声明 2 private GameDisplay gameDisplay; 3 //初始化 4 gameDisplay.setVisibility(SurfaceView.VISIBLE); 5 DisplayMetrics dm = getResources().getDisplayMetrics(); 6 screenWidth = dm.widthPixels; 7 screenHeight = dm.heightPixels; 8 gameDisplay= new GameDisplay(this,dm.widthPixels,dm.heightPixels); 9 //加入到当前activity的layout中 10 FrameLayout root = (FrameLayout) findViewById(R.id.root); 11 root.addView(gameDisplay,0);
相关推荐
大家好,今天给大家分享一下Android里的Context的一些用法. 这里大致可以分为两种:一是传递Context参数,二是调用全局的Context. 其实我们应用启动的时候会启动Application这个类,这个类是在AndroidManifest.xml...
Android Fragments 是Android开发中的一个重要概念,自Android 3.0(API级别11)引入,主要用于解决不同屏幕尺寸设备上的UI适配问题。Fragments 提供了一种灵活的方式来构建动态和可重构的用户界面,特别是在平板...
iOS方面的我不太了解,我就主要谈谈Android开发中的,其中可能会有很多说错的,请大家轻喷想要在一个App中显示一个Html5网页的功能,其实很简单,只要一个WebView就可以了。你可以点击链接来跳转网页。像这样的功能...
在本文中,我们将深入探讨这一技术,并围绕"Android操作录制、操作回放"这一主题展开讨论。 首先,我们需要了解操作录制的基本原理。Android系统通过跟踪触摸事件、键盘输入和其他用户交互来实现操作录制。这通常...
首先,我们来谈谈Android系统的基础。Android是由Google主导开发的开源移动操作系统,广泛应用于智能手机、平板电脑和智能电视等设备。它基于Linux内核,提供了一个完整的软件堆栈,包括操作系统、中间件和关键应用...
在Android开发中,Databinding库和RecyclerView是两个非常重要的组件。Databinding提供了一种更简洁、可维护的方式来绑定数据到UI元素,而RecyclerView则是一个高效的数据展示控件,适用于大量的列表或网格数据。本...
在Android开发中,状态栏图标和悬浮框是两个重要的元素,它们可以提升用户体验并提供关键信息。本篇文章将深入探讨如何在Android应用中实现这些功能,以及如何处理菜单键事件和设置窗口显示权限。 首先,让我们关注...
Android 模拟器是Android开发过程中不可或缺的一部分,它允许开发者在没有物理设备的情况下测试应用程序和系统功能。模拟器是基于著名的开源虚拟机软件QEMU(Quick Emulator)进行开发的,QEMU能够模拟多种硬件环境...
接着,我们谈谈PickView,这是一个在Android中实现选择器功能的库。在Android应用中,经常需要用户选择日期、时间或者从列表中选取特定项,PickView就是为了简化这一过程而设计的。它通常包含轮播选择、滑动选择等...
在Android应用开发中,数据持久化是一个至关重要的环节,而Android Room和ContentProvider是其中的两个关键组件。本文将深入探讨这两个技术,并结合实际案例,解释如何在Android应用中使用它们来管理和共享SQLite...
【Android NDK】是Android平台开发中的一个重要工具集,它主要为开发者提供了在Android应用中使用C或C++代码的能力。NDK的全称为Native Development Kit,它的出现并不是为了让开发者完全用C或C++来编写整个Android...
理解这些概念有助于你在开发过程中更加灵活地运用Android的各种特性。 #### 实践与理论相结合 学习任何一门技术都不能仅仅停留在理论层面,必须通过大量的实践来加深理解和记忆。你可以从简单的Hello World程序...
接下来,我们来谈谈如何在项目中集成Android-Calces。首先,你需要将项目结构调整为符合组件化的设计,即创建各个独立的组件模块。然后,在项目的根级build.gradle文件中添加对calces-gradle-plugin的依赖。这通常...
首先,我们来谈谈Android中的线程管理。Android应用程序默认运行在一个主线程中,也称为UI线程,负责处理用户交互和绘制界面。然而,复杂的计算任务或长时间的操作如果在主线程执行,会阻塞UI更新,导致应用无响应...
从一开始写Android程序,就被告知这些常识 一、dp(或者dip device independent pixels) 一种基于屏幕密度的抽象单位。在每英寸160点的显示器上,1dp=1px。不同设备有不同的显示效果,这个和设备硬件有关。 二、sp...