播放視頻文件其實並不比播放音頻文件復雜,主要是使用 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