EXOPlayer简要学习及应用

来源:互联网 发布:淘宝韩妆正品店铺推荐 编辑:程序博客网 时间:2024/06/03 20:38

本想着上效果图的,可惜图片太大了不允许上传参观移步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版本只需要

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

简要介绍只能做到这种地步了,如果要自己应用或者学习的话多动手多改源码才是正道,不要妄想看一两篇文章就想搞懂。

原创粉丝点击