Android 实现视频录制并播放
16lz
2021-01-23
AndroidManifest.xml:
VideoRead:
工程用到的lib: xUtils-2.6.14.jar 代码git路径: 点击打开链接 需要的图片:bg_movie_add_shoot.png
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.mediarecorder" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="18" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <!-- 下面的是提供给浏览器调用并传参的 --> <intent-filter> <action android:name="android.intent.action.VIEW"/> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="myapp" android:host="jp.app" android:pathPrefix="/openwith"/> </intent-filter> </activity> <activity android:name=".VideoRead"></activity> </application> <uses-permission android:name="android.permission.CAMERA" /> <!-- 调用摄像头权限 --><uses-feature android:name="android.hardware.camera"/><uses-feature android:name="android.hardware.camera.autofocus"/><!-- 硬件支持 --> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <!-- 录制视频/音频权限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- sd卡读写权限 --> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> <!-- 挂载sd卡 --><uses-permission android:name="android.permission.INTERNET" /><!-- 访问网络 --></manifest>values中的strings.xml文件:
<?xml version="1.0" encoding="utf-8"?><resources> <string name="app_name">MediaRecorder</string> <string name="action_settings">Settings</string> <string name="hello_world">Hello world!</string><string name="start">开始</string><string name="pre">暂停</string><string name="stop">上传</string></resources>values中的attrs.xml文件:
<?xml version="1.0" encoding="utf-8"?><resources> <declare-styleable name="MovieRecorderView"> <!-- 开始是否打开摄像头 --> <attr name="is_open_camera" format="boolean" /> <!-- 一次拍摄最长时间 --> <attr name="record_max_time" format="integer"/> <!-- 视频分辨率宽度 --> <attr name="width" format="integer"/> <!-- 视频分辨率高度 --> <attr name="height" format="integer"/> </declare-styleable> </resources>layout中的activity_main.xml:
<?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:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/black" android:orientation="vertical"> <com.example.mediarecorder.MovieRecorderView android:id="@+id/movieRecorderView" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:layout_margin="3dp" /> <Button android:id="@+id/shoot_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:background="@drawable/bg_movie_add_shoot" android:text="按住拍" android:textColor="#ECEBF1"/> </LinearLayout>layout中的activity_video.xml:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:weightSum="1"> <SurfaceView android:layout_height="220dip" android:layout_gravity="center" android:id="@+id/surface" android:layout_weight="0.25" android:layout_width="320dip"></SurfaceView> <LinearLayout android:id="@+id/linearLayout1" android:layout_height="wrap_content" android:layout_width="fill_parent"> <Button android:text="播放" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <Button android:text="暂停" android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <Button android:text="停止" android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <Button android:text="上传" android:id="@+id/button4" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> </LinearLayout></LinearLayout>layout中的movie_recorder_view.xml:
<?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:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/background_dark" android:orientation="vertical"> <SurfaceView android:id="@+id/surfaceview" android:layout_width="fill_parent" android:layout_height="0dp" android:layout_weight="1" /> <ProgressBar android:id="@+id/progressBar" style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="2dp" /> </LinearLayout>MainActivity:
import android.annotation.SuppressLint;import android.app.Activity;import android.content.Intent;import android.net.Uri;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.view.MotionEvent;import android.view.View;import android.view.View.OnTouchListener;import android.widget.Button;import android.widget.Toast;import com.example.mediarecorder.MovieRecorderView.OnRecordFinishListener;/** * 视频录制 * @author 胡汉三 * */public class MainActivity extends Activity {private MovieRecorderView mRecorderView;private Button mShootBtn;private boolean isFinish = true;private String userId = "";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Intent i_getvalue = getIntent(); String action = i_getvalue.getAction(); if(Intent.ACTION_VIEW.equals(action)){ Uri uri = i_getvalue.getData(); if(uri != null){ userId = uri.getQueryParameter("userId"); } }mRecorderView = (MovieRecorderView) findViewById(R.id.movieRecorderView);mShootBtn = (Button) findViewById(R.id.shoot_button);mShootBtn.setOnTouchListener(new OnTouchListener() {@Override public boolean onTouch(View v, MotionEvent event) {if (event.getAction() == MotionEvent.ACTION_DOWN) {mRecorderView.record(new OnRecordFinishListener() {@Overridepublic void onRecordFinish() {handler.sendEmptyMessage(1);}});} else if (event.getAction() == MotionEvent.ACTION_UP) {if (mRecorderView.getTimeCount() > 1)handler.sendEmptyMessage(1);else {if (mRecorderView.getmVecordFile() != null)mRecorderView.getmVecordFile().delete();mRecorderView.stop();Toast.makeText(MainActivity.this, "视频录制时间太短", Toast.LENGTH_SHORT).show();}}return true; }});}@Overridepublic void onResume() {super.onResume();isFinish = true;}@Overridepublic void onSaveInstanceState(Bundle outState) {super.onSaveInstanceState(outState);isFinish = false;mRecorderView.stop();}@Overridepublic void onPause() {super.onPause();}@Overridepublic void onDestroy() {super.onDestroy();}@SuppressLint("HandlerLeak")private Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {finishActivity();}};private void finishActivity() {if (isFinish) {mRecorderView.stop();Intent intent = new Intent(MainActivity.this, VideoRead.class);// 新建Bundle对象 Bundle mBundle = new Bundle(); // 放入path参数mBundle.putString("path", mRecorderView.getmVecordFile().toString()); mBundle.putString("userId", userId);intent.putExtras(mBundle); startActivity(intent); //VideoPlayerActivity.startActivity(this, mRecorderView.getmVecordFile().toString());} } /** * 录制完成回调 * * @author 胡汉三 * * @date 2015-2-9 */public interface OnShootCompletionListener {public void OnShootSuccess(String path, int second);public void OnShootFailure();}}MovieRecorderView.java:
import java.io.File;import java.io.IOException;import java.util.Timer;import java.util.TimerTask; import android.content.Context;import android.content.res.TypedArray;import android.graphics.Point;import android.hardware.Camera;import android.hardware.Camera.Parameters;import android.media.MediaRecorder;import android.media.MediaRecorder.OnErrorListener;import android.os.Environment;import android.util.AttributeSet;import android.view.Display;import android.view.LayoutInflater;import android.view.SurfaceHolder;import android.view.SurfaceHolder.Callback;import android.view.SurfaceView;import android.view.WindowManager;import android.widget.LinearLayout;import android.widget.ProgressBar; import com.lidroid.xutils.util.LogUtils; /** * 视频播放控件 * * @author 胡汉三 * */public class MovieRecorderView extends LinearLayout implements OnErrorListener { 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 boolean isOpenCamera;// 是否一开始就打开摄像头 private int mRecordMaxTime;// 一次拍摄最长时间 private int mTimeCount;// 时间计数 private File mVecordFile = null;// 文件 public MovieRecorderView(Context context) { this(context, null); } public MovieRecorderView(Context context, AttributeSet attrs) { this(context, attrs, 0); } @SuppressWarnings("deprecation")public MovieRecorderView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); Point size = new Point(); Display display = wm.getDefaultDisplay(); display.getSize(size); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MovieRecorderView, defStyle, 0); mWidth = a.getInteger(R.styleable.MovieRecorderView_width, 320);// 默认320 mHeight = a.getInteger(R.styleable.MovieRecorderView_height, 240);// 默认240 isOpenCamera = a.getBoolean(R.styleable.MovieRecorderView_is_open_camera, true);// 默认打开 mRecordMaxTime = a.getInteger(R.styleable.MovieRecorderView_record_max_time, 20);// 默认为10 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()); mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); a.recycle(); } /** * * @author 胡汉三 * */ private class CustomCallBack implements Callback { @Override public void surfaceCreated(SurfaceHolder holder) { if (!isOpenCamera) return; try { initCamera(); } catch (IOException e) { e.printStackTrace(); } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { if (!isOpenCamera) return; freeCameraResource(); } } /** * 初始化摄像头 * * @author 胡汉三 * @throws IOException */ private void initCamera() throws IOException { if (mCamera != null) { freeCameraResource(); } try { mCamera = Camera.open(); } catch (Exception e) { e.printStackTrace(); freeCameraResource(); } if (mCamera == null) return; setCameraParams(); mCamera.setDisplayOrientation(90); mCamera.setPreviewDisplay(mSurfaceHolder); mCamera.startPreview(); mCamera.unlock(); } /** * 设置摄像头为竖屏 * * @author 胡汉三 */ private void setCameraParams() { if (mCamera != null) { Parameters params = mCamera.getParameters(); params.set("orientation", "portrait"); mCamera.setParameters(params); } } /** * 释放摄像头资源 * * @author 胡汉三 */ 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 + "im/video/"); if (!sampleDir.exists()) { sampleDir.mkdirs(); } File vecordDir = sampleDir; // 创建文件 try { mVecordFile = File.createTempFile("recording", ".mp4", vecordDir);//mp4格式 LogUtils.i(mVecordFile.getAbsolutePath()); } catch (IOException e) { } } /** * 初始化 * * @author 胡汉三 * @throws IOException */ private void initRecord() throws IOException { mMediaRecorder = new MediaRecorder(); mMediaRecorder.reset(); if (mCamera != null) mMediaRecorder.setCamera(mCamera); mMediaRecorder.setOnErrorListener(this); mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface()); // 设置从摄像头采集图像 mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); // 设置从麦克风采集声音(或来自录像机的声音AudioSource.CAMCORDER) mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); // 设置视频文件的输出格式 // 必须在设置声音编码格式、图像编码格式之前设置 mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); // 设置声音编码的格式 mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); /* mMediaRecorder.setAudioChannels(1);//1单声道 2 多声道 mMediaRecorder.setAudioSamplingRate(12500); //设置录制的音频采样率——频率越高,音质越好 mMediaRecorder.setAudioEncodingBitRate(16); // 设置录制的音频编码比特率*/ mMediaRecorder.setVideoEncodingBitRate(1 * 1024 * 1024);// 设置帧频率,然后就清晰了 mMediaRecorder.setOrientationHint(90);// 输出旋转90度,保持竖屏录制 // 设置图像编码的格式 mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); mMediaRecorder.setVideoSize(mWidth, mHeight); // 每秒 4帧 //mMediaRecorder.setVideoFrameRate(20); mMediaRecorder.setOutputFile(mVecordFile.getAbsolutePath()); mMediaRecorder.prepare(); try { mMediaRecorder.start(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (RuntimeException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } /** * 开始录制视频 * * @author 胡汉三 * @param fileName * 视频储存位置 * @param onRecordFinishListener * 达到指定时间之后回调接口 */ public void record(final OnRecordFinishListener onRecordFinishListener) { this.mOnRecordFinishListener = onRecordFinishListener; createRecordDir(); try { if (!isOpenCamera)// 如果未打开摄像头,则打开 initCamera(); initRecord(); mTimeCount = 0;// 时间计数器重新赋值 mTimer = new Timer(); mTimer.schedule(new TimerTask() { @Override public void run() { mTimeCount++; mProgressBar.setProgress(mTimeCount);// 设置进度条 if (mTimeCount == mRecordMaxTime) {// 达到指定时间,停止拍摄 stop(); if (mOnRecordFinishListener != null) mOnRecordFinishListener.onRecordFinish(); } } }, 0, 1000); } catch (IOException e) { e.printStackTrace(); } } /** * 停止拍摄 * * @author 胡汉三 */ public void stop() { stopRecord(); releaseRecord(); freeCameraResource(); } /** * 停止录制 * * @author 胡汉三 */ public void stopRecord() { mProgressBar.setProgress(0); if (mTimer != null) mTimer.cancel(); if (mMediaRecorder != null) { // 设置后不会崩 mMediaRecorder.setOnErrorListener(null); mMediaRecorder.setPreviewDisplay(null); try { mMediaRecorder.stop(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (RuntimeException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } } /** * 释放资源 * * @author 胡汉三 */ private void releaseRecord() { if (mMediaRecorder != null) { mMediaRecorder.setOnErrorListener(null); try { mMediaRecorder.release(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } mMediaRecorder = null; } public int getTimeCount() { return mTimeCount; } /** * @return the mVecordFile */ public File getmVecordFile() { return mVecordFile; } /** * 录制完成回调接口 * * @author 胡汉三 * */ public interface OnRecordFinishListener { public void onRecordFinish(); } @Override public void onError(MediaRecorder mr, int what, int extra) { try { if (mr != null) mr.reset(); } catch (IllegalStateException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } }}OnUploadSuccessListener:
public interface OnUploadSuccessListener {public void onUploadSuccess(String result);}UploadUtil:
import com.lidroid.xutils.HttpUtils;import com.lidroid.xutils.exception.HttpException;import com.lidroid.xutils.http.RequestParams;import com.lidroid.xutils.http.ResponseInfo;import com.lidroid.xutils.http.callback.RequestCallBack;import com.lidroid.xutils.http.client.HttpRequest.HttpMethod;/** * * 上传工具类 */public class UploadUtil {public void uploadMethod(final RequestParams params,final String uploadHost) { HttpUtils http = new HttpUtils();http.send(HttpMethod.POST, uploadHost, params,new RequestCallBack <String>() { @Override public void onStart() { // msgTextview.setText("conn..."); } @Override public void onLoading(long total, long current,boolean isUploading) { if (isUploading) { // msgTextview.setText("upload: " + current + "/"+ total); } else { // msgTextview.setText("reply: " + current + "/"+ total); } } @Override public void onSuccess(ResponseInfo<String> responseInfo) { // msgTextview.setText("reply: " + responseInfo.result); } @Override public void onFailure(HttpException error, String msg) { // msgTextview.setText(error.getExceptionCode() + ":" + msg); } }); } }
VideoRead:
import java.io.File;import com.lidroid.xutils.http.RequestParams;import android.annotation.SuppressLint;import android.app.Activity;import android.media.AudioManager;import android.media.MediaPlayer;import android.os.Bundle;import android.view.SurfaceHolder;import android.view.SurfaceView;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;/** * 视频 * @author 胡汉三 * */public class VideoRead extends Activity implements SurfaceHolder.Callback{/** Called when the activity is first created. */ MediaPlayer player; SurfaceView surface; SurfaceHolder surfaceHolder; Button play,pause,stop,upload; String path = null; @SuppressWarnings("deprecation")@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_video); play=(Button)findViewById(R.id.button1); pause=(Button)findViewById(R.id.button2); stop=(Button)findViewById(R.id.button3); upload = (Button)findViewById(R.id.button4); surface=(SurfaceView)findViewById(R.id.surface); surfaceHolder=surface.getHolder(); //SurfaceHolder是SurfaceView的控制接口 surfaceHolder.addCallback(this); //因为这个类实现了SurfaceHolder.Callback接口,所以回调参数直接this surfaceHolder.setFixedSize(320, 220);//显示的分辨率,不设置为视频默认 surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);//Surface类型 play.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { player.start(); }}); pause.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { player.pause(); }}); stop.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { player.stop(); }}); upload.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { //上传 uploadFile(); }}); } @Override public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) { } //文件上传 @SuppressLint("SdCardPath")public void uploadFile(){ path = this.getIntent().getStringExtra("path"); String userId = this.getIntent().getStringExtra("userId"); String uploadHost = "http://192.168.1.103:808/sjdRtc/servlet/userUpload.do"; String filePath = "/sdcard/im/video"+path.substring(path.lastIndexOf("/")); RequestParams params=new RequestParams(); params.addBodyParameter("userId",userId); params.addBodyParameter(filePath.replace("/", ""), new File(filePath)); UploadUtil load = new UploadUtil(); load.uploadMethod(params, uploadHost); } @SuppressLint("SdCardPath")@Override public void surfaceCreated(SurfaceHolder arg0) {//必须在surface创建后才能初始化MediaPlayer,否则不会显示图像 player= new MediaPlayer(); player.setAudioStreamType(AudioManager.STREAM_MUSIC); player.setDisplay(surfaceHolder); //设置显示视频显示在SurfaceView上 try { // 新建Bundle对象 path = this.getIntent().getStringExtra("path"); player.setDataSource("/sdcard/im/video"+path.substring(path.lastIndexOf("/"))); player.prepare(); } catch (Exception e) { e.printStackTrace(); } } @Override public void surfaceDestroyed(SurfaceHolder arg0) { } @Override protected void onDestroy() { super.onDestroy(); if(player.isPlaying()){ player.stop(); } player.release(); //Activity销毁时停止播放,释放资源。不做这个操作,即使退出还是能听到视频播放的声音 }}
工程用到的lib: xUtils-2.6.14.jar 代码git路径: 点击打开链接 需要的图片:bg_movie_add_shoot.png
更多相关文章
- android openGl录制音视频
- Android 获取系统时间的三种方式
- android 视频图片混合轮播实现
- Android 滚动时间选择
- Android 调用系统摄像头拍照保存并且更新到图库
- 【Android】对话框之日期和时间对话框
- Android 获取时间戳 和时间戳转日期
- Android支持播放网络上的视频
- cocos js android 添加视频循环播放