视频播放-VideoView
播放视频文件其实并不比播放音频文件复杂,主要是使用 VideoView类来实现的。这个
类将视频的显示和控制集于一身,使得我们仅仅借助它就可以完成一个简易的视频播放器。
VideoView的用法和 MediaPlayer 也比较类似(不同点在于播放视频需要UI控件,播放音乐只需要借助MediaPlayer对象),主要有以下常用方法:
VideoView,用于播放一段视频媒体,它继承了SurfaceView,位于"android.widget.VideoView",是一个视频控件。
- int getCurrentPosition():获取当前播放的位置。
- int getDuration():获取当前播放视频的总长度。
- isPlaying():当前VideoView是否在播放视频。
- void pause():暂停
- void seekTo(int msec):从第几毫秒开始播放。
- void resume():重新播放。
- void setVideoPath(String path):以文件路径的方式设置VideoView播放的视频源。
- void setVideoURI(Uri uri):以Uri的方式设置VideoView播放的视频源,可以是网络Uri或本地Uri。
- void start():开始播放。
- void stopPlayback():停止播放。并释放资源
- setMediaController(MediaController controller):设置MediaController控制器。
- setOnCompletionListener(MediaPlayer.onCompletionListener l):监听播放完成的事件。
- setOnErrorListener(MediaPlayer.OnErrorListener l):监听播放发生错误时候的事件。
- setOnPreparedListener(MediaPlayer.OnPreparedListener l)::监听视频装载完成的事件。
关于重置见: 音频播放
有一篇利用SurfaceView实现的视频播放,具体见: 利用SurfaceView实现视频的播放
VideoView其实就是SurfaceView的子类,内部做了封装,使用起来比较方便,但是灵活性没有SufraceView高
import android.app.Activity; import android.media.MediaPlayer; import android.net.Uri; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.MediaController; import android.widget.VideoView; /** * 播放视频 */ public class MainActivity extends Activity implements View.OnClickListener { private String url = "http://qiubai-video.qiushibaike.com/VGU6K0T3CDU6N7JJ_3g.mp4"; private String url2 = "http://qiubai-video.qiushibaike.com/YXSKWQA6N838MJC4_3g.mp4"; private VideoView videoView;//播放视频的控件 private Button play; private Button pause; private Button replay; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); play = (Button) findViewById(R.id.play); pause = (Button) findViewById(R.id.pause); replay = (Button) findViewById(R.id.replay); videoView = (VideoView) findViewById(R.id.video_view); play.setOnClickListener(this); pause.setOnClickListener(this); replay.setOnClickListener(this); initVideoPath();//初始化 videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { Log.i("tag", "准备完毕,可以播放了"); } }); videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { Log.i("tag", "播放完毕"); } }); videoView.setOnErrorListener(new MediaPlayer.OnErrorListener() { @Override public boolean onError(MediaPlayer mp, int what, int extra) { Log.i("tag", "播放失败"); return false; } }); } private void initVideoPath() { /**媒体控制面版常用方法:MediaController: hide(); 隐藏MediaController; show(); 显示MediaController show(int timeout);设置MediaController显示的时间,以毫秒计算,如果设置为0则一直到调用hide()时隐藏; */ videoView.setMediaController(new MediaController(this));//这样就有滑动条了 /*File file = new File(Environment.getExternalStorageDirectory(),"movie.3gp"); videoView.setVideoPath(file.getPath()); // 指定视频文件的路径*/ videoView.setVideoURI(Uri.parse(url2));//播放网络视频 } @Override public void onClick(View v) { switch (v.getId()) { case R.id.play: if (!videoView.isPlaying()) { videoView.start(); // 开始播放 } break; case R.id.pause: if (videoView.isPlaying()) { videoView.pause(); // 暂时播放 } break; case R.id.replay: //重新指定资源开始播放 if (videoView.isPlaying()) { //videoView.resume(); // 重新播放,无效 videoView.pause();//暂停 videoView.stopPlayback();//停止播放,释放资源 videoView.setVideoURI(Uri.parse(url));//重新设置资源 videoView.start();//开始播放,可以不在准备监听中播放 } break; } } @Override protected void onDestroy() { super.onDestroy(); if (videoView != null) { videoView.suspend(); } } }
不过,为什么它的用法
和 MediaPlayer 这么相似呢?其实 VideoView 只是帮我们做了一个很好的封装而已,它的背
后仍然是使用 MediaPlayer 来对视频文件进行控制的。另外需要注意,VideoView 并不是一
个万能的视频播放工具类,它在视频格式的支持以及播放效率方面都存在着较大的不足。所
以,如果想要仅仅使用 VideoView就编写出一个功能非常强大的视频播放器是不太现实的。
但是如果只是用于播放一些游戏的片头动画,或者某个应用的视频宣传,使用 VideoView 还
是绰绰有余的。
另外也有第三方提供了视频的播放,vitamio,下载地址:https://www.vitamio.org/Download/ 将代码下载下来就行
具体使用和上面几乎是一模一样,就连方法名都没改
不过需要在清单文件中注册一个没有界面的Activity,还需要初始化
<activity android:name="io.vov.vitamio.activity.InitActivity" android:configChanges="orientation|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation" android:launchMode="singleTop" android:theme="@android:style/Theme.NoTitleBar" android:windowSoftInputMode="stateAlwaysHidden"/>
初始化:
import android.app.Activity; import android.net.Uri; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import io.vov.vitamio.MediaPlayer; import io.vov.vitamio.Vitamio; import io.vov.vitamio.widget.MediaController; /** * 播放视频 */ public class MainActivity extends Activity implements View.OnClickListener { private String url = "http://qiubai-video.qiushibaike.com/VGU6K0T3CDU6N7JJ_3g.mp4"; private String url2 = "http://qiubai-video.qiushibaike.com/YXSKWQA6N838MJC4_3g.mp4"; private io.vov.vitamio.widget.VideoView videoView;//播放视频的控件,第三方的 private Button play; private Button pause; private Button replay; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Vitamio.isInitialized(this);//初始化 setContentView(R.layout.activity_main); play = (Button) findViewById(R.id.play); pause = (Button) findViewById(R.id.pause); replay = (Button) findViewById(R.id.replay); videoView = (io.vov.vitamio.widget.VideoView) findViewById(R.id.video_view); play.setOnClickListener(this); pause.setOnClickListener(this); replay.setOnClickListener(this); initVideoPath();//初始化 videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { Log.i("tag", "准备完毕"); } }); videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { Log.i("tag", "播放结束"); } }); videoView.setOnErrorListener(new MediaPlayer.OnErrorListener() { @Override public boolean onError(MediaPlayer mp, int what, int extra) { Log.i("tag", "播放出错"); return false; } }); } private void initVideoPath() { /**媒体控制面版常用方法:MediaController: hide(); 隐藏MediaController; show(); 显示MediaController show(int timeout);设置MediaController显示的时间,以毫秒计算,如果设置为0则一直到调用hide()时隐藏; */ videoView.setMediaController(new MediaController(this));//设置控制器 /*File file = new File(Environment.getExternalStorageDirectory(),"movie.3gp"); videoView.setVideoPath(file.getPath()); // 指定视频文件的路径*/ videoView.setVideoURI(Uri.parse(url2));//播放网络视频 } @Override public void onClick(View v) { switch (v.getId()) { case R.id.play: if (!videoView.isPlaying()) { videoView.start(); // 开始播放 } break; case R.id.pause: if (videoView.isPlaying()) { videoView.pause(); // 暂时播放 } break; case R.id.replay: //重新指定资源开始播放 if (videoView.isPlaying()) { //videoView.resume(); // 重新播放,无效 videoView.pause();//暂停 videoView.stopPlayback();//停止播放,释放资源 videoView.setVideoURI(Uri.parse(url));//重新设置资源 videoView.start();//开始播放,可以不在准备监听中播放 } break; } } @Override protected void onDestroy() { super.onDestroy(); if (videoView != null) { videoView.suspend(); } } }
所需权限:
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
视频的录制:
/** * 视频认证 */ public class VideoAuthActivity extends BaseActivity implements SurfaceHolder.Callback { private static final String TAG = "VideoAuthActivity"; private SurfaceView mSurfaceview; private Button mBtnStartStop; private Button mBtnPlay; private boolean mStartedFlg = false;//是否正在录像 private boolean mIsPlay = false;//是否正在播放录像 private MediaRecorder mRecorder; private SurfaceHolder mSurfaceHolder; private ImageView mImageView; private Camera camera; private MediaPlayer mediaPlayer; private String path; private TextView textView; private int text = 0; private ProgressDialog mDialog; private android.os.Handler handler = new android.os.Handler(); private Runnable runnable = new Runnable() { @Override public void run() { text++; textView.setText(text+""); handler.postDelayed(this,1000); if(text == 10){ stopVideo(); } } }; private Button btn_auth; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_video_authentication); mSurfaceview = (SurfaceView) findViewById(R.id.surfaceview); mImageView = (ImageView) findViewById(R.id.imageview); mBtnStartStop = (Button) findViewById(R.id.btnStartStop); mBtnPlay = (Button) findViewById(R.id.btnPlayVideo); textView = (TextView)findViewById(R.id.text); mDialog = new ProgressDialog(this); mBtnStartStop.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { stopVideo(); } }); mBtnPlay.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //没有录像的时候才能播放 Log.i("tag", "路径:"+path); if(!mStartedFlg && !TextUtils.isEmpty(path)){ mIsPlay = true; mImageView.setVisibility(View.GONE); if (mediaPlayer == null) { mediaPlayer = new MediaPlayer(); } mediaPlayer.reset(); Uri uri = Uri.parse(path); mediaPlayer = MediaPlayer.create(VideoAuthActivity.this, uri); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setDisplay(mSurfaceHolder); try{ mediaPlayer.prepare(); }catch (Exception e){ e.printStackTrace(); } mediaPlayer.start(); }else{ T.showS("没有录制或者没有停止录制"); } } }); btn_auth= (Button) findViewById(R.id.btn_auth); btn_auth.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Log.i("tag", "路径:"+path); if(!TextUtils.isEmpty(path) && text>=5){ submitVideo(); }else{ updateUI(getApplicationContext(), "没有录制视频或者时间不到5秒"); } } }); SurfaceHolder holder = mSurfaceview.getHolder(); holder.addCallback(this); // setType必须设置,要不出错. holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } @Override protected void onResume() { super.onResume(); if (!mStartedFlg) { mImageView.setVisibility(View.VISIBLE); } } private void stopVideo() { if (mIsPlay) { if (mediaPlayer != null) { mIsPlay = false; mediaPlayer.stop(); mediaPlayer.reset(); mediaPlayer.release(); mediaPlayer = null; } } if (!mStartedFlg) { handler.postDelayed(runnable,1000); mImageView.setVisibility(View.GONE); if (mRecorder == null) { mRecorder = new MediaRecorder(); } camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK); if (camera != null) { camera.setDisplayOrientation(90); camera.unlock(); mRecorder.setCamera(camera); } try { // 这两项需要放在setOutputFormat之前 mRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER); mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); // Set output file format mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); // 这两项需要放在setOutputFormat之后 mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP); mRecorder.setVideoSize(640, 480); mRecorder.setVideoFrameRate(30); mRecorder.setVideoEncodingBitRate(3 * 1024 * 1024); mRecorder.setOrientationHint(90); //设置记录会话的最大持续时间(毫秒) mRecorder.setMaxDuration(20 * 1000); mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface()); path = getSDPath(); if (path != null) { File dir = new File(path + "/recordtest"); if (!dir.exists()) { dir.mkdir(); } path = dir + "/" + getDate() + ".mp4"; mRecorder.setOutputFile(path); mRecorder.prepare(); mRecorder.start(); mStartedFlg = true; mBtnStartStop.setText("停止录制"); text = 0; } } catch (Exception e) { e.printStackTrace(); } } else { //停止 if (mStartedFlg) { try { handler.removeCallbacks(runnable); mRecorder.stop(); mRecorder.reset(); mRecorder.release(); mRecorder = null; mBtnStartStop.setText("开始录制"); if (camera != null) { camera.release(); camera = null; } } catch (Exception e) { e.printStackTrace(); } } mStartedFlg = false; } } /** * 获取系统时间 * * @return */ public static String getDate() { Calendar ca = Calendar.getInstance(); int year = ca.get(Calendar.YEAR); // 获取年份 int month = ca.get(Calendar.MONTH); // 获取月份 int day = ca.get(Calendar.DATE); // 获取日 int minute = ca.get(Calendar.MINUTE); // 分 int hour = ca.get(Calendar.HOUR); // 小时 int second = ca.get(Calendar.SECOND); // 秒 String date = "" + year + (month + 1) + day + hour + minute + second; Log.d(TAG, "date:" + date); return date; } /** * 获取SD path * * @return */ public String getSDPath() { File sdDir = null; boolean sdCardExist = Environment.getExternalStorageState() .equals(android.os.Environment.MEDIA_MOUNTED); // 判断sd卡是否存在 if (sdCardExist) { sdDir = Environment.getExternalStorageDirectory();// 获取跟目录 return sdDir.toString(); } return null; } @Override public void surfaceCreated(SurfaceHolder surfaceHolder) { mSurfaceHolder = surfaceHolder; } @Override public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) { // 将holder,这个holder为开始在onCreate里面取得的holder,将它赋给mSurfaceHolder mSurfaceHolder = surfaceHolder; } @Override public void surfaceDestroyed(SurfaceHolder surfaceHolder) { mSurfaceview = null; mSurfaceHolder = null; handler.removeCallbacks(runnable); if (mRecorder != null) { mRecorder.release(); mRecorder = null; Log.d(TAG, "surfaceDestroyed release mRecorder"); } if (camera != null) { camera.release(); camera = null; } if (mediaPlayer != null){ mediaPlayer.release(); mediaPlayer = null; } } private CountDownTimer cdt=new CountDownTimer(10000,1000) { @Override public void onTick(long millisUntilFinished) { if(millisUntilFinished/1000 <5){ //5秒 允许暂停了 } } @Override public void onFinish() { //停止 T.showS("已经10秒了"); } }; @Override protected void onDestroy() { super.onDestroy(); } /** * 提交视频 */ private void submitVideo(){ mDialog.setMessage("正在提交..."); mDialog.show(); if (!CommonUtils.isNetWorkConnected()) { T.showS("请检查网络状况"); return; } String url = ConnUrls.VIDEO_AUTH; String substring = path.substring(path.lastIndexOf("/")+1); Log.i("tag", "文件名:"+substring); OkHttpUtils.post().url(url).addParams("user_id", App.userId) .addFile("img", substring, new File(path)).build() .execute(new StringCallback() { @Override public void onResponse(String response) { L.e("提交视频:", response); mDialog.dismiss(); BaseResponseBody responseBody = GsonUtil.getObject(response, BaseResponseBody.class); if (responseBody != null) { if (responseBody.errorCode == ResponseErrorCode.SUCCESS) { updateUI(getApplicationContext(), "提交成功"); finish(); } else if (responseBody.errorCode == ResponseErrorCode.DATA_NOT_FOUND) { // 数据库未找到数据 updateUI(getApplicationContext(), "提交失败"); L.e("修改用户信息:_error", responseBody.errorCode + ":数据库未找到数据"); } else if (responseBody.errorCode == ResponseErrorCode.PARAMS_ERROR) { // 输入参数错误 L.e("修改用户信息:_error", responseBody.errorCode + ":输入参数错误"); } else if (responseBody.errorCode == ResponseErrorCode.SERVER_ERROR) { // 服务器异常 L.e("修改用户信息:_error", responseBody.errorCode + ":服务器异常"); } else { // 未知异常 L.e("修改用户信息:_error", responseBody.errorCode + ":未知异常"); } } else { L.e("修改用户信息:_error", "返回数据为空"); } } @Override public void onError(Call call, Exception e) { L.e("修改用户信息:_error", "网络异常"); updateUI(getApplicationContext(), "网络异常"); } }); } }
布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/primaryBgColor" android:orientation="vertical" > <!-- 标题 --> <RelativeLayout android:layout_width="match_parent" android:layout_height="@dimen/title_dimen" android:background="@color/colorPrimary" > <ImageView android:layout_width="42dp" android:layout_height="42dp" android:layout_centerVertical="true" android:clickable="true" android:onClick="back" android:padding="12dp" android:src="@drawable/icon_back_white" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:text="视频认证" android:textColor="@color/white" android:textSize="18sp" /> <ImageView android:id="@+id/iv_home" android:layout_width="20dp" android:layout_height="20dp" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:layout_marginRight="15dp" android:clickable="true" android:onClick="toHome" android:src="@drawable/icon_home" /> </RelativeLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="200dp" android:background="#E6E6E6" > <SurfaceView android:id="@+id/surfaceview" android:layout_width="match_parent" android:layout_height="match_parent" /> <ImageView android:id="@+id/imageview" android:layout_width="40dp" android:layout_height="40dp" android:layout_centerInParent="true" android:src="@drawable/icon_video_auth" /> </RelativeLayout> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <Button android:id="@+id/btnStartStop" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:background="@color/colorPrimary" android:textColor="@color/white" android:text="开始录制" /> <Button android:id="@+id/btnPlayVideo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:background="@color/colorPrimary" android:textColor="@color/white" android:layout_toRightOf="@id/btnStartStop" android:text="播放预览" /> <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="12dp" android:layout_marginLeft="20dp" android:text="0" android:textSize="20sp" /> </RelativeLayout> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="2dp" android:gravity="center" android:text="拍摄5到10秒的真人视频" android:textSize="14sp" /> <Button android:id="@+id/btn_auth" android:layout_width="match_parent" android:layout_height="45dp" android:layout_marginLeft="15dp" android:layout_marginRight="15dp" android:layout_marginTop="40dp" android:background="@color/colorPrimary" android:text="立即认证" android:textColor="@color/white" /> </LinearLayout>
所需权限:
<uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.RECORD_AUDIO" />
效果:
使用前置摄像头与使用后置摄像头录制: 只需要在上面的基础上加上下面的判断就行,cameraPositoin做为切换的标识
if(cameraPosition==0){ //前置 camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT); }else{ //后置 camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK); } if(cameraPosition==0){ //前置 mRecorder.setOrientationHint(270);//旋转270度 }else{ //后置 mRecorder.setOrientationHint(90);//旋转90度 }
修改VideoView的宽高:
import android.content.Context; import android.util.AttributeSet; import android.widget.VideoView; /** * 修改视频的宽度与高度(占满整个屏幕) */ public class MyVideoView extends VideoView{ public MyVideoView(Context context) { super(context); } public MyVideoView(Context context, AttributeSet attrs) { super(context, attrs); } public MyVideoView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = getDefaultSize(getWidth(), widthMeasureSpec);//根据布局的高度来定 int height = getDefaultSize(getHeight(), heightMeasureSpec); setMeasuredDimension(width, height); } }
http://blog.csdn.net/qq_26440221/article/details/50946456 关于mediaController位置调整的有关问题
http://blog.csdn.net/jason_xnxm/article/details/31398939 定制VideoView,自定义MediaController