媒体录制MediaRecorder
MediaRecorder是Android自带的录制工具,通过操纵摄像头和麦克风完成媒体录制,既可录制视频,也可单独录制音频。其中对摄像头Camera的介绍参见《 Android开发笔记(五十六)摄像头拍照》。
下面是MediaRecorder的常用方法:
reset : 重置录制资源
prepare : 准备录制
start : 开始录制
stop : 结束录制
release : 释放录制资源
以上方法用于控制录像/录音操作。
setCamera : 设置摄像头对象
setPreviewDisplay : 设置预览界面。一般使用SurfaceHolder.getSurface
setOrientationHint : 设置预览的角度。跟拍照一样一般设置90度
以上三个方法用于录制视频,如果仅是录音则不需要这三个方法。
setOnErrorListener : 设置错误监听器。可监听服务器异常以及未知错误的事件。
setOnInfoListener : 设置信息监听器。可监听录制结束事件,包括达到录制时长或者达到录制大小。
以上方法用于关联录像工具和事件。
setAudioSource : 设置音频来源。一般使用麦克风AudioSource.MIC。
setAudioEncoder : 设置音频编码器。AudioEncoder.AMR_NB表示窄带编码,AudioEncoder.AMR_WB表示宽带编码,AudioEncoder.AAC表示低复杂度编码,AudioEncoder.HE_AAC表示高效率编码,AudioEncoder.AAC_ELD表示增强型低延迟编码。
注意:setAudioEncoder应在setOutputFormat之后执行,否则会出现“setAudioEncoder called in an invalid state(2)”的异常。
setAudioSamplingRate : 设置音频的采样率,单位赫兹(Hz)。该方法为可选
setAudioChannels : 设置音频的声道数。1表示单声道,2表示双声道。该方法为可选
setAudioEncodingBitRate : 设置音频每秒录制的字节数。越大则音频越清晰。该方法为可选
以上五个方法用于录制音频。
setVideoSource : 设置视频来源。一般使用摄像头VideoSource.CAMERA。
setVideoEncoder : 设置视频编码器。VideoEncoder.MPEG_4_SP表示MPEG4编码,H263表示h263编码,H264表示h264编码。
setVideoSize : 设置视频的分辨率。
setVideoEncodingBitRate : 设置视频每秒录制的字节数。越大则视频越清晰。该方法为可选
setVideoFrameRate : 设置视频每秒录制的帧数。越大则视频越连贯,当然大小也越大。该方法为可选
以上五个方法用于录制视频,如果仅是录音则不需要这五个方法。
setMaxDuration : 设置录制时长。
setMaxFileSize : 设置录制的媒体大小。
setOutputFormat : 设置媒体输出格式。OutputFormat.MPEG_4表示MPEG4格式,OutputFormat.THREE_GPP表示3GP格式。
setOutputFile : 设置输出文件的路径。
以上方法用于设置录像/录音参数。
媒体播放MediaPlayer
MediaPlayer是Android自带的播放器,可用于播放视频与音频。下面是MediaPlayer的常用方法:
reset : 重置播放器
prepare : 准备播放
start : 开始播放
pause : 暂停播放
stop : 停止播放
以上方法用于控制播放操作。
setOnPreparedListener : 设置准备播放监听器。
setOnCompletionListener : 设置结束播放监听器。
setOnSeekCompleteListener : 设置播放拖动监听器。
setOnVideoSizeChangedListener : 设置视频缩放监听器。
以上方法用于关联播放事件。
create : 创建指定Uri的播放器。
setDataSource : 设置播放数据来源。create与setDataSource只需设置其一。
setVolume : 设置音量。第一个参数是左声道,第二个参数是右声道。
setAudioStreamType : 设置音频流的类型。AudioManager.STREAM_MUSIC表示音乐,AudioManager.STREAM_RING表示铃声,AudioManager.STREAM_ALARM表示闹钟,AudioManager.STREAM_NOTIFICATION表示通知。
setDisplay : 设置播放界面,参数为SurfaceHolder类型。该方法为视频播放专用,播放音频不需要该方法。
setSurface : 设置播放表层,一般使用SurfaceHolder.getSurface。setDisplay与setSurface只需设置其一。
setLooping : 设置是否循环播放。
isPlaying : 判断是否正在播放。
seekTo : 拖动播放进度到指定位置。
getCurrentPosition : 获取当前播放进度所在的位置。
getDuration : 获取播放时长。
以上方法用于设置播放参数。
录制/播放事件
下面是几个媒体录制/播放用到的回调事件接口:
录制异常事件
监听器类名 : MediaRecorder.OnErrorListener
设置监听器的方法 : MediaRecorder.setOnErrorListener
监听器需要重写的方法 : onError
录制完毕事件
监听器类名 : MediaRecorder.OnInfoListener
设置监听器的方法 : MediaRecorder.setOnInfoListener
监听器需要重写的方法 : onInfo,其中what值为MEDIA_RECORDER_INFO_MAX_DURATION_REACHED表示达到录制时长,为MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED表示达到录制大小。
准备播放事件
监听器类名 : MediaPlayer.OnPreparedListener
设置监听器的方法 : MediaPlayer.setOnPreparedListener
监听器需要重写的方法 : onPrepared
播放结束事件
监听器类名 : MediaPlayer.OnCompletionListener
设置监听器的方法 : MediaPlayer.setOnCompletionListener
监听器需要重写的方法 : onCompletion
拖动播放事件
监听器类名 : MediaPlayer.OnSeekCompleteListener
设置监听器的方法 : MediaPlayer.setOnSeekCompleteListener
监听器需要重写的方法 : onSeekComplete
视频缩放事件
监听器类名 : MediaPlayer.OnVideoSizeChangedListener
设置监听器的方法 : MediaPlayer.setOnVideoSizeChangedListener
监听器需要重写的方法 : onVideoSizeChanged
录像与录音需要在AndroidManifest.xml中加上如下权限(录制操作通常要操作SD卡,所以也要加上SD卡的权限):
<!-- 录像/录音 -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_VIDEO"/>
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- SD卡 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
代码示例
下面是录像视图VideoRecorderView的代码示例:
import java.io.File;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
import com.example.exmcamera.R;
import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.media.MediaRecorder;
import android.media.MediaRecorder.AudioEncoder;
import android.media.MediaRecorder.AudioSource;
import android.media.MediaRecorder.OnErrorListener;
import android.media.MediaRecorder.OnInfoListener;
import android.media.MediaRecorder.OutputFormat;
import android.media.MediaRecorder.VideoEncoder;
import android.media.MediaRecorder.VideoSource;
import android.os.Environment;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
public class VideoRecorderView extends LinearLayout implements OnErrorListener,OnInfoListener {
private static final String TAG = "VideoRecorderView";
private SurfaceView mSurfaceView;
private SurfaceHolder mSurfaceHolder;
private ProgressBar mProgressBar;
private MediaRecorder mMediaRecorder;
private Camera mCamera;
private Timer mTimer; //计时器
private OnRecordFinishListener mOnRecordFinishListener; //录制完成回调接口
private int mWidth; //视频分辨率宽度
private int mHeight; //视频分辨率高度
private int mRecordMaxTime = 10; //一次拍摄最长时间
private int mTimeCount; //时间计数
private File mRecordFile = null; //文件
public VideoRecorderView(Context context) {
this(context, null);
}
public VideoRecorderView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public VideoRecorderView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mWidth = (int) context.getResources().getDimension(R.dimen.video_height);
mHeight = (int) context.getResources().getDimension(R.dimen.video_width);
LayoutInflater.from(context).inflate(R.layout.movie_recorder_view, this);
mSurfaceView = (SurfaceView) findViewById(R.id.surfaceview);
mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
mProgressBar.setMax(mRecordMaxTime); //设置进度条最大量
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(new CustomCallBack());
}
//开始录制视频
public void record(final OnRecordFinishListener onRecordFinishListener) {
mOnRecordFinishListener = onRecordFinishListener;
createRecordDir();
try {
initCamera();
initRecord();
mTimeCount = 0; //时间计数器重新赋值
mTimer = new Timer();
mTimer.schedule(new TimerTask() {
@Override
public void run() {
mProgressBar.setProgress(mTimeCount++); //设置进度条
}
}, 0, 1000); //每隔1000毫秒就重复执行run函数
} catch (Exception e) {
Log.d(TAG, "record error: " + e.getMessage());
}
}
//停止拍摄
public void stop() {
stopRecord();
releaseRecord();
freeCameraResource();
}
public File getRecordFile() {
return mRecordFile;
}
private class CustomCallBack implements Callback {
@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
initCamera();
} catch (IOException e) {
Log.d(TAG, "surfaceCreated error: "+e.getMessage());
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
freeCameraResource();
}
}
private void initCamera() throws IOException {
if (mCamera != null) {
freeCameraResource();
}
try {
mCamera = Camera.open();
} catch (Exception e) {
Log.d(TAG, "Camera.open() error: "+e.getMessage());
freeCameraResource();
}
if (mCamera == null) {
return;
}
setCameraParams();
mCamera.setDisplayOrientation(90);
mCamera.setPreviewDisplay(mSurfaceHolder);
mCamera.startPreview();
mCamera.unlock();
}
//设置摄像头为竖屏
private void setCameraParams() {
if (mCamera != null) {
Parameters params = mCamera.getParameters();
params.set("orientation", "portrait");
mCamera.setParameters(params);
}
}
private void freeCameraResource() {
if (mCamera != null) {
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.lock();
mCamera.release();
mCamera = null;
}
}
private void createRecordDir() {
File sampleDir = new File(Environment.getExternalStorageDirectory()
+ File.separator + "RecordVideo/");
if (!sampleDir.exists()) {
sampleDir.mkdirs();
}
File recordDir = sampleDir;
try {
mRecordFile = File.createTempFile("recording", ".mp4", recordDir);
Log.d(TAG, mRecordFile.getAbsolutePath());
} catch (Exception e) {
Log.d(TAG, "createTempFile error: " + e.getMessage());
}
}
private void initRecord() {
mMediaRecorder = new MediaRecorder();
mMediaRecorder.reset();
if (mCamera != null) {
mMediaRecorder.setCamera(mCamera);
}
mMediaRecorder.setOnErrorListener(this);
mMediaRecorder.setOnInfoListener(this);
mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
mMediaRecorder.setVideoSource(VideoSource.CAMERA); //视频源
mMediaRecorder.setAudioSource(AudioSource.MIC); //音频源
mMediaRecorder.setOutputFormat(OutputFormat.MPEG_4); //视频输出格式
mMediaRecorder.setAudioEncoder(AudioEncoder.AMR_NB); //音频格式
mMediaRecorder.setVideoSize(mWidth, mHeight); //设置分辨率:
//mMediaRecorder.setVideoFrameRate(16); //setVideoFrameRate与setVideoEncodingBitRate设置其一即可
mMediaRecorder.setVideoEncodingBitRate(1 * 1024 * 512); //设置帧频率,然后就清晰了
mMediaRecorder.setOrientationHint(90); //输出旋转90度,保持竖屏录制
mMediaRecorder.setVideoEncoder(VideoEncoder.MPEG_4_SP); //视频录制格式
mMediaRecorder.setMaxDuration(mRecordMaxTime * 1000); //设置录制时长
//mMediaRecorder.setMaxFileSize(1024*1024*10); //setMaxFileSize与setMaxDuration设置其一即可
mMediaRecorder.setOutputFile(mRecordFile.getAbsolutePath());
try {
mMediaRecorder.prepare();
mMediaRecorder.start();
} catch (Exception e) {
Log.d(TAG, "MediaRecorder.start error: " + e.getMessage());
}
}
private void stopRecord() {
mProgressBar.setProgress(0);
if (mTimer != null) {
mTimer.cancel();
}
if (mMediaRecorder != null) {
mMediaRecorder.setOnErrorListener(null);
mMediaRecorder.setPreviewDisplay(null);
try {
mMediaRecorder.stop();
} catch (Exception e) {
Log.d(TAG, "MediaRecorder.stop error: " + e.getMessage());
}
}
}
private void releaseRecord() {
if (mMediaRecorder != null) {
mMediaRecorder.setOnErrorListener(null);
mMediaRecorder.release();
mMediaRecorder = null;
}
}
//录制完成回调接口
public interface OnRecordFinishListener {
public void onRecordFinish();
}
@Override
public void onError(MediaRecorder mr, int what, int extra) {
if (mr != null) {
mr.reset();
}
}
@Override
public void onInfo(MediaRecorder mr, int what, int extra) {
if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
|| what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {
stop();
if (mOnRecordFinishListener != null) {
mOnRecordFinishListener.onRecordFinish();
}
}
}
}
下面是录制与播放页面VideoActivity的代码示例:
import com.example.exmcamera.R;
import com.example.exmcamera.widget.VideoRecorderView;
import android.app.Activity;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.View.OnClickListener;
import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
import android.widget.Button;
public class VideoActivity extends Activity implements OnClickListener {
private static final String TAG = "VideoActivity";
private VideoRecorderView mVideoView;
private Button start_btn;
private Button stop_btn;
private Button play_btn;
private Button pause_btn;
private SurfaceView mSurfaceView;
private MediaPlayer mMediaPlayer;
private int mPosition;
private boolean bFirstPlay = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_video);
mVideoView = (VideoRecorderView) findViewById(R.id.moive_rv);
start_btn = (Button) findViewById(R.id.start_btn);
stop_btn = (Button) findViewById(R.id.stop_btn);
play_btn = (Button) findViewById(R.id.play_btn);
pause_btn = (Button) findViewById(R.id.pause_btn);
start_btn.setOnClickListener(this);
stop_btn.setOnClickListener(this);
play_btn.setOnClickListener(this);
pause_btn.setOnClickListener(this);
start_btn.setEnabled(true);
stop_btn.setEnabled(false);
play_btn.setEnabled(false);
pause_btn.setEnabled(false);
init();
}
private void init() {
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setOnCompletionListener(new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
play_btn.setEnabled(true);
pause_btn.setEnabled(false);
bFirstPlay = true;
}
});
mSurfaceView = (SurfaceView) this.findViewById(R.id.play_surfaceV);
mSurfaceView.getHolder().addCallback(new Callback() {
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (mPosition > 0) {
try {
play();
mMediaPlayer.seekTo(mPosition);
mPosition = 0;
} catch (Exception e) {
Log.d(TAG, "surfaceCreated error: "+e.getMessage());
}
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
});
}
@Override
protected void onPause() {
// 先判断是否正在播放
if (mMediaPlayer.isPlaying()) {
// 如果正在播放我们就先保存这个播放位置
mPosition = mMediaPlayer.getCurrentPosition();
mMediaPlayer.stop();
}
super.onPause();
}
private void preplay() {
try {
mMediaPlayer.reset();
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
// 设置需要播放的视频
String path = mVideoView.getRecordFile().getAbsolutePath();
mMediaPlayer.setDataSource(path);
Log.d(TAG, "video path = "+path);
// 把视频画面输出到SurfaceView
mMediaPlayer.setDisplay(mSurfaceView.getHolder());
mMediaPlayer.prepare();
} catch (Exception e) {
Log.d(TAG, "mMediaPlayer.prepare error: "+e.getMessage());
}
}
private void play() {
try {
preplay();
//播放
mMediaPlayer.start();
} catch (Exception e) {
Log.d(TAG, "mMediaPlayer.start error: "+e.getMessage());
}
}
private void stopRecord() {
start_btn.setEnabled(true);
stop_btn.setEnabled(false);
play_btn.setEnabled(true);
preplay();
}
@Override
public void onClick(View v) {
int resid = v.getId();
if (resid == R.id.start_btn) {
mVideoView.record(new VideoRecorderView.OnRecordFinishListener() {
@Override
public void onRecordFinish() {
stopRecord();
}
});
start_btn.setEnabled(false);
stop_btn.setEnabled(true);
} else if (resid == R.id.stop_btn) {
mVideoView.stop();
stopRecord();
} else if (resid == R.id.play_btn) {
if (bFirstPlay == true) {
play();
bFirstPlay = false;
} else {
mMediaPlayer.start();
}
play_btn.setEnabled(false);
pause_btn.setEnabled(true);
} else if (resid == R.id.pause_btn) {
mMediaPlayer.pause();
play_btn.setEnabled(true);
pause_btn.setEnabled(false);
}
}
}
点此查看Android开发笔记的完整目录