1. 程式人生 > >android surfaceView+mediaPlayer 自定義視訊播放器

android surfaceView+mediaPlayer 自定義視訊播放器

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