Android实现视频播放的3种实现方式

2023-05-16

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) {
 
    }
}
效果图如下:


————————————————

转载于:https://blog.csdn.net/liuzhi0724/article/details/81318816

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android实现视频播放的3种实现方式 的相关文章

随机推荐

  • matlab二维正弦曲线

    matlab二维正弦曲线 X span class token operator 61 span span class token number 30 span span class token punctuation span A spa
  • python 编写一段自动下载微信群的聊天记录中的图片并保存到本地的代码

    以下是一段简单的 Python 代码 xff0c 可以自动下载微信群聊天记录中的图片并保存到本地 xff1a span class hljs keyword import span requests def download image u
  • 用Python写一段猜拳代码

    好的 这是一段简单的猜拳代码 span class hljs keyword import span random span class hljs built in print span span class hljs string 石头剪
  • ubuntu16.04升级到18.04

    要将 Ubuntu 16 04 升级到 18 04 xff0c 可以按照以下步骤操作 xff1a 在终端中运行以下命令 xff0c 确保系统已经更新到最新版本 xff1a sudo apt update sudo apt upgrade 接
  • 如何使用rust实现C语言编译器

    要使用 Rust 实现 C 语言编译器 xff0c 你可以遵循以下步骤 xff1a 学习 Rust 语言的语法和特性 xff0c 以便能够编写 Rust 代码 学习编译器的基本原理和 C 语言的语法和语义 xff0c 以便能够将 C 代码翻
  • docker安装redis并进行数据目录挂载

    从dockerHub拉取镜像到本地 docker pull redis 5 0 9 alpine3 11 创建目录 宿主机 mkdir p opt docker redis conf mkdir p opt docker redis dat
  • uwsgi: error while loading shared libraries: libcrypto.so.1.1: cannot open shared object file: No su

    span class token comment 找到缺少文件的绝对路径 span find span class token operator span span class token operator span name libssl
  • Linux 出现 Read-only file system 怎么处理

    如果你的 Linux 系统出现 34 Read only file system 34 的错误消息 xff0c 这意味着你的文件系统被设置为只读 xff0c 无法进行写操作 这可能是由于磁盘空间不足 文件系统损坏或磁盘 I O 错误导致的
  • windows和linux共用蓝牙鼠标,Ubuntu和Windows双系统蓝牙设备共享配对

    蓝牙设备如键盘 鼠标都可以 装的双系统win7和Ubuntu xff0c 如果只使用一个系统 xff0c 蓝牙鼠标配对一次后可以正常使用 xff0c 但如果下次进的另一个系统必须要重新配对才能使用 所以这篇文章就是解决这个问题的 xff0c
  • python类的实例化

    在 Python 中 xff0c 类的实例化是通过在类名后面加上圆括号的方式来创建一个类的实例 例如 xff0c 如果有一个名为 34 MyClass 34 的类 xff0c 可以通过如下方式创建一个该类的实例 xff1a span cla
  • 如何提取abaqus的位移和其坐标

    坐标 每次在 Abaqus 中运行作业时 xff0c 都会在工作文件夹中创建一个 job name INP 文件 该文件包含节点编号和原始坐标 xff0c 如附图所示 INP 文件可以用任何文本编辑器 xff08 word notepad
  • C语言怎么打印出枚举类型

    在 C 语言中 xff0c 你可以使用 printf 函数来打印枚举类型 首先 xff0c 你需要在枚举类型定义的时候为每一个枚举值赋予一个字符串的别名 xff1a span class hljs built in enum span sp
  • Android android_id, deviceId, oaid的区别

    一 ANDROID ID 1 ANDROID ID是设备首次启动时由系统随机生成的一串64位的十六进制数字 2 获取方式 String ANDROID ID span class token operator 61 span Setting
  • Android设备唯一标识(AndroidID,OAID等 )

    一 ID 体系 xff1a 你只是一串代码 想要了解 OAID xff0c 我们首先需要明白 ID 体系 xff1a 想要追踪一个用户就必须先找到用户 xff0c 在这个过程中 xff0c 标识符 xff08 ID xff09 就像我们的另
  • 如何使用tkinter中的pack使按钮在主窗体正中间

    使用pack 方法可以将按钮放置在主窗体正中间 xff0c 可以使用以下代码 xff1a btn pack expand 61 True fill 61 39 both 39 xff0c 其中expand 61 True表示按钮可以填充整个
  • python实例化的几种表示方式

    Python 中实例化的几种表示方式包括 通过类名直接实例化 如果定义了一个类 34 Person 34 可以使用 34 person 61 Person 34 来实例化该类 通过 super 函数实例化 可以使用 super 函数来实例化
  • 查看git项目工程的地址

    1 xff0c cmd控制台输入 git remote v 命令 首先 xff0c 进入到项目工程目录 xff1a 其次 xff0c 从该目录地址中 xff0c 进入cmd控制台 xff1a 然后 xff0c 输入git remote v
  • 使用Android Studio打包生成Jar包的方法(亲测可用)

    首先我们来说下打成jar包的分类 xff1a 1 application应用打成jar包 2 内库打成jar包 不管是那种打包jar包都得变成内库 xff0c 所以当一种方法讲解了 打jar包之前得让你的项目成内库 xff1a 如何变成内库
  • Android引包出现Duplicate class com.xxx.xxx found in modules错误

    方法一 xff1a 改依赖库的版本 其实就是依赖的包版本不一样 xff0c 产生了冲突 xff0c 把所有依赖的库改成版本一致的就可以了 注意 xff1a 如果有多个library的引用 xff0c 并且各自引入了 so库文件 xff0c
  • Android实现视频播放的3种实现方式

    Android提供了常见的视频的编码 解码机制 使用Android自带的MediaPlayer MediaController等类可以很方便的实现视频播放的功能 支持的视频格式有MP4和3GP等 这些多媒体数据可以来自于Android应用的