EXOPlayer简要学习及应用
2017-08-25 23:23
176 查看
本想着上效果图的,可惜图片太大了不允许上传参观移步GitHub
EXOPlayer是Google官方开源的一种播放器官方介绍 ,能够支持DASH, SmoothStreaming 和 HLS,可惜不能支持Adobe的rtsp、rtmp(有时间我会把B站开源播放器放上来IjkPlayer,那才叫功能强大且易上手这是后话。毕竟EXOPlayer是Google的亲儿子,我也是先应用的它而后转去ijkPlayer的)。至于以上内容如有内容不明白的可以自行查询不是我这里的重点,都是些视频相关概念,我还是着重应用。
我项目应用的EXOPlayer版本为2.0.0,此时此刻最新的版本为2.5.1。不是我不想用最新的,而是在我刚开始接触看2.5.1源码的时候,项目中分了多个Module且功能都比较独立,应用的时候必须引用多个模块,想短时间利用来开发难度很大,需要了解的内部逻辑很多,demo估计还没看完就懵了。
相反2.0.0版本只需要
我所抽取的类就两个PlayerActivity(播放界面)和EventLogger(日志打印),应用的时候一个Intent过去就解决了。
以下是干货
Intent:
PlayerActivity的控件SimpleExoPlayerView:SimpleExoPlayerView extends FrameLayout可以理解为播放控件,里面包含了播放控制控件PlaybackControlView。
控件初始化完成,需要输入player
PlayerActivity的基本思路:
onCreate 初始化控件;
onNewIntent 释放原有player设置新数据
onStart onResume 初始化initializePlayer()
onPause onStop 释放releasePlayer()
关键就是以上几个方法,源码中冗长的代码是一些回调和抽离方法
简要介绍只能做到这种地步了,如果要自己应用或者学习的话多动手多改源码才是正道,不要妄想看一两篇文章就想搞懂。
EXOPlayer是Google官方开源的一种播放器官方介绍 ,能够支持DASH, SmoothStreaming 和 HLS,可惜不能支持Adobe的rtsp、rtmp(有时间我会把B站开源播放器放上来IjkPlayer,那才叫功能强大且易上手这是后话。毕竟EXOPlayer是Google的亲儿子,我也是先应用的它而后转去ijkPlayer的)。至于以上内容如有内容不明白的可以自行查询不是我这里的重点,都是些视频相关概念,我还是着重应用。
我项目应用的EXOPlayer版本为2.0.0,此时此刻最新的版本为2.5.1。不是我不想用最新的,而是在我刚开始接触看2.5.1源码的时候,项目中分了多个Module且功能都比较独立,应用的时候必须引用多个模块,想短时间利用来开发难度很大,需要了解的内部逻辑很多,demo估计还没看完就懵了。
相反2.0.0版本只需要
compile 'com.google.android.exoplayer:exoplayer:r2.0.0'
我所抽取的类就两个PlayerActivity(播放界面)和EventLogger(日志打印),应用的时候一个Intent过去就解决了。
以下是干货
Intent:
private void goActivity(String url) { Intent intent = new Intent(MainActivity.this, PlayerActivity.class); intent.setData(Uri.parse(url)); //传入视频地址 在PlayerActivity内会根据后缀的不同区分不同的资源 intent.setAction(PlayerActivity.ACTION_VIEW); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { //ShareElement 效果 startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(MainActivity.this, imageView, "shareImage").toBundle()); } else { startActivity(intent); } }
PlayerActivity的控件SimpleExoPlayerView:SimpleExoPlayerView extends FrameLayout可以理解为播放控件,里面包含了播放控制控件PlaybackControlView。
public SimpleExoPlayerView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); ... //控件初始化逻辑 ... //这里才是关键 其实我们看到的视频界面 无非是TextureView 或SurfaceView 这里以下有逻辑解释 View view = useTextureView ? new TextureView(context) : new SurfaceView(context); ViewGroup.LayoutParams params = new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); view.setLayoutParams(params); surfaceView = view; layout.addView(surfaceView, 0); }
控件初始化完成,需要输入player
public void setPlayer(SimpleExoPlayer player) { ... //重置操作 ... this.player = player; if (player != null) {//我们看到的就是这两个控件 if (surfaceView instanceof TextureView) { player.setVideoTextureView((TextureView) surfaceView); } else if (surfaceView instanceof SurfaceView) { player.setVideoSurfaceView((SurfaceView) surfaceView); } player.setVideoListener(componentListener); player.addListener(componentListener); player.setTextOutput(componentListener); } setUseController(useController); } public void setUseController(boolean useController) { this.useController = useController; //controller 是控制控件 播放暂停之类 if (useController) { //这种设计方式比较好 不是给player设置各种控件 而是让控件去监听变化 松耦合思想 controller.setPlayer(player); } else { controller.hide(); controller.setPlayer(null); } }
PlayerActivity的基本思路:
onCreate 初始化控件;
onNewIntent 释放原有player设置新数据
onStart onResume 初始化initializePlayer()
onPause onStop 释放releasePlayer()
关键就是以上几个方法,源码中冗长的代码是一些回调和抽离方法
private void initializePlayer() { Intent intent = getIntent(); if (player == null) { //数字证书相关 如果需求不需要的话可以移除 boolean preferExtensionDecoders = intent.getBooleanExtra(PREFER_EXTENSION_DECODERS, false); UUID drmSchemeUuid = intent.hasExtra(DRM_SCHEME_UUID_EXTRA) ? UUID.fromString(intent.getStringExtra(DRM_SCHEME_UUID_EXTRA)) : null; DrmSessionManager drmSessionManager = null; if (drmSchemeUuid != null) { String drmLicenseUrl = intent.getStringExtra(DRM_LICENSE_URL); String[] keyRequestPropertiesArray = intent.getStringArrayExtra(DRM_KEY_REQUEST_PROPERTIES); Map<String, String> keyRequestProperties; if (keyRequestPropertiesArray == null || keyRequestPropertiesArray.length < 2) { keyRequestProperties = null; } else { keyRequestProperties = new HashMap<>(); for (int i = 0; i < keyRequestPropertiesArray.length - 1; i += 2) { keyRequestProperties.put(keyRequestPropertiesArray[i], keyRequestPropertiesArray[i + 1]); } } try { drmSessionManager = buildDrmSessionManager(drmSchemeUuid, drmLicenseUrl, keyRequestProperties); } catch (UnsupportedDrmException e) { int errorStringId = Util.SDK_INT < 18 ? R.string.error_drm_not_supported : (e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME ? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown); 4000 showToast(errorStringId); return; } } //数字证书相关 eventLogger = new EventLogger();//日志打印 TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveVideoTrackSelection.Factory(BANDWIDTH_METER); trackSelector = new DefaultTrackSelector(mainHandler, videoTrackSelectionFactory); trackSelector.addListener(this); trackSelector.addListener(eventLogger); player = ExoPlayerFactory.newSimpleInstance(this, trackSelector, new DefaultLoadControl(), drmSessionManager, preferExtensionDecoders); player.addListener(this); player.addListener(eventLogger); player.setAudioDebugListener(eventLogger); player.setVideoDebugListener(eventLogger); player.setId3Output(eventLogger); simpleExoPlayerView.setPlayer(player); if (shouldRestorePosition) { if (playerPosition == C.TIME_UNSET) { player.seekToDefaultPosition(playerWindow); } else { player.seekTo(playerWindow, playerPosition); } } player.setPlayWhenReady(shouldAutoPlay); playerNeedsSource = true; } if (playerNeedsSource) { String action = intent.getAction(); Uri[] uris; String[] extensions; if (ACTION_VIEW.equals(action)) {// 多个资源的话会无缝连接播放 uris = new Uri[]{intent.getData()}; extensions = new String[]{intent.getStringExtra(EXTENSION_EXTRA)}; } else if (ACTION_VIEW_LIST.equals(action)) { String[] uriStrings = intent.getStringArrayExtra(URI_LIST_EXTRA); uris = new Uri[uriStrings.length]; for (int i = 0; i < uriStrings.length; i++) { uris[i] = Uri.parse(uriStrings[i]); } extensions = intent.getStringArrayExtra(EXTENSION_LIST_EXTRA); if (extensions == null) { extensions = new String[uriStrings.length]; } } else { showToast(getString(R.string.unexpected_intent_action, action)); return; } if (Util.maybeRequestReadExternalStoragePermission(this, uris)) {// 外部存储权限 // The player will be reinitialized if the permission is granted. return; } MediaSource[] mediaSources = new MediaSource[uris.length]; //将URL组装成资源类 for (int i = 0; i < uris.length; i++) { mediaSources[i] = buildMediaSource(uris[i], extensions[i]); } MediaSource mediaSource = mediaSources.length == 1 ? mediaSources[0] : new ConcatenatingMediaSource(mediaSources); player.prepare(mediaSource, !shouldRestorePosition); playerNeedsSource = false; } } //根据后缀区分资源类型 private MediaSource buildMediaSource(Uri uri, String overrideExtension) { int type = Util.inferContentType(!TextUtils.isEmpty(overrideExtension) ? "." + overrideExtension : uri.getLastPathSegment()); switch (type) { case Util.TYPE_SS: return new SsMediaSource(uri, buildDataSourceFactory(false), new DefaultSsChunkSource.Factory(mediaDataSourceFactory), mainHandler, eventLogger); case Util.TYPE_DASH: return new DashMediaSource(uri, buildDataSourceFactory(false), new DefaultDashChunkSource.Factory(mediaDataSourceFactory), mainHandler, eventLogger); case Util.TYPE_HLS: return new HlsMediaSource(uri, mediaDataSourceFactory, mainHandler, eventLogger); case Util.TYPE_OTHER: return new ExtractorMediaSource(uri, mediaDataSourceFactory, new DefaultExtractorsFactory(), mainHandler, eventLogger); default: { throw new IllegalStateException("Unsupported type: " + type); } } } private void releasePlayer() { if (player != null) { shouldAutoPlay = player.getPlayWhenReady(); shouldRestorePosition = false; Timeline timeline = player.getCurrentTimeline(); if (timeline != null) { playerWindow = player.getCurrentWindowIndex(); Timeline.Window window = timeline.getWindow(playerWindow, new Timeline.Window()); if (!window.isDynamic) { shouldRestorePosition = true; playerPosition = window.isSeekable ? player.getCurrentPosition() : C.TIME_UNSET; //保存播放位置 } } player.release(); player = null; trackSelector = null; eventLogger = null; } }
简要介绍只能做到这种地步了,如果要自己应用或者学习的话多动手多改源码才是正道,不要妄想看一两篇文章就想搞懂。
相关文章推荐
- mini2440应用例程学习(一)—— led-player
- IjkPlayer简要学习及应用
- ZooKeeper的学习与应用
- iOS开发音频播放基础——AVAudioPlayer的应用
- 《COM原理及应用》学习笔记之写在前面的话
- ExtJS 学习专题(一) 如何应用ExtJS(附实例)
- [转]Android系统Surface机制的SurfaceFlinger服务简要介绍和学习计划
- 【学习笔记】AIDL跨应用Service运用
- 【斯坦福---机器学习】复习笔记之监督学习应用.梯度下降
- 关于 lwip 应用在 stm32f407ig 与上位机 的通信 简要 说明
- Android开发者应该深入学习的10个开源应用项目
- iPhone/iPad高级应用与手游开发学习笔记:多点触摸与手势检测(一:UITapGestureRecognizer)
- Dalvik虚拟机简要介绍和学习计划
- CURL的学习和应用(附多线程实现)
- 一站式学习Wireshark(八):应用Wireshark过滤条件抓取特定数据流
- Android进程间通信(IPC)机制Binder简要介绍和学习计划
- 神经网络学习的原理与在OpenCV中的应用
- 深度强化学习及其在自动驾驶中的应用: DRL&ADS系列之(1): 强化学习概述
- 【c#基础7】应用框架的设计与实现学习手札之类工厂服务——反射
- Java基础学习应用_for循环打印99乘法表