android播放视频(三)利用原生的MediaPlayer+SurfaceView之提高
转载请注明地址: 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播放网络视频
更多相关文章
- Android 性能优化系列视频(五)
- Android Studio App设置TextView文字内容大小颜色
- 短视频直播源码Android关于分屏的知识总结
- [Android Training视频系列]1.1 Creating an Android Project
- Android IOS WebRTC 音视频开发总结(十五)-- courseIndex
- 无废话Android之android下junit测试框架配置、保存文件到手机内
- Android视频采集
- Android实现网络视频播放
- Android 视频列表(RecyclerView)实现自动播放