Android多媒体六:三种方式实现视频的播放

在Android中,我们有三种方式来实现视频的播放:

  • 使用其自带的播放器。指定Action为ACTION_VIEW,Data为Uri,Type为其MIME类型。
  • 使用VideoView来播放。在布局文件中使用VideoView结合MediaController来实现对其控制。
  • 使用MediaPlayer类和SurfaceView来实现,这种方式很灵活。
  • 第三方播放视频
第三方播放视频:

Vitamio 框架 使用起来和VideoView基本一样,区别在于,我们自己写的和系统自带的控件无法做到所有格式的视频都可以播放,但是这个框架基本可以实现,我用真机测试了mp4,flv,mkv,rmvb,3gp,avi这5中格式的视频,其中只有avi的只有声音没有影像,其他都可以正常播放

vitamio的library下载地址是:https://www.vitamio.org/Download/

第一种方式:系统自带的播放器
Uri uri = Uri.parse(Environment.getExternalStorageDirectory().getPath()+"/Test_Movie.mp4");
//调用系统自带的播放器
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "video/mp4");
startActivity(intent);
第二种方式:videoview播放
Uri uri = Uri.parse(Environment.getExternalStorageDirectory().getPath()+"/Test_Movie.mp4");
VideoView videoView = this.findViewById(R.id.video_view);
videoView.setMediaController(new MediaController(this));
videoView.setVideoURI(uri);
videoView.start();
videoView.requestFocus();
第三种方式:MediaPlayer和SurfaceView

代码实现:

package com.example.administrator;

import java.io.File;

import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.Toast;

public class MainActivity extends Activity {
    private final String TAG = "main";
    private SurfaceView sv;
    private Button btn_play, btn_pause, btn_replay, btn_stop;
    private MediaPlayer mediaPlayer;
    private SeekBar seekBar;
    private int currentPosition = 0;
    private boolean isPlaying;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    seekBar = findViewById(R.id.seekBar);
    sv = findViewById(R.id.sv);

    btn_play = findViewById(R.id.btn_play);
    btn_pause = findViewById(R.id.btn_pause);
    btn_replay = findViewById(R.id.btn_replay);
    btn_stop = findViewById(R.id.btn_stop);

    btn_play.setOnClickListener(click);
    btn_pause.setOnClickListener(click);
    btn_replay.setOnClickListener(click);
    btn_stop.setOnClickListener(click);

    // 为SurfaceHolder添加回调
    sv.getHolder().addCallback(callback);

    // 4.0版本之下需要设置的属性
    // 设置Surface不维护自己的缓冲区,而是等待屏幕的渲染引擎将内容推送到界面
    // sv.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

    // 为进度条添加进度更改事件
    seekBar.setOnSeekBarChangeListener(change);
}

private Callback callback = new Callback() {
    // SurfaceHolder被修改的时候回调
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        Log.i(TAG, "SurfaceHolder 被销毁");
        // 销毁SurfaceHolder的时候记录当前的播放位置并停止播放
        if (mediaPlayer != null && mediaPlayer.isPlaying()) {
            currentPosition = mediaPlayer.getCurrentPosition();
            mediaPlayer.stop();
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        Log.i(TAG, "SurfaceHolder 被创建");
        if (currentPosition > 0) {
            // 创建SurfaceHolder的时候,如果存在上次播放的位置,则按照上次播放位置进行播放
            play(currentPosition);
            currentPosition = 0;
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        Log.i(TAG, "SurfaceHolder 大小被改变");
    }

};

private OnSeekBarChangeListener change = new OnSeekBarChangeListener() {
    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
        // 当进度条停止修改的时候触发
        // 取得当前进度条的刻度
        int progress = seekBar.getProgress();
        if (mediaPlayer != null && mediaPlayer.isPlaying()) {
            // 设置当前播放的位置
            mediaPlayer.seekTo(progress);
        }
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {

    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

    }
};

private View.OnClickListener click = new View.OnClickListener() {

    @Override
    public void onClick(View v) {

        switch (v.getId()) {
            case R.id.btn_play:
                play(0);
                break;
            case R.id.btn_pause:
                pause();
                break;
            case R.id.btn_replay:
                replay();
                break;
            case R.id.btn_stop:
                stop();
                break;
            default:
                break;
        }
    }
};


/*
 * 停止播放
 */
protected void stop() {
    if (mediaPlayer != null && mediaPlayer.isPlaying()) {
        mediaPlayer.stop();
        mediaPlayer.release();
        mediaPlayer = null;
        btn_play.setEnabled(true);
        isPlaying = false;
    }
}

/**
 * 开始播放
 *
 * @param msec 播放初始位置
 */
protected void play(final int msec) {
    // 获取视频文件地址
    //String path = et_path.getText().toString().trim();
    String path = "/storage/emulated/0/Test_Movie.mp4";
    File file = new File(path);
    if (!file.exists()) {
        Toast.makeText(this, "视频文件路径错误", Toast.LENGTH_SHORT).show();
        return;
    }
    try {
        mediaPlayer = new MediaPlayer();
        mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        // 设置播放的视频源
        mediaPlayer.setDataSource(file.getAbsolutePath());
        // 设置显示视频的SurfaceHolder
        mediaPlayer.setDisplay(sv.getHolder());
        Log.i(TAG, "开始装载");
        mediaPlayer.prepareAsync();
        mediaPlayer.setOnPreparedListener(new OnPreparedListener() {
            @Override
            public void onPrepared(MediaPlayer mp) {
                Log.i(TAG, "装载完成");
                mediaPlayer.start();
                // 按照初始位置播放
                mediaPlayer.seekTo(msec);
                // 设置进度条的最大进度为视频流的最大播放时长
                seekBar.setMax(mediaPlayer.getDuration());
                // 开始线程,更新进度条的刻度
                new Thread() {
                    @Override
                    public void run() {
                        try {
                            isPlaying = true;
                            while (isPlaying) {
                                int current = mediaPlayer.getCurrentPosition();
                                seekBar.setProgress(current);
                                sleep(500);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }.start();
                btn_play.setEnabled(false);
            }
        });
        mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                // 在播放完毕被回调
                btn_play.setEnabled(true);
            }
        });
        mediaPlayer.setOnErrorListener(new OnErrorListener() {
            @Override
            public boolean onError(MediaPlayer mp, int what, int extra) {
                // 发生错误重新播放
                play(0);
                isPlaying = false;
                return false;
            }
        });
    } catch (Exception e) {
        e.printStackTrace();
    }
}
/**
 * 重新开始播放
 */
protected void replay() {
    if (mediaPlayer != null && mediaPlayer.isPlaying()) {
        mediaPlayer.seekTo(0);
        Toast.makeText(this, "重新播放", Toast.LENGTH_SHORT).show();
        btn_pause.setText("暂停");
        return;
    }
    isPlaying = false;
    play(0);
}
/**
 * 暂停或继续
 */
protected void pause() {
    if (btn_pause.getText().toString().trim().equals("继续")) {
        btn_pause.setText("暂停");
        mediaPlayer.start();
        Toast.makeText(this, "继续播放", Toast.LENGTH_SHORT).show();
        return;
    }
    if (mediaPlayer != null && mediaPlayer.isPlaying()) {
        mediaPlayer.pause();
        btn_pause.setText("继续");
        Toast.makeText(this, "暂停播放", Toast.LENGTH_SHORT).show();
    }
}}
获取视频缩略图

Android得到视频缩略图,可以通过接口类 MediaMetadataRetriever 来实现
其中函数getFrameAtTime()有其他重载函数,该函数会随机选择一帧抓取,
如果想要指定具体时间的缩略图,可以用函数getFrameAtTime(long timeUs), getFrameAtTime(long timeUs, int option)

代码实现:

package com.example.administrator;

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Main2Activity extends Activity {

private ListView lv;
private Map pathMap = new HashMap<>();

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main2);
    lv = findViewById(R.id.lv);

    //检查当前权限(若没有该权限,值为-1;若有该权限,值为0)
    int hasReadExternalStoragePermission = ContextCompat.checkSelfPermission(getApplication(), Manifest.permission.READ_EXTERNAL_STORAGE);
    if (hasReadExternalStoragePermission == PackageManager.PERMISSION_GRANTED) {
        setData();
    } else {
        //若没有授权,会弹出一个对话框(这个对话框是系统的,开发者不能自己定制),用户选择是否授权应用使用系统权限
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
    }
}

private void setData() {
    MyAdapter adapter = new MyAdapter();
    lv.setAdapter(adapter);
    lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView parent, View view, int position, long id) {
            Uri uri = Uri.parse(pathMap.get(position));
            //调用系统自带的播放器
            Intent intent = new Intent(Intent.ACTION_VIEW);
            Log.v("URI:::::::::", uri.toString());
            intent.setDataAndType(uri, "video/mp4");
            startActivity(intent);
        }
    });
}

//用户选择是否同意授权后,会回调这个方法
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    if (requestCode == 1) {
        if (permissions[0].equals(Manifest.permission.READ_EXTERNAL_STORAGE) && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            //用户同意授权,执行读取文件的代码
           setData();
        } else {
            //若用户不同意授权,直接暴力退出应用。
            // 当然,这里也可以有比较温柔的操作。
            finish();
        }
    }
}

public List getList(Context context) {
    List sysVideoList = new ArrayList<>();
    // MediaStore.Video.Thumbnails.DATA:视频缩略图的文件路径
    String[] thumbColumns = {MediaStore.Video.Thumbnails.DATA, MediaStore.Video.Thumbnails.VIDEO_ID};
    // 视频其他信息的查询条件
    String[] mediaColumns = {MediaStore.Video.Media._ID, MediaStore.Video.Media.DATA, MediaStore.Video.Media.DURATION};
    Cursor cursor = context.getContentResolver().query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, mediaColumns, null, null, null);
    if (cursor == null) {
        return sysVideoList;
    }
    if (cursor.moveToFirst()) {
        do {
            EntityVideo info = new EntityVideo();
            int id = cursor.getInt(cursor.getColumnIndex(MediaStore.Video.Media._ID));
            Cursor thumbCursor = context.getContentResolver().query(MediaStore.Video.Thumbnails.EXTERNAL_CONTENT_URI, thumbColumns, MediaStore.Video.Thumbnails.VIDEO_ID + "=" + id, null, null);
            if (thumbCursor.moveToFirst()) {
                info.setThumbPath(thumbCursor.getString(thumbCursor.getColumnIndex(MediaStore.Video.Thumbnails.DATA)));
            }
            info.setPath(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA)));
            info.setDuration(cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION)));
            sysVideoList.add(info);
        } while (cursor.moveToNext());
    }
    return sysVideoList;
}

private class MyAdapter extends BaseAdapter {
    List videoList = getList(Main2Activity.this);

    @Override
    public int getCount() {
        return videoList.size();
    }

    @Override
    public Object getItem(int position) {
        return videoList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View view = View.inflate(Main2Activity.this, R.layout.item, null);
        TextView thumPath = view.findViewById(R.id.thumPath);
        String thumbPath = (videoList.get(position).getThumbPath());
        if (thumbPath != null) {
            thumPath.setText(thumbPath);
        }
        TextView path = view.findViewById(R.id.path);
        String videoPath = videoList.get(position).getPath();
        if (videoPath != null) {
            pathMap.put(position,videoPath);
            path.setText(videoPath);
            ImageView iv = view.findViewById(R.id.iv);
            iv.setImageBitmap(getVideoThumbnail(videoPath));
        }
        return view;
    }
}

/**
 * Android得到视频缩略图,可以通过接口类 MediaMetadataRetriever 来实现
 * 其中函数getFrameAtTime()有其他重载函数,该函数会随机选择一帧抓取,
 * 如果想要指定具体时间的缩略图,可以用函数getFrameAtTime(long timeUs), getFrameAtTime(long timeUs, int option)
 * @param filePath
 * @return
 */
public Bitmap getVideoThumbnail(String filePath) {
    Bitmap bitmap = null;
    MediaMetadataRetriever retriever = new MediaMetadataRetriever();
    try {
        retriever.setDataSource(filePath);
        bitmap = retriever.getFrameAtTime();
    }
    catch(IllegalArgumentException e) {
        e.printStackTrace();
    }
    catch (RuntimeException e) {
        e.printStackTrace();
    }
    finally {
        try {
            retriever.release();
        }
        catch (RuntimeException e) {
            e.printStackTrace();
        }
    }
    return bitmap;
}}

你可能感兴趣的:(Android基础)