zoukankan      html  css  js  c++  java
  • Android实现视频播放的3种实现方式

    Android提供了常见的视频的编码、解码机制。使用Android自带的MediaPlayer、MediaController等类可以很方便的实现视频播放的功能。支持的视频格式有MP4和3GP等。这些多媒体数据可以来自于Android应用的资源文件,也可以来自于外部存储器上的文件,甚至可以是来自于网络上的文件流。

    下面来说一下视频播放的几种实现方式:

    1、MediaController+VideoView实现方式
    这种方式是最简单的实现方式。VideoView继承了SurfaceView同时实现了MediaPlayerControl接口,MediaController则是安卓封装的辅助控制器,带有暂停,播放,停止,进度条等控件。通过VideoView+MediaController可以很轻松的实现视频播放、停止、快进、快退等功能。

    布局文件如下:

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".VideoViewTestActivity">
    <VideoView
    android:id="@+id/videoView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
    </android.support.constraint.ConstraintLayout>
    程序代码如下:

    public class VideoViewTestActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_video_view_test);
    VideoView videoView = (VideoView)findViewById(R.id.videoView);

    //加载指定的视频文件
    String path = Environment.getExternalStorageDirectory().getPath()+"/20180730.mp4";
    videoView.setVideoPath(path);

    //创建MediaController对象
    MediaController mediaController = new MediaController(this);

    //VideoView与MediaController建立关联
    videoView.setMediaController(mediaController);

    //让VideoView获取焦点
    videoView.requestFocus();

    }
    }
    使用此实现方式的步骤:

    加载指定的视频文件
    建立VideoView和MediaController之间的关联,这样就不需要自己去控制视频的播放、暂停等。让MediaController控制即可。
    VideoView获取焦点。
    实现效果图如下:

    界面中的快退、播放、快进、时间、进度条等是由MediaController提供的。

    2、MediaPlayer+SurfaceView+自定义控制器
    虽然VideoView的实现方式很简单,但是由于是自带的封装好的类,所以无论是播放器的大小、位置以及控制都不受我们控制。

    这种实现方式步骤如下:

    创建MediaPlayer对象,并让它加载指定的视频文件。可以是应用的资源文件、本地文件路径、或者URL。
    在界面布局文件中定义SurfaceView组件,并为SurfaceView的SurfaceHolder添加Callback监听器。
    调用MediaPlayer对象的setDisplay(SurfaceHolder sh)将所播放的视频图像输出到指定的SurfaceView组件。
    调用MediaPlayer对象的prepareAsync()或prepare()方法装载流媒体文件
    调用MediaPlayer对象的start()、stop()和pause()方法来控制视频的播放。
    在实现第二步之前需要先给surfaceHolder设置一个callback,callback的3个回调函数如下:

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

    }
    布局文件如下:

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".PlayVideoActivity">

    <RelativeLayout
    android:id="@+id/root_rl"
    android:layout_width="match_parent"
    android:layout_height="400dp"
    android:background="#000000">

    <SurfaceView
    android:id="@+id/surfaceView"
    android:layout_width="match_parent"
    android:layout_height="400dp" />
    <ImageView
    android:id="@+id/playOrPause"
    android:layout_width="60dp"
    android:layout_height="60dp"
    android:layout_centerInParent="true"
    android:src="@android:drawable/ic_media_play"/>
    <LinearLayout
    android:id="@+id/control_ll"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:background="#005500"
    android:orientation="vertical">

    <RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginLeft="10dp"
    android:layout_marginRight="10dp"
    android:orientation="horizontal"
    android:paddingBottom="5dp">

    <TextView
    android:id="@+id/tv_start_time"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentLeft="true"
    android:layout_marginLeft="30dp"
    android:text="00.00"
    android:textColor="#ffffff"/>
    <TextView
    android:id="@+id/tv_separate_time"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_toRightOf="@+id/tv_start_time"
    android:layout_marginLeft="1dp"
    android:text="/"
    android:textColor="#ffffff"/>
    <TextView
    android:id="@+id/tv_end_time"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_toRightOf="@+id/tv_separate_time"
    android:layout_marginLeft="1dp"
    android:text="00.00"
    android:textColor="#ffffff"/>
    <ImageView
    android:id="@+id/tv_backward"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_below="@+id/tv_start_time"
    android:layout_alignParentLeft="true"
    android:layout_marginLeft="1dp"
    android:src="@android:drawable/ic_media_rew"/>

    <SeekBar
    android:id="@+id/tv_progess"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_toRightOf="@+id/tv_backward"
    android:layout_toLeftOf="@+id/tv_forward"
    android:layout_below="@+id/tv_start_time"/>

    <ImageView
    android:id="@+id/tv_forward"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_below="@+id/tv_start_time"
    android:layout_alignParentRight="true"
    android:layout_marginRight="1dp"
    android:src="@android:drawable/ic_media_ff"/>

    </RelativeLayout>

    </LinearLayout>
    </RelativeLayout>

    </android.support.constraint.ConstraintLayout>
    程序代码如下:

    public class PlayVideoActivity extends AppCompatActivity implements SurfaceHolder.Callback,
    MediaPlayer.OnPreparedListener,
    MediaPlayer.OnCompletionListener,
    MediaPlayer.OnErrorListener,
    MediaPlayer.OnInfoListener, View.OnClickListener,
    MediaPlayer.OnSeekCompleteListener,
    MediaPlayer.OnVideoSizeChangedListener,
    SeekBar.OnSeekBarChangeListener,
    {

    private ImageView playOrPauseIv;
    private SurfaceView videoSuf;
    private MediaPlayer mPlayer;
    private SeekBar mSeekBar;
    private String path;
    private RelativeLayout rootViewRl;
    private LinearLayout controlLl;
    private TextView startTime, endTime;
    private ImageView forwardButton, backwardButton;
    private boolean isShow = false;

    public static final int UPDATE_TIME = 0x0001;
    public static final int HIDE_CONTROL = 0x0002;

    private Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
    switch (msg.what) {
    case UPDATE_TIME:
    updateTime();
    mHandler.sendEmptyMessageDelayed(UPDATE_TIME, 500);
    break;
    case HIDE_CONTROL:
    hideControl();
    break;
    }
    }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_play_video);
    initViews();
    initData();
    initSurfaceView();
    initPlayer();
    initEvent();
    }
    private void initData() {
    path = Environment.getExternalStorageDirectory().getPath() + "/20180730.mp4";//这里写上你的视频地址
    }

    private void initEvent() {
    playOrPauseIv.setOnClickListener(this);
    rootViewRl.setOnClickListener(this);
    rootViewRl.setOnTouchListener(this);
    forwardButton.setOnClickListener(this);
    backwardButton.setOnClickListener(this);
    mSeekBar.setOnSeekBarChangeListener(this);
    }
    private void initSurfaceView() {
    videoSuf = (SurfaceView) findViewById(R.id.surfaceView);
    videoSuf.setZOrderOnTop(false);
    videoSuf.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    videoSuf.getHolder().addCallback(this);
    }

    private void initPlayer() {
    mPlayer = new MediaPlayer();
    mPlayer.setOnCompletionListener(this);
    mPlayer.setOnErrorListener(this);
    mPlayer.setOnInfoListener(this);
    mPlayer.setOnPreparedListener(this);
    mPlayer.setOnSeekCompleteListener(this);
    mPlayer.setOnVideoSizeChangedListener(this);
    try {
    //使用手机本地视频
    mPlayer.setDataSource(path);
    } catch (Exception e) {
    e.printStackTrace();
    }
    }

    private void initViews() {
    playOrPauseIv = (ImageView) findViewById(R.id.playOrPause);
    startTime = (TextView) findViewById(R.id.tv_start_time);
    endTime = (TextView) findViewById(R.id.tv_end_time);
    mSeekBar = (SeekBar) findViewById(R.id.tv_progess);
    rootViewRl = (RelativeLayout) findViewById(R.id.root_rl);
    controlLl = (LinearLayout) findViewById(R.id.control_ll);
    forwardButton = (ImageView) findViewById(R.id.tv_forward);
    backwardButton = (ImageView) findViewById(R.id.tv_backward);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
    mPlayer.setDisplay(holder);
    mPlayer.prepareAsync();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

    }
    @Override
    public void onPrepared(MediaPlayer mp) {
    startTime.setText(FormatTimeUtil.formatLongToTimeStr(mp.getCurrentPosition()));
    endTime.setText(FormatTimeUtil.formatLongToTimeStr(mp.getDuration()));
    mSeekBar.setMax(mp.getDuration());
    mSeekBar.setProgress(mp.getCurrentPosition());
    }
    @Override
    public void onCompletion(MediaPlayer mp) {

    }
    @Override
    public boolean onError(MediaPlayer mp, int what, int extra) {
    return false;
    }

    @Override
    public boolean onInfo(MediaPlayer mp, int what, int extra) {
    return false;
    }
    private void play() {
    if (mPlayer == null) {
    return;
    }
    Log.i("playPath", path);
    if (mPlayer.isPlaying()) {
    mPlayer.pause();
    mHandler.removeMessages(UPDATE_TIME);
    mHandler.removeMessages(HIDE_CONTROL);
    playOrPauseIv.setVisibility(View.VISIBLE);
    playOrPauseIv.setImageResource(android.R.drawable.ic_media_play);
    } else {
    mPlayer.start();
    mHandler.sendEmptyMessageDelayed(UPDATE_TIME, 500);
    mHandler.sendEmptyMessageDelayed(HIDE_CONTROL, 5000);
    playOrPauseIv.setVisibility(View.INVISIBLE);
    playOrPauseIv.setImageResource(android.R.drawable.ic_media_pause);
    }
    }
    @Override
    public void onSeekComplete(MediaPlayer mp) {
    //TODO
    }

    @Override
    public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {

    }
    @Override
    public void onClick(View v) {
    switch (v.getId()) {
    case R.id.tv_backward:
    backWard();
    break;
    case R.id.tv_forward:
    forWard();
    break;
    case R.id.playOrPause:
    play();
    break;
    case R.id.root_rl:
    showControl();
    break;
    }
    }
    /**
    * 更新播放时间
    */
    private void updateTime() {

    startTime.setText(FormatTimeUtil.formatLongToTimeStr(
    mPlayer.getCurrentPosition()));
    mSeekBar.setProgress(mPlayer.getCurrentPosition());
    }

    /**
    * 隐藏进度条
    */
    private void hideControl() {
    isShow = false;
    mHandler.removeMessages(UPDATE_TIME);
    controlLl.animate().setDuration(300).translationY(controlLl.getHeight());
    }
    /**
    * 显示进度条
    */
    private void showControl() {
    if (isShow) {
    play();
    }
    isShow = true;
    mHandler.removeMessages(HIDE_CONTROL);
    mHandler.sendEmptyMessage(UPDATE_TIME);
    mHandler.sendEmptyMessageDelayed(HIDE_CONTROL, 5000);
    controlLl.animate().setDuration(300).translationY(0);
    }
    /**
    * 设置快进10秒方法
    */
    private void forWard(){
    if(mPlayer != null){
    int position = mPlayer.getCurrentPosition();
    mPlayer.seekTo(position + 10000);
    }
    }

    /**
    * 设置快退10秒的方法
    */
    public void backWard(){
    if(mPlayer != null){
    int position = mPlayer.getCurrentPosition();
    if(position > 10000){
    position-=10000;
    }else{
    position = 0;
    }
    mPlayer.seekTo(position);
    }
    }

    //OnSeekBarChangeListener
    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean b) {
    if(mPlayer != null && b){
    mPlayer.seekTo(progress);
    }
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {

    }
    }
    注意事项:MediaPlayer有prepare和prepareAsync两种方法。这两种方法的区别是:prepare方法是将资源同步缓存到内存中,一般加载本地较小的资源可以用这个,如果是较大的资源或者网络资源建议使用prepareAsync方法,异步加载。

    实现效果如下所示:

    3、MediaPlayer+SurfaceView+MediaController
    第二种实现方式使用的是自定义控件,MediaPlayer+SurfaceView也可以使用系统自带的MediaController控制器。

    使用这个方式实现,布局文件只需一个SurfaceView即可,其他的控件都交给MediaController控制器,布局文件如下:

    <?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"
    tools:context=".MediaControllerTestActivity"
    android:id="@+id/root_ll">
    <SurfaceView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/controll_surfaceView"/>
    </LinearLayout>
    程序代码如下:

    public class MediaControllerTestActivity extends Activity implements
    MediaController.MediaPlayerControl,
    MediaPlayer.OnBufferingUpdateListener,
    SurfaceHolder.Callback{
    private MediaPlayer mediaPlayer;
    private MediaController controller;
    private int bufferPercentage = 0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_media_controller_test);
    mediaPlayer = new MediaPlayer();
    controller = new MediaController(this);
    controller.setAnchorView(findViewById(R.id.root_ll));
    initSurfaceView();
    }

    private void initSurfaceView() {
    SurfaceView videoSuf = (SurfaceView) findViewById(R.id.controll_surfaceView);
    videoSuf.setZOrderOnTop(false);
    videoSuf.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    videoSuf.getHolder().addCallback(this);
    }
    @Override
    protected void onResume() {
    super.onResume();
    try {
    String path = Environment.getExternalStorageDirectory().getPath() + "/20180730.mp4";
    mediaPlayer.setDataSource(path);
    mediaPlayer.setOnBufferingUpdateListener(this);
    //mediaPlayer.prepare();

    controller.setMediaPlayer(this);
    controller.setEnabled(true);

    }catch (IOException e){
    e.printStackTrace();
    }
    }

    @Override
    protected void onPause() {
    super.onPause();
    if (mediaPlayer.isPlaying()){
    mediaPlayer.stop();
    }
    }

    @Override
    protected void onDestroy() {
    super.onDestroy();
    if (null != mediaPlayer){
    mediaPlayer.release();
    mediaPlayer = null;
    }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
    controller.show();
    return super.onTouchEvent(event);
    }

    //MediaPlayer
    @Override
    public void onPointerCaptureChanged(boolean hasCapture) {

    }
    //MediaPlayerControl
    @Override
    public void onBufferingUpdate(MediaPlayer mediaPlayer, int i) {
    bufferPercentage = i;
    }

    @Override
    public void start() {
    if (null != mediaPlayer){
    mediaPlayer.start();
    }
    }

    @Override
    public void pause() {
    if (null != mediaPlayer){
    mediaPlayer.pause();
    }
    }

    @Override
    public int getDuration() {
    return mediaPlayer.getDuration();
    }

    @Override
    public int getCurrentPosition() {
    return mediaPlayer.getCurrentPosition();
    }

    @Override
    public void seekTo(int i) {
    mediaPlayer.seekTo(i);
    }

    @Override
    public boolean isPlaying() {
    if (mediaPlayer.isPlaying()){
    return true;
    }
    return false;
    }

    @Override
    public int getBufferPercentage() {
    return bufferPercentage;
    }

    @Override
    public boolean canPause() {
    return true;
    }

    @Override
    public boolean canSeekBackward() {
    return true;
    }

    @Override
    public boolean canSeekForward() {
    return true;
    }

    @Override
    public int getAudioSessionId() {
    return 0;
    }

    //SurfaceHolder.callback
    @Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {
    mediaPlayer.setDisplay(surfaceHolder);
    mediaPlayer.prepareAsync();
    }

    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {

    }
    }
    效果图如下:


    ————————————————
    版权声明:本文为CSDN博主「若尘风」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/liuzhi0724/article/details/81318816

  • 相关阅读:
    导入GooglePlay Services出现的一个错误
    在Linux下后台运行jar包文件
    android7.0闪退问题java.lang.SecurityException: MODE_WORLD_READABLE no longer supported
    从手机中扫描以com.xx.xxx 为前缀的apk包,使用列表的形式展现
    AS运行项目出现Error while Launching activity的错误
    Android——解决引入jar包和arr包时support.v7包的冲突
    Java知识点小记
    异步解压ZIP文件
    WP手机升级WIN10被PIN码锁定
    EXCEL 跨表比较数据
  • 原文地址:https://www.cnblogs.com/javalinux/p/14474543.html
Copyright © 2011-2022 走看看