Surface+MediaPlayer显示视频

本文介绍了如何使用MediaPlayer和SurfaceView播放视频,以及如何解决VideoView带来的限制。通过引用并定制一个名为VideoControllerView的外部组件,实现了自定义的媒体控制器,包括播放、暂停、全屏切换等功能。详细步骤包括下载相关代码、调整包名、导入资源文件,并在Activity中设置布局。此方法允许更灵活地控制视频显示和交互界面。
摘要由CSDN通过智能技术生成
/**
 * 该实例中使用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) { 

        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(); 
    } 
}

这是MediaPlayer+Surface组合播放视频,VideoView就是基于这个实现的。
SurfaceView负责显示,MediaPlayer负责处理视频数据,当Surface创建出来之后就设置MediaPlayer以SurfaceView作为播放平台,然后MediaPlayer就可以准备去加载资源,因为是异步加载,所以setOnPrepared()来处理当加载完毕之后的事情->MediaPlayer.start();


上次讨论VideoView的时候发现限制多多,例如它会自动调整VideoView的大小来适应按比例调整大小的视频,还有就是MediaController的问题,要解决这些问题因为VideoView的封装的缘故而显得好难解决,但是MediaPlayer+Surface就明显开放得多,例如你指定了XML中Surface的大小,那么播放的时候视频就会自动填满Surface了,第一个问题就解决了。
第二个问题,MediaController就没那么好解决了,之前也提到了解决的想法,就是自己写FrameLayout铺在视频上面,Touch视频的时候就显示出来,平时就隐藏起来,自己写的FrameLayout想要什么按钮就写什么按钮,但是这个FrameLayout也不是那么好写的,不然SDK也不会专门为我们写好MediaController类了,特别是看过源码之后就知道进度条,按钮的状态,缓冲进度,自动消失等都不是那么好写的,也不是说一定写不出,但是至少得花上一定时间吧。
下面提供一个外国的博客来解决这个问题,其实就是他已经帮我们写好了这个FrameLayout,而且代码就写在那里,想怎么改怎么改,这篇博客也是主要探究怎改的
http://www.brightec.co.uk/blog/custom-android-media-controller

先来张效果图:

代码和图标都在stackoverflow里面
http://stackoverflow.com/questions/12482203/how-to-create-custom-ui-for-android-mediacontroller/14323144#14323144

1 - Download VideoControllerView.java into your project then make the following changes:
Change the package name to match your project
Either change the last import line to match your project or delete the line if it’s not necessary
If your project’s minSdkVersion is less than 14, delete the onInitializeAccessibilityEvent and onInitializeAccessibilityNodeInfo methods on lines 561-571.
2 - Download media_controller.xml into your project’s layout folder. This file references a string called description so either create a string with this name (see 2a) or delete the android:contentDescription references in the layout.
2a - To create a description string, open strings.xml and add a line like Media Controls
3 - Download the 4 images into your project’s drawable-xhdpi folder and name them as ic_media_play.png, ic_media_pause.png, ic_media_fullscreen_shrink.png and ic_media_fullscreen_stretch.png

至于怎样使用这个拿来的VideoControllerView
首先就是在Activity设置XML:

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/video_container" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:gravity="center_horizontal|center_vertical" 
    android:orientation="vertical" > 
    <FrameLayout 
        android:id="@+id/videoSurfaceContainer" 
        
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值