android 使用MediaPlayer实现视频播放
阅读原文时间:2021年04月20日阅读:1

一、MediaPlayer介绍

1)如何获得MediaPlayer实例:

可以使用直接new的方式:

MediaPlayer mp = new MediaPlayer();

也可以使用create的方式,如:

MediaPlayer mp = MediaPlayer.create(this, R.raw.test);//这时就不用调用setDataSource了

2) 如何设置要播放的文件:

MediaPlayer要播放的文件主要包括3个来源:

a. 用户在应用中事先自带的resource资源

例如:MediaPlayer.create(this, R.raw.test);

b. 存储在SD卡或其他文件路径下的媒体文件

例如:mp.setDataSource("/sdcard/test.mp3");

c. 网络上的媒体文件

例如:mp.setDataSource("http://www.citynorth.cn/music/confucius.mp3");

MediaPlayer的setDataSource一共四个方法:

setDataSource (String path)

setDataSource (FileDescriptor fd)

setDataSource (Context context, Uri uri)

setDataSource (FileDescriptor fd, long offset, long length)

其中使用FileDescriptor时,需要将文件放到与res文件夹平级的assets文件夹里,然后使用:

AssetFileDescriptor fileDescriptor = getAssets().openFd("rain.mp3");
m_mediaPlayer.setDataSource(fileDescriptor.getFileDescriptor(),fileDescriptor.getStartOffset(), fileDescriptor.getLength());

来设置datasource

3)对播放器的主要控制方法:

Android通过控制播放器的状态的方式来控制媒体文件的播放,其中:

prepare()和prepareAsync() 提供了同步和异步两种方式设置播放器进入prepare状态,需要注意的是,如果MediaPlayer实例是由create方法创建的,那么第一次启动播放前不需要再调用prepare()了,因为create方法里已经调用过了。

start()是真正启动文件播放的方法,

pause()和stop()比较简单,起到暂停和停止播放的作用,

seekTo()是定位方法,可以让播放器从指定的位置开始播放,需要注意的是该方法是个异步方法,也就是说该方法返回时并不意味着定位完成,尤其是播放的网络文件,真正定位完成时会触发OnSeekComplete.onSeekComplete(),如果需要是可以调用setOnSeekCompleteListener(OnSeekCompleteListener)设置监听器来处理的。

release()可以释放播放器占用的资源,一旦确定不再使用播放器时应当尽早调用它释放资源。

reset()可以使播放器从Error状态中恢复过来,重新会到Idle状态。

4)设置播放器的监听器:

MediaPlayer提供了一些设置不同监听器的方法来更好地对播放器的工作状态进行监听,以期及时处理各种情况,

如: setOnCompletionListener(MediaPlayer.OnCompletionListener listener)、

setOnErrorListener(MediaPlayer.OnErrorListener listener)等,设置播放器时需要考虑到播放器可能出现的情况设置好监听和处理逻辑,以保持播放器的健壮性。

二、SurfaceView介绍

SurfaceView是视图(View)的继承类,这个视图里内嵌了一个专门用于绘制的Surface。你可以控制这个Surface的格式和尺寸。Surfaceview控制这个Surface的绘制位置。(Surface是原始图像缓冲区(raw buffer)的一个句柄,而原始图像缓冲区是由屏幕图像合成器(screen compositor)管理的)
surface是纵深排序(Z-ordered)的,这表明它总在自己所在窗口的后面。surfaceview提供了一个可见区域,只有在这个可见区域内 的surface部分内容才可见,可见区域外的部分不可见。surface的排版显示受到视图层级关系的影响,它的兄弟视图结点会在顶端显示。这意味者 surface的内容会被它的兄弟视图遮挡,这一特性可以用来放置遮盖物(overlays)(例如,文本和按钮等控件)。注意,如果surface上面 有透明控件,那么它的每次变化都会引起框架重新计算它和顶层控件的透明效果,这会影响性能。
你可以通过SurfaceHolder接口访问这个surface,getHolder()方法可以得到这个接口。
surfaceview变得可见时,surface被创建;surfaceview隐藏前,surface被销毁。这样能节省资源。如果你要查看 surface被创建和销毁的时机,可以重载surfaceCreated(SurfaceHolder)和 surfaceDestroyed(SurfaceHolder)。
surfaceview的核心在于提供了两个线程:UI线程和渲染线程。这里应注意:
1> 所有SurfaceView和SurfaceHolder.Callback的方法都应该在UI线程里调用,一般来说就是应用程序主线程。渲染线程所要访问的各种变量应该作同步处理。
2> 由于surface可能被销毁,它只在SurfaceHolder.Callback.surfaceCreated()和 SurfaceHolder.Callback.surfaceDestroyed()之间有效,所以要确保渲染线程访问的是合法有效的surface。

关于SurfaceView我们要知道和注意以下几点:

(1)SurfaceView 允许我们在非UI主线程中改变SurfaceView的内容,一般View是不允许在线程中访问的。由于这个特点游戏开发在界面处理上大多会选择SurfaceView。

(2)在使用SurfaceView的时候一定要等创建成功以后在使用,也就是在SurfaceView的getHolder中添加回调addCallback中的surfaceCreated。

(3)SurfaceView可能被销毁,而且有可能在Activity的OnDestroy()之前销毁,所以在开发中一定要做处理。

(4)SurfaceView位于UI的最底层的视图层次中,允许在其上面添加一下图层,但不能是透明的。

(5)SurfaceView的构成比其它的View复杂,占用资源也多,所以除了使用view不能实现的,再建议使用。

注意:有些视频是android播放器不能播放的,不能播放时videoHeight=0,videoWidth=0,以此来判断是否播放视频。看onPrepare()方法。

 /** 
     * 该实例中使用MediaPlayer完成播放,同时界面使用SurfaceView来实现 
     *  
     * 这里我们实现MediaPlayer中很多状态变化时的监听器 
     *  
     * 使用Mediaplayer时,也可以使用MediaController类,但是需要实现MediaController.mediaController接口 
     * 实现一些控制方法。 
     *  
     * 然后,设置controller.setMediaPlayer(),setAnchorView(),setEnabled(),show()就可以了,这里不再实现 
     * @author Administrator 
     * 
     */  
    public class VideoSurfaceDemo extends Activity implements OnCompletionListener,OnErrorListener,OnInfoListener,  
        OnPreparedListener, OnSeekCompleteListener,OnVideoSizeChangedListener,SurfaceHolder.Callback{  
        private Display currDisplay;  
        private SurfaceView surfaceView;  
        private SurfaceHolder holder;  
        private MediaPlayer player;  
        private int vWidth,vHeight;  
        //private boolean readyToPlay = false;  

        public void onCreate(Bundle savedInstanceState){  
            super.onCreate(savedInstanceState);  
            this.setContentView(R.layout.video_surface);  

            surfaceView = (SurfaceView)this.findViewById(R.id.video_surface);  
            //给SurfaceView添加CallBack监听  
            holder = surfaceView.getHolder();  
            holder.addCallback(this);  
            //为了可以播放视频或者使用Camera预览,我们需要指定其Buffer类型  
            holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);  

            //下面开始实例化MediaPlayer对象  
            player = new MediaPlayer();  
            player.setOnCompletionListener(this);  
            player.setOnErrorListener(this);  
            player.setOnInfoListener(this);  
            player.setOnPreparedListener(this);  
            player.setOnSeekCompleteListener(this);  
            player.setOnVideoSizeChangedListener(this);  
            Log.v("Begin:::", "surfaceDestroyed called");  
            //然后指定需要播放文件的路径,初始化MediaPlayer  
            String dataPath = Environment.getExternalStorageDirectory().getPath()+"/Test_Movie.m4v";  
            try {  
                player.setDataSource(dataPath);  
                Log.v("Next:::", "surfaceDestroyed called");  
            } catch (IllegalArgumentException e) {  
                e.printStackTrace();  
            } catch (IllegalStateException e) {  
                e.printStackTrace();  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
            //然后,我们取得当前Display对象  
            currDisplay = this.getWindowManager().getDefaultDisplay();  
        }  

        @Override  
        public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {  
            // 当Surface尺寸等参数改变时触发  
            Log.v("Surface Change:::", "surfaceChanged called");  
        }  
        @Override  
        public void surfaceCreated(SurfaceHolder holder) {  
            // 当SurfaceView中的Surface被创建的时候被调用  
            //在这里我们指定MediaPlayer在当前的Surface中进行播放  
            player.setDisplay(holder);  
            //在指定了MediaPlayer播放的容器后,我们就可以使用prepare或者prepareAsync来准备播放了  
            player.prepareAsync();  

        }  
        @Override  
        public void surfaceDestroyed(SurfaceHolder holder) {  
             //SurfaceView销毁,如果SurfaceView没有销毁,则销毁,方法如下:if(player != null){player.release();player =null;}
            Log.v("Surface Destory:::", "surfaceDestroyed called");  
        }  
        @Override  
        public void onVideoSizeChanged(MediaPlayer arg0, int arg1, int arg2) {  
            // 当video大小改变时触发  
            //这个方法在设置player的source后至少触发一次  
            Log.v("Video Size Change", "onVideoSizeChanged called");  

        }  
        @Override  
        public void onSeekComplete(MediaPlayer arg0) {  
            // seek操作完成时触发  
            Log.v("Seek Completion", "onSeekComplete called");  

        }  
        @Override  
        public void onPrepared(MediaPlayer player) {  
            // 当prepare完成后,该方法触发,在这里我们播放视频  

            //首先取得video的宽和高  
            vWidth = player.getVideoWidth();  
            vHeight = player.getVideoHeight();  

            if(vWidth > currDisplay.getWidth() || vHeight > currDisplay.getHeight()){  
                //如果video的宽或者高超出了当前屏幕的大小,则要进行缩放  
                float wRatio = (float)vWidth/(float)currDisplay.getWidth();  
                float hRatio = (float)vHeight/(float)currDisplay.getHeight();  

                //选择大的一个进行缩放  
                float ratio = Math.max(wRatio, hRatio);  

                vWidth = (int)Math.ceil((float)vWidth/ratio);  
                vHeight = (int)Math.ceil((float)vHeight/ratio);  

                //设置surfaceView的布局参数  
                surfaceView.setLayoutParams(new LinearLayout.LayoutParams(vWidth, vHeight));  

                //然后开始播放视频  

                player.start();  
            }  
        }  
        @Override  
        public boolean onInfo(MediaPlayer player, int whatInfo, int extra) {  
            // 当一些特定信息出现或者警告时触发  
            switch(whatInfo){  
            case MediaPlayer.MEDIA_INFO_BAD_INTERLEAVING:  
                break;  
            case MediaPlayer.MEDIA_INFO_METADATA_UPDATE:    
                break;  
            case MediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING:  
                break;  
            case MediaPlayer.MEDIA_INFO_NOT_SEEKABLE:   
                break;  
            }  
            return false;  
        }  
        @Override  
        public boolean onError(MediaPlayer player, int whatError, int extra) {  
            Log.v("Play Error:::", "onError called");  
            switch (whatError) {  
            case MediaPlayer.MEDIA_ERROR_SERVER_DIED:  
                Log.v("Play Error:::", "MEDIA_ERROR_SERVER_DIED");  
                break;  
            case MediaPlayer.MEDIA_ERROR_UNKNOWN:  
                Log.v("Play Error:::", "MEDIA_ERROR_UNKNOWN");  
                break;  
            default:  
                break;  
            }  
            return false;  
        }  
        @Override  
        public void onCompletion(MediaPlayer player) {  
            // 当MediaPlayer播放完成后触发  
            Log.v("Play Over:::", "onComletion called");  
            this.finish();  
        }  
    }  

参考文章:

1、http://www.open-open.com/lib/view/open1341754267229.html

2、http://blog.csdn.net/hellogv/article/details/6429455

3、http://blog.csdn.net/shenxiaolei507/article/details/41349295

4、http://www.cnblogs.com/fbsk/archive/2012/02/10/2345128.html

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章