android surfaceView+mediaPlayer 自定義視訊播放器
阿新 • • 發佈:2019-01-26
Android 視訊播放一般分為兩種(其實3種,還一種是調Android自帶播放器):
1.使用Android自帶View VideoView ,其中一些功能,已經都給實現好了(此view一般不滿足自己的需求,比如:介面啊,控制啊,以及可能還有手勢控制音量,手勢控制亮度等等,那麼就會選擇第二種方式)
2.surfaceView+mediaPlayer 自定義播放器(這個可以製作自己想要任何功能以及介面的播放器,當然前提得自己實現)
主要就是
surfaceView:介面輸出
mediaPlayer :媒體控制 :包括音量,暫停,播放,停止等等,還有視訊各種狀態的監聽等等。
本片博文不對surfaceView和mediaPlayer 這兩個類細解,如果你不瞭解這兩個類的話,看本博文之前應該先去好好了解一下,不然很難看懂,在這裡就不說了,直接看效果。
貼出程式碼:
package com.lcworld.videodemo; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.media.AudioManager; import android.media.MediaPlayer; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.provider.Settings; import android.support.v7.app.AppCompatActivity; import android.util.DisplayMetrics; import android.util.Log; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.SeekBar; import android.widget.TextView; import java.io.IOException; import java.text.SimpleDateFormat; /** * 建立:dongshuaijun * 日期:2016/7/1 * 註釋:視屏播放 */ public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback, View.OnClickListener , SeekBar.OnSeekBarChangeListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener, MediaPlayer.OnBufferingUpdateListener, MediaPlayer.OnPreparedListener, MediaPlayer.OnSeekCompleteListener { //surfaceView private SurfaceView surfaceView; //視訊最外層layout private RelativeLayout videoLayout; //控制檯layout private LinearLayout controlLayout; //播放、全屏button private ImageButton playBtn, screenBtn; //進度條 private SeekBar seekBar; //載入視訊進度progressBar private ProgressBar progressBar; //當前時間,總時間 private TextView currTime, countTime; //surface holder private SurfaceHolder mHolder; //媒體控制 mediaPlayer private MediaPlayer mediaPlayer; //是否全屏 private boolean isFullScreen = false; //是否正在播放 private boolean isPlay = false; //控制檯是否顯示 private boolean isControl = false; //是否正在拖動seekBar private boolean isSetProgress = false; //是否播放完成 private boolean isPlayCom = false; //是否是第一次載入視訊 private boolean isFirstLoadVideo = true; //是否銷燬activity private boolean isOnDestroy = false; //是否可見 private boolean isPause = false; //媒體音量管理 private AudioManager audioManager; //點選縱座標 private float dY = 0; //點選橫座標 private float dX = 0; //擡起縱座標 private float uY = 0; //擡起橫座標 private float uX = 0; //螢幕當前亮度 private float f = 0; //手機當前亮度模式 0 1 private int countLight; //系統當前亮度 1-255 private int currLight; private static final int HIDE_CONTROL_LAYOUT = -1; //這個地址是我抓的某平臺的,我發現這個地址是變化的,所以有可能不能使用,如果不能播放,換個正常的就可以運行了,不要用模擬器執行 private static final String VIDEO_URL = "http://vod.hcs.cmvideo.cn:8088/699056/20160330/16/2206381358/88401385/gx0308wx07sn_54.mp4.m3u8?msisdn=452046967&sid=2206381358×tamp=20160702163222&Channel_ID=305300090030000&preview=1&playseek=000000-001000&encrypt=61b1958423d270fb55606bb955f62d3a"; private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == HIDE_CONTROL_LAYOUT) { refreshControlLayout(); } else { currTime.setText(formatTime(msg.what)); seekBar.setProgress(msg.what); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); initVideoSize(); initSurface(); setListener(); } private void initView() { surfaceView = (SurfaceView) findViewById(R.id.surface_view); videoLayout = (RelativeLayout) findViewById(R.id.video_layout); controlLayout = (LinearLayout) findViewById(R.id.control_layout); playBtn = (ImageButton) findViewById(R.id.playBtn); screenBtn = (ImageButton) findViewById(R.id.screenBtn); seekBar = (SeekBar) findViewById(R.id.seekBar); progressBar = (ProgressBar) findViewById(R.id.load_bar); currTime = (TextView) findViewById(R.id.curr_time); countTime = (TextView) findViewById(R.id.count_time); mHolder = surfaceView.getHolder(); mediaPlayer = new MediaPlayer(); audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); initScreenLight(); refreshControlLayout(); } //初始化螢幕亮度 private void initScreenLight() { try { //獲取亮度模式 0:手動 1:自動 countLight = Settings.System.getInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE); //設定手動設定 Settings.System.putInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL); //獲取螢幕亮度,獲取失敗則返回255 currLight = android.provider.Settings.System.getInt(getContentResolver(), android.provider.Settings.System.SCREEN_BRIGHTNESS, 255); f = currLight / 255f; } catch (Settings.SettingNotFoundException e) { e.printStackTrace(); } } //重新整理控制檯 顯示則隱藏 隱藏則顯示 並5S之後隱藏 private void refreshControlLayout() { if (isControl) { controlLayout.setVisibility(View.INVISIBLE); isControl = false; } else { controlLayout.setVisibility(View.VISIBLE); isControl = true; handler.removeMessages(HIDE_CONTROL_LAYOUT); handler.sendEmptyMessageDelayed(HIDE_CONTROL_LAYOUT, 5000); } } private void setListener() { playBtn.setOnClickListener(this); screenBtn.setOnClickListener(this); seekBar.setOnSeekBarChangeListener(this); mediaPlayer.setOnCompletionListener(this); mediaPlayer.setOnErrorListener(this); mediaPlayer.setOnBufferingUpdateListener(this); mediaPlayer.setOnPreparedListener(this); mediaPlayer.setOnSeekCompleteListener(this); surfaceView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: dX = event.getX(); dY = event.getY(); refreshControlLayout(); break; case MotionEvent.ACTION_UP: break; case MotionEvent.ACTION_MOVE: if (isFullScreen) { uY = event.getY(); if (dX > getWidth() / 2) {//聲音控制 if (Math.abs(uY - dY) > 25) setVolume(uY - dY); } else if (dX <= getWidth() / 2) {//亮度控制 setLight(dY - uY); } } break; } return true; } }); } //手勢調節音量 private void setVolume(float vol) { if (vol < 0) {//增大音量 audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_RAISE, AudioManager.FX_FOCUS_NAVIGATION_UP); } else if (vol > 0) {//降低音量 audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_LOWER, AudioManager.FX_FOCUS_NAVIGATION_UP); } } /** * 手勢設定螢幕亮度 * 設定當前的螢幕亮度值,及時生效 0.004-1 * 該方法僅對當前應用螢幕亮度生效 */ private void setLight(float vol) { Window localWindow = getWindow(); WindowManager.LayoutParams localLayoutParams = localWindow.getAttributes(); f += vol / getWidth(); if (f > 1) { f = 1f; } else if (f <= 0) { f = 0.004f; } localLayoutParams.screenBrightness = f; localWindow.setAttributes(localLayoutParams); } //初始化surfaceView private void initSurface() { //設定回撥引數 mHolder.addCallback(this); //設定SurfaceView自己不管理的緩衝區 mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); //顯示的解析度,不設定為視訊預設 // mHolder.setFixedSize(320, 220); } private void playUrl(String url) { try { //使mediaPlayer重新進入ide狀態 mediaPlayer.reset(); //設定媒體型別 mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); //將影像輸出到surfaceView mediaPlayer.setDisplay(mHolder); //設定 視訊資源 可以是本地視訊 也可是網路資源 // mediaPlayer.setDataSource("/storage/sdcard1/DCIM/Camera/VID_20160629_164144.mp4"); mediaPlayer.setDataSource(url); //同步準備 // mediaPlayer.prepare(); //因為是網路視訊 這裡用非同步準備 mediaPlayer.prepareAsync(); } catch (IOException e) { e.printStackTrace(); } } //初始化視訊顯示的大小 private void initVideoSize() { RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); params.height = getWidth() / 10 * 6; surfaceView.setLayoutParams(params); } //surfaceView建立完成 @Override public void surfaceCreated(SurfaceHolder holder) { Log.e("TAG", "surfaceCreated"); //等surfaceView建立完成再開始播放視訊 if (!isPause) { playUrl(VIDEO_URL); } else { isPause = false; mediaPlayer.setDisplay(holder); if (isPlay) mediaPlayer.start(); } } //surfaceView改變 @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.e("TAG", "surfaceChanged"); } //surfaceView銷燬 @Override public void surfaceDestroyed(SurfaceHolder holder) { Log.e("TAG", "surfaceDestroyed"); } @Override public void onClick(View v) { isControl = false; refreshControlLayout(); if (isFirstLoadVideo) { return; } switch (v.getId()) { case R.id.playBtn: if (mediaPlayer.isPlaying()) { mediaPlayer.pause(); isPlay = false; playBtn.setBackgroundResource(R.mipmap.play); } else if (isPlayCom) { mediaPlayer.seekTo(0); isPlay = true; isPlayCom = false; playBtn.setBackgroundResource(R.mipmap.pause); } else { mediaPlayer.start(); isPlay = true; playBtn.setBackgroundResource(R.mipmap.pause); } break; case R.id.screenBtn: if (isFullScreen) { smallScreen(); screenBtn.setBackgroundResource(R.mipmap.large_screen); } else { fullScreen(); screenBtn.setBackgroundResource(R.mipmap.small_screen); } break; } } //橫豎屏切換 @Override public void onConfigurationChanged(Configuration newConfig) { if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { Log.e("TAG", "當前螢幕為橫屏"); isFullScreen = true; fullScreen(); screenBtn.setBackgroundResource(R.mipmap.small_screen); } else { Log.e("TAG", "當前螢幕為豎屏"); isFullScreen = false; smallScreen(); screenBtn.setBackgroundResource(R.mipmap.large_screen); } super.onConfigurationChanged(newConfig); } //全屏 private void fullScreen() { getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);//設定成全屏模式 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);//強制為橫屏 showFullSurface(); } //豎屏 private void smallScreen() { getWindow().setFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);//強制為豎屏 showSmallSurface(); } private void showFullSurface() { RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); surfaceView.setLayoutParams(params); } private void showSmallSurface() { RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); params.height = getWidth() / 10 * 6; surfaceView.setLayoutParams(params); } //進度改變 @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { currTime.setText(formatTime(seekBar.getProgress())); if (isSetProgress) { Log.e("TAG", "onProgressChanged:refreshControlLayout"); isControl = false; refreshControlLayout(); } } //開始拖動 @Override public void onStartTrackingTouch(SeekBar seekBar) { currTime.setText(formatTime(seekBar.getProgress())); isSetProgress = true; isControl = false; refreshControlLayout(); } //停止拖動 @Override public void onStopTrackingTouch(SeekBar seekBar) { isSetProgress = false; isControl = false; refreshControlLayout(); if (isFirstLoadVideo) { return; } mediaPlayer.seekTo(seekBar.getProgress()); currTime.setText(formatTime(seekBar.getProgress())); } public int getWidth() { WindowManager manager = getWindowManager(); DisplayMetrics outMetrics = new DisplayMetrics(); manager.getDefaultDisplay().getMetrics(outMetrics); return outMetrics.widthPixels; } //更新進度 private void updateSeekBar() { new Thread(new Runnable() { @Override public void run() { while (!isOnDestroy) { //結束執行緒標示 if (isPlay && !isPause) { try { Message message = new Message(); message.what = mediaPlayer.getCurrentPosition(); handler.sendMessage(message); Log.e("TAG", "while"); Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } } } } }).start(); } //播放完成 @Override public void onCompletion(MediaPlayer mp) { // Log.e("TAG", "播放完成"); playBtn.setBackgroundResource(R.mipmap.play); isPlay = false; isPlayCom = true; isControl = false; Message message = new Message(); message.what = mediaPlayer.getDuration(); handler.sendMessage(message); refreshControlLayout(); } //播放出錯 @Override public boolean onError(MediaPlayer mp, int what, int extra) { isPlay = false; return false; } private String formatTime(long time) { SimpleDateFormat format = new SimpleDateFormat("mm:ss"); return format.format(time); } @Override public void onBufferingUpdate(MediaPlayer mp, int percent) { Log.e("TAG", "onBufferingUpdate" + ",percent:" + percent); } //準備完成 @Override public void onPrepared(MediaPlayer mp) { //設定最大進度 seekBar.setMax(mediaPlayer.getDuration()); //設定按鈕背景圖片 playBtn.setBackgroundResource(R.mipmap.pause); //設定視訊最大時間 countTime.setText(formatTime(mediaPlayer.getDuration())); //隱藏載入進度條 progressBar.setVisibility(View.INVISIBLE); //開始播放 mediaPlayer.start(); //更改播放狀態 isPlay = true; //更改狀態 if (isFirstLoadVideo) isFirstLoadVideo = false; //開啟執行緒更新進度 updateSeekBar(); } @Override protected void onDestroy() { Log.e("TAG", "onDestroy"); isOnDestroy = true; if (mediaPlayer.isPlaying()) { mediaPlayer.stop(); isPlay = false; } mediaPlayer.release(); super.onDestroy(); } //seekTo()是非同步的方法 在此監聽是否執行完畢 @Override public void onSeekComplete(MediaPlayer mp) { Log.e("TAG", "onSeekComplete"); if (!isPlay) { mediaPlayer.pause(); } else { mediaPlayer.start(); } } //監聽返回鍵 如果是全屏狀態則返回豎屏 否則直接返回 @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (isFullScreen) { smallScreen(); return false; } return super.onKeyDown(keyCode, event); } @Override protected void onPause() { Log.e("TAG", "onPause"); isPause = true; if (mediaPlayer.isPlaying()) { mediaPlayer.pause(); } super.onPause(); } @Override protected void onResume() { super.onResume(); if (isPause && isPlay && mHolder.getSurface().isValid()) { isPause = false; mediaPlayer.start(); } } }
demo下載地址:https://github.com/dongxiaoshuai/VideoDemo