ExoPlayer是运行在YouTube app Android版本上的视频播放器。不仅功能强大,而且使用简单,可定制性强。ExoPlayer也是Google官方推荐的Android媒体播放器,可以在Android官方文档的音频和视频目录中找到。

一,优点和缺点

优点:

1,支持DASH和SmoothStreaming这两种数据格式的资源,而MediaPlayer对这两种数据格式都不支持。它还支持其它格式的数据资源,比如MP4, M4A, FMP4, WebM, MKV, MP3, Ogg, WAV, MPEG-TS, MPEG-PS, FLV and ADTS (AAC)等

2,支持高级的HLS特性,比如能正确的处理#EXT-X-DISCONTINUITY标签

3,无缝连接,合并和循环播放多媒体的能力

4,和应用一起更新播放器(ExoPlayer),因为ExoPlayer是一个集成到应用APK里面的库,你可以决定你所想使用的ExoPlayer版本,并且可以随着应用的更新把ExoPlayer更新到一个最新的版本。

5,较少的关于设备的特殊问题,并且在不同的Android版本和设备上很少会有不同的表现。

6,在Android4.4(API level 19)以及更高的版本上支持Widevine通用加密

7,为了符合你的开发需求,播放器支持自定义和扩展。其实ExoPlayer为此专门做了设计,并且允许很多组件可以被自定义的实现类替换。

8,使用官方的扩展功能可以很快的集成一些第三方的库,比如IMA扩展功能通过使用互动媒体广告SDK可以很容易地将视频内容货币化(变现)

缺点:

1,在某些设备上播放音频,ExoPlayer可能会比MediaPlayer消耗更多的电量。

二,使用

1,添加依赖

implementation 'com.google.android.exoplayer:exoplayer:2.X.X'

目前最新版本是2.10.5,最新的版本可以查看github地址。

上面是省事的方式,依赖了整个ExoPlayer库。也可以根据自己的需求选择性添加依赖,如核心库和UI库,这两个可以满足基本上的视频播放需求:

implementation 'com.google.android.exoplayer:exoplayer-core:2.X.X'
implementation 'com.google.android.exoplayer:exoplayer-ui:2.X.X'

整个ExoPlayer库包含5个字库,依赖5个子库和依赖整个库效果是一样的。

  • exoplayer-core:核心功能 (必要)
  • exoplayer-dash:支持DASH内容
  • exoplayer-hls:支持HLS内容
  • exoplayer-smoothstreaming:支持SmoothStreaming内容
  • exoplayer-ui:用于ExoPlayer的UI组件和相关的资源。

2,添加Java8的支持

android {compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8}
}

3,布局中引入

<com.google.android.exoplayer2.ui.PlayerViewandroid:id="@+id/video_view"android:layout_width="match_parent"android:layout_height="match_parent"/>

4,activity中使用

val player = ExoPlayerFactory.newSimpleInstance(this,DefaultTrackSelector(),DefaultLoadControl())
player.playWhenReady = true
video_view.player = playerval uri = Uri.parse("https://cdn.xxxxxx.com/new-sing/66c3d05eaa177e07d57465f948f0d8b934b7a7ba.mp4")
val dataSourceFactory = DefaultHttpDataSourceFactory("user-agent")
val videoSource = ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(uri)
// 播放
player.prepare(mediaSource)

此时已经可以播放了,如下:

5,释放资源

适当时候释放资源

player.release()

6,添加监听

Player.EventListener中有很多监听方法,但都是default类型的,可以根据需求添加实现。

player.addListener(object:Player.EventListener{override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {Log.e("ExoPlayer","playWhenReady: $playWhenReady  +$playbackState")when (playbackState){Player.STATE_BUFFERING->Toast.makeText(this@MainActivity,"加载中",Toast.LENGTH_LONG).show()Player.STATE_READY->Toast.makeText(this@MainActivity,"播放中",Toast.LENGTH_LONG).show()Player.STATE_ENDED->Toast.makeText(this@MainActivity,"播放完成",Toast.LENGTH_LONG).show()}}override fun onPlayerError(error: ExoPlaybackException?) {Log.e("ExoPlayer","ExoPlaybackException: $error")}...
})

三,其他功能

1,Clipping a video(视频裁剪)

播放指定的范围:

MediaSource videoSource =new ProgressiveMediaSource.Factory(...).createMediaSource(videoUri);
// Clip to start at 5 seconds and end at 10 seconds.
ClippingMediaSource clippingSource =new ClippingMediaSource(videoSource,/* startPositionUs= */ 5_000_000,/* endPositionUs= */ 10_000_000);

2,Side-loading a subtitle file(加载台词)

// Build the video MediaSource.
MediaSource videoSource =new ProgressiveMediaSource.Factory(...).createMediaSource(videoUri);
// Build the subtitle MediaSource.
Format subtitleFormat = Format.createTextSampleFormat(id, // An identifier for the track. May be null.MimeTypes.APPLICATION_SUBRIP, // The mime type. Must be set correctly.selectionFlags, // Selection flags for the track.language); // The subtitle language. May be null.
MediaSource subtitleSource =new SingleSampleMediaSource.Factory(...).createMediaSource(subtitleUri, subtitleFormat, C.TIME_UNSET);
// Plays the video with the sideloaded subtitle.
MergingMediaSource mergedSource =new MergingMediaSource(videoSource, subtitleSource);

3,Advanced composition(视频合并)

下面两种方式实现第一个视频播放两次,第二个播放一次

方法一:

MediaSource firstSource =new ProgressiveMediaSource.Factory(...).createMediaSource(firstVideoUri);
MediaSource secondSource =new ProgressiveMediaSource.Factory(...).createMediaSource(secondVideoUri);
// Plays the first video twice.
LoopingMediaSource firstSourceTwice = new LoopingMediaSource(firstSource, 2);
// Plays the first video twice, then the second video.
ConcatenatingMediaSource concatenatedSource =new ConcatenatingMediaSource(firstSourceTwice, secondSource);

方法二:

MediaSource firstSource =new ProgressiveMediaSource.Factory(...).createMediaSource(firstVideoUri);
MediaSource secondSource =new ProgressiveMediaSource.Factory(...).createMediaSource(secondVideoUri);
// Plays the first video twice, then the second video.
ConcatenatingMediaSource concatenatedSource =new ConcatenatingMediaSource(firstSource, firstSource, secondSource);

还有其他功能,可以参看官方文档:Hello world! - ExoPlayer

四,自定义UI

1,简单自定义

这种方式是通过修改ExoPlayer预留的布局文件来实现定制化,但是这种方式只能修改特定的一些UI。

在源代码中可以找到

int controllerLayoutId = R.layout.exo_player_control_view;

也就是说ExoPlayer默认使用的是这个布局,我们可以在代码中新建一个名为:exo_player_control_view 的layout,或者在xml中添加

<com.google.android.exoplayer2.ui.PlayerViewandroid:id="@+id/video_view"android:layout_width="match_parent"android:layout_height="match_parent"app:controller_layout_id="@layout/xxx_view"/>

指定我们的layout来实现覆盖的目的。如下:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"><android.support.constraint.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_gravity="bottom"android:background="@drawable/player_bottom_bg"><TextViewandroid:id="@+id/exo_position"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="10dp"android:layout_marginEnd="10dp"android:layout_marginTop="10dp"android:textColor="@android:color/white"app:layout_constraintLeft_toLeftOf="parent" /><TextViewandroid:id="@+id/exo_duration"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginEnd="10dp"android:layout_marginStart="10dp"android:layout_marginTop="10dp"android:textColor="@android:color/white"app:layout_constraintRight_toRightOf="parent" /><com.google.android.exoplayer2.ui.DefaultTimeBarandroid:id="@id/exo_progress"android:layout_width="0dp"android:layout_height="26dp"android:layout_weight="1"app:layout_constraintBottom_toBottomOf="@id/exo_position"app:layout_constraintLeft_toRightOf="@id/exo_position"app:layout_constraintRight_toLeftOf="@id/exo_duration"app:layout_constraintTop_toTopOf="@id/exo_position"app:played_color="#FFDE81"app:unplayed_color="@android:color/black"app:buffered_color="@android:color/darker_gray"/><ImageViewandroid:id="@+id/exo_play"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/play_btn"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toBottomOf="@id/exo_progress" /><ImageViewandroid:id="@+id/exo_pause"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/pause_btn"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toBottomOf="@id/exo_progress" /><android.support.constraint.Guidelineandroid:id="@+id/gl"android:layout_width="wrap_content"android:layout_height="match_parent"android:orientation="vertical"app:layout_constraintGuide_percent="0.5"/><ImageViewandroid:id="@+id/exo_rew"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/pre_btn"android:layout_marginEnd="8dp"app:layout_constraintRight_toLeftOf="@id/gl"app:layout_constraintTop_toTopOf="@id/exo_play" /><ImageViewandroid:id="@+id/exo_ffwd"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/next_btn"android:layout_marginStart="8dp"app:layout_constraintLeft_toRightOf="@id/gl"app:layout_constraintTop_toTopOf="@id/exo_play" /></android.support.constraint.ConstraintLayout></FrameLayout>

效果如下:

缺点:

这种方式控件的id不能随便起,要与exoPlayer原来PlaybackControlView的布局控件id,名称一致,不然就人家的代码中是获取不到你的id的。

有这些控件id可以使用:

exo_play –>播放

exo_pause –>暂停

exo_rew –>后退

exo_ffwd –>前进

exo_prev –>上一个

exo_next –>下一个

exo_repeat_toggle –>重复模式开关

exo_duration –>视频总时长

exo_position –>当前播放位置

exo_progress –>播放进度

2,高级自定义

高级自定义可以实现任意的效果,如下:

1,首先需要重写: com.google.android.exoplayer2.ui.PlayerView。这里的“重写”是指新建一个类比如MyPlayerView,然后复制PlayerView中的代码到MyPlayerView。使用时引用MyPlayerView。

2,引入自定义的布局,如:senior_diy_player_control_view.xml,通过controller_layout_id属性设置布局,如下:

<com.example.exoplayerdemo.custom.MyPlayerViewandroid:id="@+id/video_view"android:layout_width="match_parent"android:layout_height="match_parent"app:show_buffering="always"app:controller_layout_id="@layout/senior_diy_player_control_view"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent"/>

3,改变了布局以后需要添加自定义的事件,需要重写com.google.android.exoplayer2.ui.PlayerControlView,跟上面一样新建一个MyPlayerControlView,然后把代码复制进去,同时需要把MyPlayerView中所有的PlayerControlView的引用改为MyPlayerControlView的引用,此时MyPlayerControlView就生效了,在这里可以找到原布局中的播放、暂停、快进、快退按钮:

private final View playButton;
private final View pauseButton;
private final View fastForwardButton;
private final View rewindButton;

以及对应的点击事件:

playButton = findViewById(R.id.exo_play);
if (playButton != null) {playButton.setOnClickListener(componentListener);
}
pauseButton = findViewById(R.id.exo_pause);
if (pauseButton != null) {pauseButton.setOnClickListener(componentListener);
}

以及具体的事件方法:

@Override
public void onClick(View view) {...if (playButton == view) {...} else if (pauseButton == view) {...}...
}

接下来怎么做就不用多说了。

4,此时还不能改变进度条的样式,进度条通过com.google.android.exoplayer2.ui.DefaultTimeBar实现的,如果要改变进度条样式就需要重写DefaultTimeBar,和上面的一样,新建一个MyTimeBar,把代码复制进去,修改MyPlayerControlView中对DefaultTimeBar的引用,DefaultTimeBar继承自View,修改样式也就是自定义View的操作,比如我这里把原来的进度改成圆头的代码:

private void drawTimeBar(Canvas canvas) {..if (duration <= 0) {
//      canvas.drawRect(progressBar.left, barTop, progressBar.right, barBottom, unplayedPaint);// 改为圆角canvas.drawRoundRect(progressBar.left, barTop, progressBar.right, barBottom, barHeight/2,barHeight/2, unplayedPaint);return;}...if (progressLeft < progressBar.right) {
//      canvas.drawRect(progressLeft, barTop, progressBar.right, barBottom, unplayedPaint);// 改为圆角canvas.drawRoundRect(scrubberBar.left, barTop, progressBar.right, barBottom, barHeight/2,barHeight/2,unplayedPaint);}...if (bufferedRight > bufferedLeft) {
//      canvas.drawRect(bufferedLeft, barTop, bufferedRight, barBottom, bufferedPaint);// 改为圆角canvas.drawRoundRect(bufferedLeft, barTop, bufferedRight, barBottom, barHeight/2,barHeight/2,bufferedPaint);}if (scrubberBar.width() > 0) {
//      canvas.drawRect(scrubberBar.left, barTop, scrubberBar.right, barBottom, playedPaint);// 改为圆角canvas.drawRoundRect(scrubberBar.left, barTop, scrubberBar.right, barBottom, barHeight/2,barHeight/2, playedPaint);}...
}

这里就不多说了。

代码地址:https://gitee.com/HappyAndroid666/ExoPlayerDemo,感觉有用的帮忙star一下

史上最好用的Android音视频播放器-ExoPlayer的使用及自定义UI相关推荐

  1. android音视频播放器开发百度云,Android 播放端 SDK

    1 概述 PLDroidPlayer 是一个适用于 Android 平台的音视频播放器 SDK,可高度定制化和二次开发,为 Android 开发者提供了简单.快捷的接口,帮助开发者在 Android ...

  2. 史上最全的Android文章精选合集

    用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你能获得这些料: 知道setContentView()之后发生了什么? ... Andr ...

  3. Android本地视频播放器开发 - 搜索本地视频(1)

    这一章的主要内容是搜索手机本地视频,添加到ListView列表里,每一个表项含有这个视频的缩略图,视频的播放时间,视频的标题,在搜索本地视频(1)中我们先制作搜索功能.   Video.java--视 ...

  4. 开源安卓Android流媒体音视频播放器实现声音自动停止、恢复、一键静音功能源码

    本文转自EasyDarwin团队John的博客:http://blog.csdn.net/jyt0551/article/details/60802145 我们在开发安卓Android流媒体音视频播放 ...

  5. android 音乐视频播放器(github上十二款最著名的Android播放器开源项目)

    1.ijkplayer 项目地址: https://github.com/Bilibili/ijkplayer 介绍:Ijkplayer 是Bilibili发布的基于 FFplay 的轻量级 Andr ...

  6. 史上最详细的Android Studio系列教程四--Gradle基础

    史上最详细的Android Studio系列教程四--Gradle基础 转载于:https://www.cnblogs.com/zhujiabin/p/5125917.html

  7. 2016年GitHub上史上最全的Android开源项目分类汇总

    以下内容为转载 版主原网址 http://itindex.net/detail/51896-github-android-开源 GitHub上史上最全的Android开源项目分类汇总 今天在看博客的时 ...

  8. 史上最完美的Android沉浸式状态导航栏攻略

    前言 最近我在小破站开发一款新App,叫高能链.我是一个完美主义者,所以不管对架构还是UI,我都是比较抠细节的,在状态栏和导航栏沉浸式这一块,我还是踩了挺多坑,费了挺多精力的.这次我将我踩坑,适配各机 ...

  9. GitHub上史上最全的Android开源项目分类汇总 (转)

    GitHub上史上最全的Android开源项目分类汇总 标签: github android 开源 | 发表时间:2014-11-23 23:00 | 作者:u013149325 分享到: 出处:ht ...

最新文章

  1. 如何使用C#在ASP.NET Core中轻松实现QRCoder
  2. 能让你醍醐灌顶的34个人生哲理
  3. hbuilder怎么做登录界面_hbuilder 第三方登录实例
  4. ArcGIS API for JavaScript 入门教程[5] 再讲数据——Map类之底图与高程
  5. oracle sql语句怎么查询所有存储过程中是否包含某个注释?
  6. java calendar字符串显示_java关于字符串和日期的代码展示
  7. 光纤 matlab,matlab – 均衡光纤通道的最小均方
  8. OpenShift 4 Tekton (1) - OpenShift Pipeline入门-安装Pipeline Operator
  9. java textvaluechanged 全选删除不触发_具有TextChanged事件的AutoCompleteBox未正确选择
  10. Tricks(二十)—— 从 N 个数中等概率地产生 M 个数
  11. myEclipse-svn的安装使用
  12. zbbz插件使用教程_zbbz加载成功用不了_坐标标注插件zbbz【CAD教学】
  13. dell服务器卸载系统,如何通过 iDRAC9 装载和卸载驱动程序包
  14. 连线被拒,请检查主机名称和埠号,并确定 postmaster 可以接受 TCP/IP 连线
  15. 医院计算机管理工资,医院绩效管理平台:绩效工资管理系统
  16. Delphi 读取注册表REG_MULTI_SZ类型,注意事项
  17. Android 仿微信小程序开屏页加载loading
  18. 微信「扫一扫识物」 的背后技术揭秘
  19. 最新IOS xcode12真机调试步骤
  20. 京东回应显卡售后传闻;​IBM发布第一个2纳米芯片;苹果以工程师数量评估收购对象 | EA周报...

热门文章

  1. 【PdgCntEditor】利用PDF目录书签编辑软件PdgCntEditor为PDF型图书快速添加书签的方法
  2. 怎样解决c盘文件无法更改
  3. 机器调度问题的转移瓶颈启发式算法
  4. wps如何把文档上传到云服务器,WPS上传技巧:WPS怎么上传文件到云文档
  5. FineReport学习-【01 帆软报表入门】
  6. SpringBoot 项目中集成 Prometheus 和 Grafana
  7. 360度全景图显著性检测数据库
  8. 基于主成分分析的支持向量机入侵检测系统
  9. html锚点链接回到顶部,回到顶部和回到底部特效-jquery锚点移动兼容所有浏览器...
  10. iText的简单应用