視訊播放surfaceView camera,獲取預覽影象顯示setPreviewCallback()
阿新 • • 發佈:2019-02-19
同事找我做一個視訊預覽複製,實現螢幕一分為二,同時顯示相同畫面。這裡寫個demo版
本篇的重點是
camera.setPreviewCallback()
在視訊預覽過程中,每一幀的影象資料均會通過這個callback返回,在這裡面我們可以處理返回的位元組陣列,轉換為bitmap,然後顯示出來
其中要注意的是返回的陣列影象格式為YUV,並不支援直接BitmapFactory.decodeByteArray()方法,需要通過格式轉換。但這也導致執行計算所化時間較長,第二個介面卡頓。目前沒有很好的解決方案,想到的兩種解決方式,通過開啟執行緒池,處理資料;或者通過native提高運算效率。
camera.setPreviewCallback(new Camera.PreviewCallback() { @Override public void onPreviewFrame(byte[] data, Camera camera) { //處理data previewSize = camera.getParameters().getPreviewSize();//獲取尺寸,格式轉換的時候要用到 YuvImage yuvimage = new YuvImage( data, ImageFormat.NV21, previewSize.width, previewSize.height, null); baos = new ByteArrayOutputStream(); yuvimage.compressToJpeg(new Rect(0, 0, previewSize.width, previewSize.height), 100, baos);// 80--JPG圖片的質量[0-100],100最高 rawImage = baos.toByteArray(); //將rawImage轉換成bitmap BitmapFactory.Options options = new BitmapFactory.Options(); options.inPreferredConfig = Bitmap.Config.RGB_565; bitmap = BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length, options); // idx++; // text.setText(idx+""); // bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); icon.setImageBitmap(bitmap); } });
介面佈局
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.administrator.carmavideoapplication.MainActivity"> <Button android:id="@+id/button1" android:layout_width="match_parent" android:visibility="gone" android:layout_height="48dp" android:text="button1"/> <Button android:id="@+id/button2" android:visibility="gone" android:layout_width="match_parent" android:layout_height="48dp" android:text="button2"/> <TextView android:id="@+id/text" android:layout_width="match_parent" android:layout_height="48dp" /> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.example.administrator.carmavideoapplication.SurfaceViewJereh android:id="@+id/surfaceview" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1"> </com.example.administrator.carmavideoapplication.SurfaceViewJereh> <ImageView android:id="@+id/icon" android:layout_weight="1" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout> </LinearLayout>
activity初始化
首先是對ui初始化,然後關聯camera和surfaceview即可
package com.example.administrator.carmavideoapplication; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.ImageFormat; import android.graphics.Rect; import android.graphics.YuvImage; import android.hardware.Camera; import android.media.MediaRecorder; import android.os.Environment; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class MainActivity extends AppCompatActivity implements View.OnClickListener{ SurfaceViewJereh surfaceView; ImageView icon; private Button button, button2; private TextView text; private MediaRecorder mediaRecorder; private String path; private Camera camera; private SurfaceHolder.Callback callback; private int idx; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); initSurfaceView(); } private void initSurfaceView() { surfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); surfaceView.setKeepScreenOn(true); callback = new SurfaceHolder.Callback() { //在控制元件建立的時候,進行相應的初始化工作 public void surfaceCreated(SurfaceHolder holder) { //開啟相機,同時進行各種控制元件的初始化mediaRecord等 camera = Camera.open(); mediaRecorder = new MediaRecorder(); } //當控制元件發生變化的時候呼叫,進行surfaceView和camera進行繫結,可以進行畫面的顯示 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { doChange(holder); } @Override public void surfaceDestroyed(SurfaceHolder holder) { camera.setPreviewCallback(null); camera.stopPreview(); } }; //為SurfaceView設定回撥函式 surfaceView.getHolder().addCallback(callback); } ByteArrayOutputStream baos; byte[] rawImage; Bitmap bitmap; Camera.Size previewSize; BitmapFactory.Options newOpts = new BitmapFactory.Options(); //當我們的程式開始執行,即使我們沒有開始錄製視訊,我們的surFaceView中也要顯示當前攝像頭顯示的內容 private void doChange(SurfaceHolder holder) { try { camera.setPreviewDisplay(holder); //設定surfaceView旋轉的角度,系統預設的錄製是橫向的畫面,把這句話註釋掉執行你就會發現這行程式碼的作用 // camera.setDisplayOrientation(getDegree()); if (camera != null ) { try { Camera.Parameters parameters = camera.getParameters(); //獲取攝像頭引數 // parameters.setZoom(); //鏡頭縮放 // 設定預覽照片的大小 // parameters.setPreviewSize(200, 200); // 設定預覽照片時每秒顯示多少幀的最小值和最大值 // parameters.setPreviewFpsRange(4, 10); // 設定圖片格式 // parameters.setPictureFormat(ImageFormat.JPEG); // 設定JPG照片的質量 // parameters.set("jpeg-quality", 85); // 設定照片的大小 // parameters.setPictureSize(200, 200); camera.setParameters(parameters); } catch (Exception e) { e.printStackTrace(); } } newOpts.inJustDecodeBounds = true; camera.setPreviewCallback(new Camera.PreviewCallback() { @Override public void onPreviewFrame(byte[] data, Camera camera) { //處理data // previewSize = camera.getParameters().getPreviewSize();//獲取尺寸,格式轉換的時候要用到 // YuvImage yuvimage = new YuvImage( // data, // ImageFormat.NV21, // previewSize.width, // previewSize.height, // null); // baos = new ByteArrayOutputStream(); // yuvimage.compressToJpeg(new Rect(0, 0, previewSize.width, previewSize.height), 100, baos);// 80--JPG圖片的質量[0-100],100最高 // rawImage = baos.toByteArray(); // //將rawImage轉換成bitmap // BitmapFactory.Options options = new BitmapFactory.Options(); // options.inPreferredConfig = Bitmap.Config.RGB_565; // bitmap = BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length, options); //// idx++; //// text.setText(idx+""); //// bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); // icon.setImageBitmap(bitmap); } }); camera.startPreview();//開始預覽 } catch (IOException e) { e.printStackTrace(); } } public int getDegree() { //獲取當前螢幕旋轉的角度 int rotating = this.getWindowManager().getDefaultDisplay().getRotation(); int degree = 0; //根據手機旋轉的角度,來設定surfaceView的顯示的角度 switch (rotating) { case Surface.ROTATION_0: degree = 90; break; case Surface.ROTATION_90: degree = 0; break; case Surface.ROTATION_180: degree = 270; break; case Surface.ROTATION_270: degree = 180; break; } return degree; } private void init() { button = (Button) findViewById(R.id.button1); button2 = (Button) findViewById(R.id.button2); surfaceView = (SurfaceViewJereh)findViewById(R.id.surfaceview); icon = (ImageView)findViewById(R.id.icon); text = (TextView) findViewById(R.id.text); button.setOnClickListener(this); button2.setOnClickListener(this); path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/hello.3gp"; } @Override public void onClick(View v) { switch (v.getId()) { case R.id.button1: startRecord(); break; case R.id.button2: stopRecord(); break; } } private void stopRecord() { //當結束錄製之後,就將當前的資源都釋放 mediaRecorder.release(); camera.release(); mediaRecorder = null; //然後再重新初始化所有的必須的控制元件和物件 camera = Camera.open(); mediaRecorder = new MediaRecorder(); doChange(surfaceView.getHolder()); } private void startRecord() { //先釋放被佔用的camera,在將其設定為mediaRecorder所用的camera camera.unlock(); mediaRecorder.setCamera(camera); mediaRecorder.setPreviewDisplay(surfaceView.getHolder().getSurface()); //設定音訊的來源 麥克風 mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); //設定視訊的來源 mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); //設定視訊的輸出格式 3gp mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); //設定視訊中的聲音和視訊的編碼格式 mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT); //設定儲存的路徑 mediaRecorder.setOutputFile(path); //開始錄製 try { mediaRecorder.prepare(); mediaRecorder.start(); } catch (IOException e) { e.printStackTrace(); } } @Override protected void onDestroy() { super.onDestroy(); if(camera!=null) camera.release();//釋放相機資源 } }