转载请注明地址: http://blog.csdn.net/shenxiaolei507

本文是在 android视频播放(二) 利用android原生的MediaPlayer+SurfaceView的基础上,进行的一些功能上再次提高和一些需求的实现,如果对android利用MediaPlayer+SurfaceView播放视频不熟悉,可以看下这篇文章。

众所周知,我们在开发视频播放的时候,往往不光是简单的对视频进行播放管理,还会有一些其它的需求和高级功能,比如切换到屏幕时我们要保持视频,屏幕旋转,视频大小模式,视频截图等等,好了,下面我们就开始讲解一部分功能的实现。

一、屏幕旋转处理

         在视频播放的时候,我们一般都会采用横屏的形式来播放视频,但是手机在用户没有关闭自动旋转的情况下,屏幕会在横竖屏之间切换,用户体验会非常不好,所以我们要对这种情况做处理。我们只需要在AndroidManifest的文件中,对播放视频的Activity加上这样的配置,我们就会一直处于横屏状态。

android:screenOrientation="userLandscape"android:configChanges="keyboardHidden|orientation|screenSize"

对上述参数介绍:

android:screenOrientation 为设置屏幕方向参数,userLandescape为横屏,其中参数有很多,这里不是咱们介绍重点,如果有兴趣大家可以自己摸索。这里推荐一篇文章 http://www.cnblogs.com/bluestorm/archive/2012/05/07/2486998.html

android:configChanges 为配置变化参数。其中keyboardHidden为键盘的可用性发生改变,orientation是屏幕的方向发生改变,screeSize为屏幕的大小发生改变,设置本参数的目的就是为了防止屏幕旋转时重新调用Activity的各个生命周期,关于android:configChanges我们要注意以下几点:

(1)不设置android:configChanges时屏幕旋转会重新调用activity的各个生命周期,切横屏时会调用一次,切竖屏时调用两次。

(2)只设置android:configChanges="orientation"时屏幕旋转会重新调用activity的各个生命周期,但是横竖屏时都只会调用一次。

(3)设置android:configChanges="orientation|keyboardHidden"时屏幕旋转不会调用activity各个生命周期,只会调用Activity的onConfigurationChanged方法,但在Android API13以后,因为屏幕的大小screeSize会随着屏幕的切换而改变,Android默认会再调用Activity的各个生命周期,所以我们也加上screeSize的属性,最后为了兼容API上下版本我们设置android:configChanges="keyboardHidden|orientation|screenSize"


二、视频界面大小模式处理

我们都知道视频播放的时候,有的用户习惯在全屏模式下观看,也有的用户习惯在窗口模式下观看,所以这样的需求在视频播放方面也比较多,下面我们就来看下具体的处理方法,

 /**     * 改变视频的显示大小,全屏,窗口,内容     */    public void changeVideoSize() {        // 改变视频大小        String videoSizeString = videoSizeButton.getText().toString();        // 获取视频的宽度和高度        int width = mediaPlayer.getVideoWidth();        int height = mediaPlayer.getVideoHeight();        // 如果按钮文字为窗口则设置为窗口模式        if ("窗口".equals(videoSizeString)) {            /*             * 如果为全屏模式则改为适应内容的,前提是视频宽高小于屏幕宽高,如果大于宽高 我们要做缩放             * 如果视频的宽高度有一方不满足我们就要进行缩放. 如果视频的大小都满足就直接设置并居中显示。             */            if (width > screenWidth || height > screenHeight) {                // 计算出宽高的倍数                float vWidth = (float) width / (float) screenWidth;                float vHeight = (float) height / (float) screenHeight;                // 获取最大的倍数值,按大数值进行缩放                float max = Math.max(vWidth, vHeight);                // 计算出缩放大小,取接近的正值                width = (int) Math.ceil((float) width / max);                height = (int) Math.ceil((float) height / max);            }            // 设置SurfaceView的大小并居中显示            RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(width,                    height);            layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);            surfaceView.setLayoutParams(layoutParams);            videoSizeButton.setText("全屏");        } else if ("全屏".equals(videoSizeString)) {            // 设置全屏            // 设置SurfaceView的大小并居中显示            RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(screenWidth,                    screenHeight);            layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);            surfaceView.setLayoutParams(layoutParams);            videoSizeButton.setText("窗口");        }    }

关于上述方法我主要讲解以下几点:

(1)通过MediaPlayer的getVideoWidth,getVideoHeight方法,可以获取到视频的原始大小。

(2)在视频的原始大小超出手机屏幕的范围的情况下,我们要对视频的大小进行等比例缩放,通过计算宽高的倍数,根据最大的倍数,算出缩放以后的宽高。

(3)设置视频的宽高大小是通过为SurfaceView设置LayoutParams来实现的,关于使用那个类下面的LayoutParams,主要参考SurfaceView的父布局。而surfaceHolder.setFixedSize(?, ?);是设置分辨率,视频窗口的大小是由surfaceView的大小决定的。


三、视频截图

关于视频截图的需求,在很多视频播放器上,都有涉及。而且很多开源的视频播放框架,也会提供这样的功能。关于网络视频截图的实现有一定的技术难度,本文暂时不介绍,现在来介绍一种本地视频截图的方法实现,采用的是android系统提供的API,下面来看代码

  /**     * 保存视频截图.该方法只能支持本地视频文件     *      * @param time视频当前位置     */    public void savaScreenShot(long time) {        // 标记是否保存成功        boolean isSave = false;        // 获取文件路径        String path = null;        // 文件名称        String fileName = null;        if (time >= 0) {            try {                MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();                mediaMetadataRetriever.setDataSource(pathString);                // 获取视频的播放总时长单位为毫秒                String timeString = mediaMetadataRetriever                        .extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);                // 转换格式为微秒                long timelong = Long.parseLong(timeString) * 1000;                // 计算当前视频截取的位置                long index = (time * timelong) / mediaPlayer.getDuration();                // 获取当前视频指定位置的截图,时间参数的单位是微秒,做了*1000处理                // 第二个参数为指定位置,意思接近的位置截图                Bitmap bitmap = mediaMetadataRetriever.getFrameAtTime(time * 1000,                        MediaMetadataRetriever.OPTION_CLOSEST_SYNC);                // 释放资源                mediaMetadataRetriever.release();                // 判断外部设备SD卡是否存在                if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {                    // 存在获取外部文件路径                    path = Environment.getExternalStorageDirectory().getPath();                } else {                    // 不存在获取内部存储                    path = Environment.getDataDirectory().getPath();                }                // 设置文件名称 ,以事件毫秒为名称                fileName = Calendar.getInstance().getTimeInMillis() + ".jpg";                // 设置保存文件                File file = new File(path + "/shen/" + fileName);                if (!file.exists()) {                    file.createNewFile();                }                FileOutputStream fileOutputStream = new FileOutputStream(file);                bitmap.compress(CompressFormat.JPEG, 100, fileOutputStream);                isSave = true;            } catch (Exception e) {                e.printStackTrace();            }            // 保存成功以后,展示图片            if (isSave) {                ImageView imageView = new ImageView(this);                imageView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,                        LayoutParams.WRAP_CONTENT));                imageView.setImageBitmap(BitmapFactory.decodeFile(path + "/shen/" + fileName));                new AlertDialog.Builder(this).setView(imageView).show();            }        }    }

关于视频的实现,主要通过以下代码实现的

MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();mediaMetadataRetriever.setDataSource(pathString);// 获取当前视频指定位置的截图,时间参数的单位是微秒,做了*1000处理// 第二个参数为指定位置,意思接近的位置截图Bitmap bitmap = mediaMetadataRetriever.getFrameAtTime(time * 1000,                       MediaMetadataRetriever.OPTION_CLOSEST_SYNC);

(1)通过setDataSource();设置播放视频的路径,该路径为本地路径。

(2)通过getFrameAtTime();方法获取本地截图返回Bitmap对象。该方法有两个参数第一个参数为指定的时间的位置,该时间位置的单位为微妙,比毫秒还小一级,1毫秒等于1000微秒。通过MediaPlayer.getCurrentPosition()方法获取的播放时间位置是毫秒,所以要乘以1000。第二个参数为与该位置的位置,该参数有很多方式,都是MediaMetadataRetriever的常量数值,其中有OPTION_PREVIOUS_SYNC表示返回指定位置稍早的截图,OPTION_NEXT_SYNC表示返回指定位置稍晚的截图,OPTION_CLOSEST_SYNC表示返回接近指定位置的截图,OPTION_CLOSEST表示返回比指定位置早更多的截图,OPTION_CLOSEST表示返回比指定位置晚更多的截图,而其中的OPTION_CLOSEST_SYNC为其中的默认参数设置。


四、关于一些MediaPlayer的一些接口的解释

    /**     * 视频播放完成以后调用的回调函数     */    @Override    public void onCompletion(MediaPlayer mp) {    }
该方法为视频播放视频播放完毕监听。具体功能实现看具体需求。
    /**     * 视频播放发生错误时调用的回调函数     */    @Override    public boolean onError(MediaPlayer mp, int what, int extra) {        switch (what){            case MediaPlayer.MEDIA_ERROR_UNKNOWN:                Log.e("text","发生未知错误");                break;            case MediaPlayer.MEDIA_ERROR_SERVER_DIED:                Log.e("text","媒体服务器死机");                break;            default:                Log.e("text","onError+"+what);                break;        }        switch (extra){            case MediaPlayer.MEDIA_ERROR_IO:                //io读写错误                Log.e("text","文件或网络相关的IO操作错误");                break;            case MediaPlayer.MEDIA_ERROR_MALFORMED:                //文件格式不支持                Log.e("text","比特流编码标准或文件不符合相关规范");                break;            case MediaPlayer.MEDIA_ERROR_TIMED_OUT:                //一些操作需要太长时间来完成,通常超过3 - 5秒。                Log.e("text","操作超时");                break;            case MediaPlayer.MEDIA_ERROR_UNSUPPORTED:                //比特流编码标准或文件符合相关规范,但媒体框架不支持该功能                Log.e("text","比特流编码标准或文件符合相关规范,但媒体框架不支持该功能");                break;            default:                Log.e("text","onError+"+extra);                break;        }        //如果未指定回调函数, 或回调函数返回假,VideoView 会通知用户发生了错误。        return false;    }
该方法的为播放错误监听方法,具体的错误信息,在代码中已经很有详细的注释,这里不在进行解释。

    /**     * 视频缓存大小监听,当视频播放以后 在started状态会调用     */    public void onBufferingUpdate(MediaPlayer mp, int percent) {        // TODO Auto-generated method stub        // percent 表示缓存加载进度,0为没开始,100表示加载完成,在加载完成以后也会一直调用该方法        Log.e("text", "onBufferingUpdate-->" + percent);    }
该方法为视频缓存大小监听大小方法,该方法是在视频处于Started(已经播放)状态后开始起作用。参数percent 表示缓存加载进度,0为没开始,100表示加载完成。在加载完成以后也会一直调用该方法,所以在处理的时候也要特别注意。


好了关于Android利用原生MediaPlayer+SurfaceView播放视频的提高和更多功能实现,就讲解到这里。欢迎大家多多指正!!!。


源码地址:Android利用MediaPlayer+SurfaceView播放网络视频




更多相关文章

  1. Android开发视频教程大总结—23部视频教程
  2. Android(安卓)性能优化系列视频(五)
  3. Android(安卓)Studio App设置TextView文字内容大小颜色
  4. 短视频直播源码Android关于分屏的知识总结
  5. [Android(安卓)Training视频系列]1.1 Creating an Android(安卓)
  6. Android(安卓)IOS WebRTC 音视频开发总结(十五)-- courseIndex
  7. android开发学习笔记(1)我的第一个android应用程序
  8. Android仿腾讯视频悬浮窗的实现
  9. android视频教程 网盘下载

随机推荐

  1. android中关于class Build以及如何修改an
  2. android studio更新arr无效,API找不到
  3. Android TabHost两种实现方式
  4. No toolchains found in the NDK toolcha
  5. Android(安卓)Activity启动模式
  6. Android(安卓)SQLite数据库操作
  7. Android日志收集
  8. android屏幕适配建议 (二)
  9. Android中同时选择日期和时间
  10. Android初步了解入门