应用 Camera API 进行视频的采集,别离应用 SurfaceView、TextureView 来预览 Camera 数据,取到NV21 的数据回调
筹备
应用相机权限
<uses-permission android:name="android.permission.CAMERA" />
camera 预览回调中默认应用 NV21格局。
查看手机是否反对摄像头。
UI筹备
<!-- 全屏显示 --><style name="FullScreenTheme" parent="AppTheme"> <item name="windowNoTitle">true</item> <item name="android:windowFullscreen">true</item></style>
承载预览图像
<FrameLayout android:id="@+id/camera_preview" android:layout_width="match_parent" android:layout_height="match_parent" />
应用 SurfaceView 预览 Camera,取到 NV21 数据
自定义 CameraPreview
继承 SurfaceView
,实现 SurfaceHolder.Callback
接口
获取 NV21 数据,Camera.setPreviewCallback()
要放在 Camera.startPreview()
之前。 应用Camera.PreviewCallback
获取预览数据回调。默认是NV21格局。
surfaceChanged
中,camera 启动预览前能够进行设置,例如设置尺寸,调整方向
/** * camera预览视图 * Created by Rust on 2018/2/26. */public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback { private static final String TAG = "rustApp"; private SurfaceHolder mHolder; private Camera mCamera; private int mFrameCount = 0; public CameraPreview(Context context) { super(context); } public CameraPreview(Context context, Camera camera) { super(context); mCamera = camera; mHolder = getHolder(); mHolder.addCallback(this); mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } public void setCamera(Camera c) { this.mCamera = c; } @Override public void surfaceCreated(SurfaceHolder holder) { // 开启预览 try { mCamera.setPreviewDisplay(holder); mCamera.startPreview(); } catch (IOException e) { Log.d(TAG, "Error setting camera preview: " + e.getMessage()); } } @Override public void surfaceDestroyed(SurfaceHolder holder) { // 可在此开释camera } @Override public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { // 若须要旋转、更改大小或从新设置,请确保障已进行预览 if (mHolder.getSurface() == null) { return; } try { mCamera.stopPreview(); } catch (Exception e) { // ignore: tried to stop a non-existent preview } Camera.Parameters parameters = mCamera.getParameters(); // ImageFormat.NV21 == 17 Log.d(TAG, "parameters.getPreviewFormat(): " + parameters.getPreviewFormat()); if (this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) { mCamera.setDisplayOrientation(90); } else { mCamera.setDisplayOrientation(0); } try { mCamera.setPreviewDisplay(mHolder); mCamera.setPreviewCallback(mCameraPreviewCallback); // 回调要放在 startPreview() 之前 mCamera.startPreview(); } catch (Exception e) { Log.d(TAG, "Error starting camera preview: " + e.getMessage()); } } private Camera.PreviewCallback mCameraPreviewCallback = new Camera.PreviewCallback() { @Override public void onPreviewFrame(byte[] data, Camera camera) { mFrameCount++; Log.d(TAG, "onPreviewFrame: data.length=" + data.length + ", frameCount=" + mFrameCount); } };}
为了避免阻塞 UI 线程,在子线程中关上 camera。camera 常放在 try catch
中应用。
public class MainActivity extends AppCompatActivity { private static final String TAG = "rustApp"; private Camera mCamera; private CameraPreview mPreview; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new InitCameraThread().start(); } @Override protected void onResume() { if (null == mCamera) { if (safeCameraOpen()) { mPreview.setCamera(mCamera); // 从新获取camera操作权 } else { Log.e(TAG, "无奈操作camera"); } } super.onResume(); } @Override protected void onPause() { super.onPause(); releaseCamera(); } private boolean safeCameraOpen() { boolean qOpened = false; try { releaseCamera(); mCamera = Camera.open(); qOpened = (mCamera != null); } catch (Exception e) { Log.e(TAG, "failed to open Camera"); e.printStackTrace(); } return qOpened; } private void releaseCamera() { if (mCamera != null) { mCamera.setPreviewCallback(null); mCamera.release(); // release the camera for other applications mCamera = null; } } private class InitCameraThread extends Thread { @Override public void run() { super.run(); if (safeCameraOpen()) { Log.d(TAG, "开启摄像头"); runOnUiThread(new Runnable() { @Override public void run() { mPreview = new CameraPreview(MainActivity.this, mCamera); FrameLayout preview = findViewById(R.id.camera_preview); preview.addView(mPreview); } }); } } }}
应用 TextureView 预览 Camera,取到 NV21 数据
TextureView
可用于显示内容流。内容流能够是视频或者OpenGL的场景。内容流可来自利用过程或是近程其它过程。
Textureview
必须在硬件加速开启的窗口中应用。若是软解,TextureView
不会显示货色。
不同于 SurfaceView
,TextureView
不会建设一个独自的窗口,而是像一个惯例的View一样(集体认为这是个长处)。 这使得 TextureView
能够被挪动,转换或是增加动画。比方,能够调用myView.setAlpha(0.5f)
将其设置成半透明。
应用 TextureView
很简略:获取到它的 SurfaceTexture
,应用 SurfaceTexture
出现内容。
CameraPreview
继承了 TextureView
,内部须要传入 camera 实例。在 onSurfaceTextureAvailable
中,配置 camera,比方设置图像方向。 通过设置 Camera.PreviewCallback
来获得预览数据。
import java.io.IOException;import android.content.Context;import android.content.res.Configuration;import android.graphics.SurfaceTexture;import android.hardware.Camera;import android.util.Log;import android.view.TextureView;public class CameraPreview extends TextureView implements TextureView.SurfaceTextureListener { private static final String TAG = "rustApp"; private Camera mCamera; public CameraPreview(Context context) { super(context); } public CameraPreview(Context context, Camera camera) { super(context); mCamera = camera; } public void setCamera(Camera camera) { this.mCamera = camera; } @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { Log.d(TAG, "TextureView onSurfaceTextureAvailable"); if (this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) { mCamera.setDisplayOrientation(90); } else { mCamera.setDisplayOrientation(0); } try { mCamera.setPreviewCallback(mCameraPreviewCallback); mCamera.setPreviewTexture(surface); // 应用SurfaceTexture mCamera.startPreview(); } catch (IOException ioe) { // Something bad happened } } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { Log.d(TAG, "TextureView onSurfaceTextureSizeChanged"); // Ignored, Camera does all the work for us } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { Log.d(TAG, "TextureView onSurfaceTextureDestroyed"); mCamera.stopPreview(); mCamera.release(); return true; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { // Invoked every time there's a new Camera preview frame } private Camera.PreviewCallback mCameraPreviewCallback = new Camera.PreviewCallback() { @Override public void onPreviewFrame(byte[] data, Camera camera) { Log.d(TAG, "onPreviewFrame: data.length=" + data.length); } };}
操作界面 TextureAct
。获取 camera 操作权,初始化 CameraPreview
并增加到布局中。第一次获取camera时在子线程中操作。
在 onPause
中开释 camera,onResume
中尝试取回 camera 控制权。这样利用临时退回后盾时,其余利用能够操作摄像头。
public class TextureAct extends AppCompatActivity { private static final String TAG = "rustApp"; private Camera mCamera; private CameraPreview mPreview; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_texture); new InitCameraThread().start(); } @Override protected void onResume() { if (null == mCamera) { if (safeCameraOpen()) { mPreview.setCamera(mCamera); // 从新获取camera操作权 } else { Log.e(TAG, "无奈操作camera"); } } super.onResume(); } @Override protected void onPause() { super.onPause(); releaseCamera(); } @Override protected void onDestroy() { super.onDestroy(); releaseCamera(); } private boolean safeCameraOpen() { boolean qOpened = false; try { releaseCamera(); mCamera = Camera.open(); qOpened = (mCamera != null); } catch (Exception e) { Log.e(TAG, "failed to open Camera"); e.printStackTrace(); } return qOpened; } private void releaseCamera() { if (mCamera != null) { mCamera.setPreviewCallback(null); mCamera.release(); // release the camera for other applications mCamera = null; } } private class InitCameraThread extends Thread { @Override public void run() { super.run(); if (safeCameraOpen()) { Log.d(TAG, "TextureAct 开启摄像头"); runOnUiThread(new Runnable() { @Override public void run() { mPreview = new CameraPreview(TextureAct.this, mCamera); mPreview.setSurfaceTextureListener(mPreview); FrameLayout preview = findViewById(R.id.camera_preview); preview.addView(mPreview); } }); } } }}
Textureview
必须在硬件加速开启的窗口中应用。android:hardwareAccelerated="true"
默认的这个属性就是 true,无需再设置。
每接到一帧数据,就会调用一次 onSurfaceTextureUpdated()
。通过这个接口。可能将上来的SurfaceTexture 送给 OpenGL 再去解决。
【Android音视频开发系列教程】