欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android开发之ExoPlayer的学习和使用(音频)讲解

程序员文章站 2022-09-21 22:23:00
android开发之exoplayer的学习和使用(音频)讲解 1.前言 google github exoplayer 简要说明: exoplayer is an application leve...

android开发之exoplayer的学习和使用(音频)讲解

1.前言

google github exoplayer

简要说明:

exoplayer is an application level media player for android. it provides an alternative to android’s mediaplayer api for playing audio and video both locally and over the internet. exoplayer supports features not currently supported by android’s mediaplayer api, including dash and smoothstreaming adaptive playbacks. unlike the mediaplayer api, exoplayer is easy to customize and extend, and can be updated through play store application updates.

来自google翻译:

exoplayer是android的应用程序级媒体播放器。 它提供了android的mediaplayer api的替代品,用于在本地和互联网上播放音频和视频。 exoplayer支持android mediaplayer api目前不支持的功能,包括dash和smoothstreaming自适应回放。 与mediaplayer api不同,exoplayer易于定制和扩展,并可通过play store应用程序更新进行更新。

综上所述,大概就是mediaplayer不如exoplayer,google推荐使用exoplayer。

2.how to use

如何能用才是我们关注的重点,看得到效果,才知道是不是适合我们的。

首先我们至少要能让音频能够播放,我们才能做更多想做的事情,对吧,不然捣鼓半天,都不知道音频咋播放的,那真是不高兴。

接下来开始接入步骤,先让音频播放起来,

2.1.添加依赖

根目录的build.gradle文件添加

repositories {
 jcenter()
 google()
}

app或者module下的build.gradle文件下添加

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

下面的内容按需添加,符合自己需求的

implementation 'com.google.android.exoplayer:exoplayer-core:2.x.x'
implementation 'com.google.android.exoplayer:exoplayer-dash:2.x.x' 

(这里解释一下dash(dynamic adaptive streaming over http)即自适应流媒体传输,什么意思呢,简单概括来说,就是在服务器端提前存好同一内容的不同码率、不同分辨率的多个分片以及相应的描述文件mpd,客户端在播放时即可以根据自身性能以及网络环境选择最适宜的版本)

implementation 'com.google.android.exoplayer:exoplayer-ui:2.x.x'

(这里我使用的是  implementation 'com.google.android.exoplayer:exoplayer:2.8.1'
 implementation 'com.google.android.exoplayer:exoplayer-core:2.8.1'  )

2.2.代码编写

1-> 

获取player的一个实例,大多数情况可以直接使用 defaulttrackselector 

( defaulttrackselector 该类可以对当前播放的音视频进行操作,比如设置音轨,设置约束曲目选择,禁用渲染器)

val player = exoplayerfactory.newsimpleinstance(this, defaulttrackselector())

val defaultdatasourcefactory = defaultdatasourcefactory(this, "audio/mpeg") //  audio/mpeg

val concatenatingmediasource = concatenatingmediasource() //创建一个媒体连接源 

2->

val mediasource1 = extractormediasource.factory(defaultdatasourcefactory)
.createmediasource(uri.parse("https://xiaxiayige.u.qiniudn.com/big%20big%20world.mp3")) //创建一个播放数据源

3->

concatenatingmediasource.addmediasource(mediasource1) //把数据源添加到concatenatingmediasource里面,相当于添加到一个播放池

4->

player.playwhenready = true //设置属性,当准备好以后 自动开始播放

5->

 player.prepare(concatenatingmediasource) //把player和数据源关联起来  

6->

ok,播放的功能就这样结束了,下面看看完整代码


class audioplayerdemo_a : appcompatactivity() {

override fun oncreate(savedinstancestate: bundle) {
 super.oncreate(savedinstancestate)
 setcontentview(r.layout.activity_audio_player_demo_)
 val player = exoplayerfactory.newsimpleinstance(this, defaulttrackselector())
 val defaultdatasourcefactory = defaultdatasourcefactory(this, "audio/mpeg") //  useragent -> audio/mpeg  不能为空
 val concatenatingmediasource = concatenatingmediasource() //创建一个媒体连接源
 val mediasource1 = extractormediasource.factory(defaultdatasourcefactory)
.createmediasource(uri.parse("https://xiaxiayige.u.qiniudn.com/big%20big%20world.mp3")) //创建一个播放数据源
 concatenatingmediasource.addmediasource(mediasource1)
 player.playwhenready = true
 player.prepare(concatenatingmediasource)
 }
}

直接拷贝上面代码 ,是可以直接打卡播放音频的哦,当前activity没有做其他任何操作,打开以后自动播放。

注意:如果需要自己控制播放或者暂停可以调用 player.playwhenready = true 或者 player.playwhenready = false 可以控制播放的暂停和继续播放

3.完成需求

上面已经讲了如何播放一个音频,已经差不多完成了我们一个需求,毕竟至少是要能够播放。

接下来我们完成更多的需求。

1.多个音频连续播放
2.实现倍速播放-慢速或快速
3.more and more (后台播放,锁屏播放,通知栏ui显示播放情况)

3.1.多个音频连续播放

首先完成来完成第一个需求,第一个需求比较简单,上面已经涉及到了。

val mediasource1 = extractormediasource.factory(defaultdatasourcefactory)
.createmediasource(uri.parse("https://xiaxiayige.u.qiniudn.com/big%20big%20world.mp3")) //创建一个播放数据源
concatenatingmediasource.addmediasource(mediasource1)

上面这串代码是把mediasource1 添加到一个列表中,当然既然提供了add方法,肯定还能继续add,内部也是通过一个list来存储添加的数据。

这里需要了解一下 loopingmediasourcemergingmediasourceconcatenatingmediasourceclippingmediasource 这4个mediasource都是继承了compositemediasource这个抽象类,所以我们一个一个来看看都是什么作用

3.1.1.loopingmediasource:

可以将concatenatingmediasource添加到loopingmediasource中 

 val mediasource1 = extractormediasource.factory(defaultdatasourcefactory)
.createmediasource(uri.parse("https://xiaxiayige.u.qiniudn.com/big%20big%20world.mp3")) //创建一个播放数据源

 val mediasource2 = extractormediasource.factory(defaultdatasourcefactory)
.createmediasource(uri.parse("https://xiaxiayige.u.qiniudn.com/let%20it%20go%20%281%29.mp3")) //创建一个播放数据源

concatenatingmediasource.addmediasource(mediasource1)
concatenatingmediasource.addmediasource(mediasource2) //添加多个mediasorce

val loopmediasouce=loopingmediasource(concatenatingmediasource)// 实现多个音频循环播放
player.playwhenready = true
player.prepare(loopmediasouce) //调用次方法播放完成以后将不再继续播放

player.prepare(concatenatingmediasource)//调用次方法播放完成以后将不再继续播放

3.1.2.concatenatingmediasource: (线程安全,播放期间可以修改播放列表)

可以调用add或remove修改播放列表

3.1.3.mergingmediasource:

类似concatenatingmediasource,合并2个音频,该类应该多用于视频,可以合并视频+字幕等信息 需要在同一个timeline上,如果音频
播放使用该类,也相当于添加到一个list中,使用player.prepare(mergingmediasource) 也将循环播放,不能动态修改播放列表

以上三个类型的代码如下:

/***
* 音频的连续播放
*/
 class audioplayerdemo_b : appcompatactivity() {

override fun oncreate(savedinstancestate: bundle) {
 super.oncreate(savedinstancestate)
 setcontentview(r.layout.activity_audio_player_demo_)
 val player = exoplayerfactory.newsimpleinstance(this, defaulttrackselector())
 val defaultdatasourcefactory = defaultdatasourcefactory(this, "audio/mpeg") //  useragent -> audio/mpeg  不能为空
 val concatenatingmediasource = concatenatingmediasource() //创建一个媒体连接源
 val mediasource1 = extractormediasource.factory(defaultdatasourcefactory)
.createmediasource(uri.parse("https://xiaxiayige.u.qiniudn.com/big%20big%20world.mp3")) //创建一个播放数据源

 val mediasource2 = extractormediasource.factory(defaultdatasourcefactory)
.createmediasource(uri.parse("https://xiaxiayige.u.qiniudn.com/let%20it%20go%20%281%29.mp3")) //创建一个播放数据源

 concatenatingmediasource.addmediasource(mediasource1)
 concatenatingmediasource.addmediasource(mediasource2)

 val loopmediasouce = loopingmediasource(concatenatingmediasource)// 实现多个音频循环播放
 val mergingmediasource = mergingmediasource(mediasource1, mediasource2) //音频合并
 player.playwhenready = true


//  player.prepare(loopmediasouce) 讲loop关联player
//  player.prepare(concatenatingmediasource) //concatenatingmediasource 关联串联的medisource
 player.prepare(mergingmediasource)
}
}

3.1.4.clippingmediasource

提供剪切功能,能够裁剪一个一段音频的区间,试一试,代码如下:


class audioplayerdemo_c : appcompatactivity() {

override fun oncreate(savedinstancestate: bundle) {
 super.oncreate(savedinstancestate)
 setcontentview(r.layout.activity_audio_player_demo_)
 val player = exoplayerfactory.newsimpleinstance(this, defaulttrackselector())
 val defaultdatasourcefactory = defaultdatasourcefactory(this, "audio/mpeg") //  useragent -> audio/mpeg  不能为空
 val concatenatingmediasource = concatenatingmediasource() //创建一个媒体连接源
 val mediasource1 = extractormediasource.factory(defaultdatasourcefactory)
.createmediasource(uri.parse("https://xiaxiayige.u.qiniudn.com/big%20big%20world.mp3")) //创建一个播放数据源
 concatenatingmediasource.addmediasource(mediasource1)

 val clippingmediasource=clippingmediasource(mediasource1,10*1000*1000,20*1000*1000)  // 这里需要注意的是后面的开始时间和结束时间是微秒的单位,这里需要注意,并且结束时间不能小于开始时间。

 player.playwhenready = true
 player.prepare(clippingmediasource)
 }
}

现在到这里我们差不多已经知道了,上面的第一需求,怎么让音频连续播放,其中还扩展了其他几个功能 裁剪,合并,循环播放的功能。

3.2 音频倍速播放

实现倍速播放通过exoplayer的playbackparameters 属性设置

playbackparameters =playbackparameters(speed,pitch,skipsilence)

speed :播放速率
pitch:声调的变化
skipsilence:是否跳过音频流中的静音

3.2.1 首先添加2个按钮控制播放速度的加减

<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".mainactivity">

<seekbar
    android:id="@+id/seek"
    android:layout_width="match_parent"
    android:layout_height="20dp" />


<linearlayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <button
        android:id="@+id/b1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="-0.1倍速"
        app:layout_constraintbottom_tobottomof="parent"
        app:layout_constraintleft_toleftof="parent"
        app:layout_constraintright_torightof="parent"
        app:layout_constrainttop_totopof="parent" />

    <button
        android:id="@+id/b2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="+0.1倍速"
        app:layout_constraintbottom_tobottomof="parent"
        app:layout_constraintleft_toleftof="parent"
        app:layout_constraintright_torightof="parent"
        app:layout_constrainttop_totopof="parent" />
</linearlayout>

<linearlayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <button
        android:id="@+id/b4"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="减音调"
        app:layout_constraintbottom_tobottomof="parent"
        app:layout_constraintleft_toleftof="parent"
        app:layout_constraintright_torightof="parent"
        app:layout_constrainttop_totopof="parent" />

    <button
        android:id="@+id/b5"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="加音调"
        app:layout_constraintbottom_tobottomof="parent"
        app:layout_constraintleft_toleftof="parent"
        app:layout_constraintright_torightof="parent"
        app:layout_constrainttop_totopof="parent" />
</linearlayout>

</linearlayout>

如图:

Android开发之ExoPlayer的学习和使用(音频)讲解

前面已经说了通过playbackparameters来控制倍速和音调的加减 所以直接上代码了。

 /***
 * 倍速播放
 */
class audioplayerdemo_d : appcompatactivity() {
var speed = 1.0f
var pitch = 1.0f
override fun oncreate(savedinstancestate: bundle) {
 super.oncreate(savedinstancestate)
 setcontentview(r.layout.activity_audio_player_demo_d)

 val player = exoplayerfactory.newsimpleinstance(this, defaulttrackselector())
 val defaultdatasourcefactory = defaultdatasourcefactory(this, "audio/mpeg") //  useragent -> audio/mpeg  不能为空
 val concatenatingmediasource = concatenatingmediasource() //创建一个媒体连接源
 val mediasource1 = extractormediasource.factory(defaultdatasourcefactory)
.createmediasource(uri.parse("https://xiaxiayige.u.qiniudn.com/big%20big%20world.mp3")) //创建一个播放数据源
 concatenatingmediasource.addmediasource(mediasource1)
 player.playwhenready = true
 player.prepare(concatenatingmediasource)


 b1.setonclicklistener {
  speed -= 0.1f
  if (speed <= 0) speed = 0.1f
  player.playbackparameters = playbackparameters(speed, pitch)
 }

 b2.setonclicklistener {
  speed += 0.1f
  player.playbackparameters = playbackparameters(speed, pitch)
 }


 b4.setonclicklistener {
  pitch -= 0.1f
  player.playbackparameters = playbackparameters(speed, pitch)
 }

 b5.setonclicklistener {
  pitch += 0.1f
  player.playbackparameters = playbackparameters(speed, pitch)
 }


}
}

前面的代码基本一致,没有做什么变动

上图可以看到,顶部有一个进度条,那么,我们也顺便来做下如何获取播放进度吧,并且调整进度。

步骤:

 1.需要通过监听音频开始播放的时候
 2.需要不断的去获取当前播放进度,也就是需要一个定时器去获取当前播放时长
 3.通过seekbar调整播放进度

/***
* 倍速播放
 */
class audioplayerdemo_d : appcompatactivity() {
var speed = 1.0f
var pitch = 1.0f
override fun oncreate(savedinstancestate: bundle) {
 super.oncreate(savedinstancestate)
 setcontentview(r.layout.activity_audio_player_demo_d)

 val player = exoplayerfactory.newsimpleinstance(this, defaulttrackselector())
 val defaultdatasourcefactory = defaultdatasourcefactory(this, "audio/mpeg") //  useragent -> audio/mpeg  不能为空
 val concatenatingmediasource = concatenatingmediasource() //创建一个媒体连接源
 val mediasource1 = extractormediasource.factory(defaultdatasourcefactory)
.createmediasource(uri.parse("https://xiaxiayige.u.qiniudn.com/big%20big%20world.mp3")) //创建一个播放数据源
 concatenatingmediasource.addmediasource(mediasource1)
 player.playwhenready = true
 player.prepare(concatenatingmediasource)


 b1.setonclicklistener {
  speed -= 0.1f
  if (speed <= 0) speed = 0.1f
  player.playbackparameters = playbackparameters(speed, pitch)
 }

 b2.setonclicklistener {
  speed += 0.1f
  player.playbackparameters = playbackparameters(speed, pitch)
 }


 b4.setonclicklistener {
  pitch -= 0.1f
  player.playbackparameters = playbackparameters(speed, pitch)
 }

 b5.setonclicklistener {
  pitch += 0.1f
  player.playbackparameters = playbackparameters(speed, pitch)
 }

 val playhandler = playhandler(seek, player)
 concatenatingmediasource.addeventlistener(playhandler, object : defaultmediasourceeventlistener() {
  override fun onloadstarted(windowindex: int, mediaperiodid: mediasource.mediaperiodid, loadeventinfo: mediasourceeventlistener.loadeventinfo, medialoaddata: mediasourceeventlistener.medialoaddata) {
super.onloadstarted(windowindex, mediaperiodid, loadeventinfo, medialoaddata)
seek.max = player.duration.toint()
playhandler.sendemptymessage(0)
  }
 })

 seek.setonseekbarchangelistener(object : seekbar.onseekbarchangelistener {
  override fun onprogresschanged(seekbar: seekbar, progress: int, fromuser: boolean) {
  }

  override fun onstarttrackingtouch(seekbar: seekbar) {
  }

  override fun onstoptrackingtouch(seekbar: seekbar) {
player.seekto(seekbar.progress!!.tolong())
  }

 })


}

//更新进度条
class playhandler(seekbar: seekbar, player: exoplayer) : handler() {
 val seekbars = weakreference(seekbar)
 val players = weakreference(player)

 override fun handlemessage(msg: message) {
  super.handlemessage(msg)
  when (msg.what) {
0 -> {
 seekbars.get().max = players.get().duration!!.toint()
 seekbars.get().progress = players.get().currentposition!!.toint()
 sendemptymessagedelayed(0, 300)
}
  }
 }
}

}

结尾

好的,到这里基本上暂时告一段落,从上面,学习了

1.如何使用exoplayer进行简单的音频的播放
2.使用exoplayer对多个音频顺序播放或者循环播放,并且使用concatenatingmediasource可以在过程中操作更新mediasource-添加或移除,mergingmediasource不能实现更新操作,
3.学习了音频的裁剪,可以指定播放某一段音频数据
4.学习了音频的倍速播放,已经音频的声调修改
5.使用seekbar实现对exoplayer指定播放,并且获取播放当前位置

最后的最后在activity销毁的时候别忘记释放掉player实例

 override fun ondestroy() {
 super.ondestroy()
 player.release()
}