Android使用MediaRecorder實(shí)現(xiàn)錄像功能
用MediaRecorder實(shí)現(xiàn)簡單的錄像功能
思路:定義一個(gè)SurfaceView用來顯示預(yù)覽,在SurfaceHolder的回調(diào)中用Camera對象啟動預(yù)覽。然后調(diào)用MediaRecorder來錄像。僅僅是實(shí)現(xiàn)了簡單的錄像開始和停止功能。頂部能顯示顯示錄像的時(shí)間,還有待完好。
代碼例如以下:
在AndroidManifest.xml加入以下的權(quán)限:
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!-- 硬件支持 --> <uses-feature android:name="android.hardware.camera"/> <uses-feature android:name="android.hardware.camera.autofocus"/> activity_main.xml <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <SurfaceView android:id="@+id/camera_preview" android:layout_width="match_parent" android:layout_height="match_parent" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:orientation="horizontal"> <TextView android:id="@+id/timestamp_minute_prefix" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#7F00FF" android:textSize="30sp" android:text="0"/> <TextView android:id="@+id/timestamp_minute_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#7F00FF" android:textSize="30sp" android:text="0"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#7F00FF" android:textSize="30sp" android:text=":"/> <TextView android:id="@+id/timestamp_second_prefix" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#7F00FF" android:textSize="30sp" android:text="0"/> <TextView android:id="@+id/timestamp_second_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#7F00FF" android:textSize="30sp" android:text="0"/> </LinearLayout> <ImageButton android:id="@+id/record_shutter" android:layout_width="64dp" android:layout_height="64dp" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="15dp" android:background="@android:color/transparent" android:scaleType="centerCrop" android:src="@drawable/recording_shutter" /> </RelativeLayout>
MainActivity.java
package com.jackie.videorecorder; import java.io.File; import android.app.Activity; import android.hardware.Camera; import android.hardware.Camera.Parameters; import android.hardware.Camera.Size; import android.media.MediaRecorder; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.View.OnClickListener; import android.widget.ImageButton; import android.widget.TextView; import org.w3c.dom.Text; public class MainActivity extends Activity implements OnClickListener { private SurfaceView mCameraPreview; private SurfaceHolder mSurfaceHolder; private ImageButton mShutter; private TextView mMinutePrefix; private TextView mMinuteText; private TextView mSecondPrefix; private TextView mSecondText; private Camera mCamera; private MediaRecorder mRecorder; private final static int CAMERA_ID = 0; private boolean mIsRecording = false; private boolean mIsSufaceCreated = false; private static final String TAG = "Jackie"; private Handler mHandler = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mCameraPreview = (SurfaceView) findViewById(R.id.camera_preview); mMinutePrefix = (TextView) findViewById(R.id.timestamp_minute_prefix); mMinuteText = (TextView) findViewById(R.id.timestamp_minute_text); mSecondPrefix = (TextView) findViewById(R.id.timestamp_second_prefix); mSecondText = (TextView) findViewById(R.id.timestamp_second_text); mSurfaceHolder = mCameraPreview.getHolder(); mSurfaceHolder.addCallback(mSurfaceCallback); mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); mShutter = (ImageButton) findViewById(R.id.record_shutter); mShutter.setOnClickListener(this); } @Override protected void onPause() { super.onPause(); if (mIsRecording) { stopRecording(); } stopPreview(); } private SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() { @Override public void surfaceDestroyed(SurfaceHolder holder) { mIsSufaceCreated = false; } @Override public void surfaceCreated(SurfaceHolder holder) { mIsSufaceCreated = true; } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { startPreview(); } }; //啟動預(yù)覽 private void startPreview() { //保證僅僅有一個(gè)Camera對象 if (mCamera != null || !mIsSufaceCreated) { Log.d(TAG, "startPreview will return"); return; } mCamera = Camera.open(CAMERA_ID); Parameters parameters = mCamera.getParameters(); Size size = getBestPreviewSize(CameraUtils.PREVIEW_WIDTH, CameraUtils.PREVIEW_HEIGHT, parameters); if (size != null) { parameters.setPreviewSize(size.width, size.height); } parameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); parameters.setPreviewFrameRate(20); //設(shè)置相機(jī)預(yù)覽方向 mCamera.setDisplayOrientation(90); mCamera.setParameters(parameters); try { mCamera.setPreviewDisplay(mSurfaceHolder); // mCamera.setPreviewCallback(mPreviewCallback); } catch (Exception e) { Log.d(TAG, e.getMessage()); } mCamera.startPreview(); } private void stopPreview() { //釋放Camera對象 if (mCamera != null) { try { mCamera.setPreviewDisplay(null); } catch (Exception e) { Log.e(TAG, e.getMessage()); } mCamera.stopPreview(); mCamera.release(); mCamera = null; } } private Camera.Size getBestPreviewSize(int width, int height, Camera.Parameters parameters) { Camera.Size result = null; for (Camera.Size size : parameters.getSupportedPreviewSizes()) { if (size.width <= width && size.height <= height) { if (result == null) { result = size; } else { int resultArea = result.width * result.height; int newArea = size.width * size.height; if (newArea > resultArea) { result = size; } } } } return result; } @Override public void onClick(View v) { if (mIsRecording) { stopRecording(); } else { initMediaRecorder(); startRecording(); //開始錄像后,每隔1s去更新錄像的時(shí)間戳 mHandler.postDelayed(mTimestampRunnable, 1000); } } private void initMediaRecorder() { mRecorder = new MediaRecorder();//實(shí)例化 mCamera.unlock(); //給Recorder設(shè)置Camera對象,保證錄像跟預(yù)覽的方向保持一致 mRecorder.setCamera(mCamera); mRecorder.setOrientationHint(90); //改變保存后的視頻文件播放時(shí)是否橫屏(不加這句。視頻文件播放的時(shí)候角度是反的) mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); // 設(shè)置從麥克風(fēng)採集聲音 mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); // 設(shè)置從攝像頭採集圖像 mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); // 設(shè)置視頻的輸出格式 為MP4 mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT); // 設(shè)置音頻的編碼格式 mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); // 設(shè)置視頻的編碼格式 mRecorder.setVideoSize(176, 144); // 設(shè)置視頻大小 mRecorder.setVideoFrameRate(20); // 設(shè)置幀率 // mRecorder.setMaxDuration(10000); //設(shè)置最大錄像時(shí)間為10s mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface()); //設(shè)置視頻存儲路徑 File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES) + File.separator + "VideoRecorder"); if (!file.exists()) { //多級目錄的創(chuàng)建 file.mkdirs(); } mRecorder.setOutputFile(file.getPath() + File.separator + "VID_" + System.currentTimeMillis() + ".mp4"); } private void startRecording() { if (mRecorder != null) { try { mRecorder.prepare(); mRecorder.start(); } catch (Exception e) { mIsRecording = false; Log.e(TAG, e.getMessage()); } } mShutter.setImageDrawable(getResources().getDrawable(R.drawable.recording_shutter_hl)); mIsRecording = true; } private void stopRecording() { if (mCamera != null) { mCamera.lock(); } if (mRecorder != null) { mRecorder.stop(); mRecorder.release(); mRecorder = null; } mShutter.setImageDrawable(getResources().getDrawable(R.drawable.recording_shutter)); mIsRecording = false; mHandler.removeCallbacks(mTimestampRunnable); //將錄像時(shí)間還原 mMinutePrefix.setVisibility(View.VISIBLE); mMinuteText.setText("0"); mSecondPrefix.setVisibility(View.VISIBLE); mSecondText.setText("0"); //重新啟動預(yù)覽 startPreview(); } private Runnable mTimestampRunnable = new Runnable() { @Override public void run() { updateTimestamp(); mHandler.postDelayed(this, 1000); } }; private void updateTimestamp() { int second = Integer.parseInt(mSecondText.getText().toString()); int minute = Integer.parseInt(mMinuteText.getText().toString()); second++; Log.d(TAG, "second: " + second); if (second < 10) { mSecondText.setText(String.valueOf(second)); } else if (second >= 10 && second < 60) { mSecondPrefix.setVisibility(View.GONE); mSecondText.setText(String.valueOf(second)); } else if (second >= 60) { mSecondPrefix.setVisibility(View.VISIBLE); mSecondText.setText("0"); minute++; mMinuteText.setText(String.valueOf(minute)); } else if (minute >= 60) { mMinutePrefix.setVisibility(View.GONE); } } }
效果例如以下:
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android DrawableTextView圖片文字居中顯示實(shí)例
在我們開發(fā)中,TextView設(shè)置Android:drawableLeft一定使用的非常多,但Drawable和Text同時(shí)居中顯示可能不好控制,小編想到通過自定義TextView實(shí)現(xiàn),具體詳情大家參考下本文2017-03-03Android開發(fā)中使用mms模塊收發(fā)單卡和雙卡短信的教程
這篇文章主要介紹了Android開發(fā)中使用mms模塊收發(fā)單卡和雙卡短信的教程,文中舉了MOTO XT800手機(jī)(估計(jì)已經(jīng)落伍很久了--)的例子來說明如何解決雙卡雙待時(shí)的短信異常問題,需要的朋友可以參考下2016-02-02Android 7.0開發(fā)獲取存儲設(shè)備信息的方法
這篇文章主要介紹了Android 7.0開發(fā)獲取存儲設(shè)備信息的方法,結(jié)合實(shí)例形式分析了Android7.0針對存儲設(shè)備信息的獲取、判斷操作方法與相關(guān)注意事項(xiàng),需要的朋友可以參考下2017-11-11Android NDK開發(fā)的環(huán)境搭建與簡單示例
本文主要介紹Android NDK的知識,這里整理了相關(guān)資料,來說明如何搭建相應(yīng)環(huán)境和簡單實(shí)例,幫助大家理解,有興趣的小伙伴可以參考下2016-09-09Android實(shí)現(xiàn)多次閃退清除數(shù)據(jù)
這篇文章主要介紹了Android實(shí)現(xiàn)多次閃退清除數(shù)據(jù)的相關(guān)資料,感興趣的小伙伴們可以參考一下2016-04-04android電源信息查看(電量、溫度、電壓)實(shí)例代碼
這篇文章主要介紹了android電源信息查看方法,以實(shí)例形式較為詳細(xì)的分析了Android實(shí)現(xiàn)電源電量、電壓、溫度等信息查看的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10Android畫板開發(fā)之添加背景和保存畫板內(nèi)容為圖片
這篇文章主要為大家詳細(xì)介紹了Android畫板開發(fā)之添加背景和保存畫板內(nèi)容為圖片,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12Android自定義ViewGroup之FlowLayout(三)
這篇文章主要為大家詳細(xì)介紹了Android自定義ViewGroup之FlowLayout,常用于關(guān)鍵字標(biāo)簽,搜索熱詞列表等功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09Android 用adb pull或push 拷貝手機(jī)文件到到電腦上,拷貝手機(jī)數(shù)據(jù)庫到電腦上,拷貝電腦數(shù)據(jù)庫到手機(jī)上
這篇文章主要介紹了Android 用adb pull或push 拷貝手機(jī)文件到到電腦上,拷貝手機(jī)數(shù)據(jù)庫到電腦上,拷貝電腦數(shù)據(jù)庫到手機(jī)上 的相關(guān)資料,需要的朋友可以參考下2016-01-01