Android Exoplayer - 不同视频质量的不同 URL 和手动更改
Android Exoplayer - Different URLs for different video quality and change manually
我在我的应用程序中使用 exoplayer。我用它来播放来自 url 的视频。我想做的是我有三个不同的 urls 用于相同视频的高、中和低质量,我想让用户能够手动更改视频质量。
{
"lowQualityUrl":"string url",
"mediumQualityUrl":"string url",
"highQualityUrl":"string url"
}
在 JWplayer 中,有一个选项可以为不同的质量添加不同的 sources/url。有没有类似的东西可以在exoplayer中完成...?
编辑:我不想一个接一个地播放视频。我只想切换到同一视频的不同质量,就像在 YouTube 中一样。但是我没有使用单个 url 作为源,而是 3 种不同的 urls 用于同一视频的 3 种质量(低、中、高).
我找到了该问题的解决方案或变通方法。我在 recylcerview 中使用 exoplayer。此代码可能需要一些优化。
我从这个问题下的另一个答案得到了这个想法,其中有this github link,但我认为作者删除了它。
我所做的是创建一个 class 来保存特定视频的所有 url。然后在 exoplayer 上方显示一个 Spinner,当用户选择特定质量时,我会用新的 URL 准备 exoplayer,然后搜索到之前播放的位置。您可以忽略 StringUtils 方法。
VideoPlayerConfig.kt
object VideoPlayerConfig {
//Minimum Video you want to buffer while Playing
val MIN_BUFFER_DURATION = 3000
//Max Video you want to buffer during PlayBack
val MAX_BUFFER_DURATION = 5000
//Min Video you want to buffer before start Playing it
val MIN_PLAYBACK_START_BUFFER = 1500
//Min video You want to buffer when user resumes video
val MIN_PLAYBACK_RESUME_BUFFER = 5000
}
VideoQuality.kt
您可以根据需要更改此 class。我需要为低、中和高质量 url 存储 3 个 url。我还需要在微调器中按此顺序显示它们。
class VideoQuality {
private val videoQualityUrls = HashMap<String, String>()
companion object {
val LOW = getStringResource(R.string.low)
val MEDIUM = getStringResource(R.string.medium)
val HIGH = getStringResource(R.string.high)
}
val qualityArray
get() = arrayListOf<String>().apply {
if (hasQuality(LOW)) add(LOW)
if (hasQuality(MEDIUM)) add(MEDIUM)
if (hasQuality(HIGH)) add(HIGH)
}
var defaultVideoQuality: String? = HIGH
var lowQuality: String?
set(value) {
setVideoQualityUrl(LOW, value)
}
get() = videoQualityUrls[LOW] ?: ""
var mediumQuality: String?
set(value) {
setVideoQualityUrl(MEDIUM, value)
}
get() = videoQualityUrls[MEDIUM] ?: ""
var highQuality: String?
set(value) {
setVideoQualityUrl(HIGH, value)
}
get() = videoQualityUrls[HIGH] ?: ""
private fun setVideoQualityUrl(quality: String?, url: String?) {
if (url != null && quality != null) {
videoQualityUrls[quality] = url
}
}
private fun hasQuality(quality: String?): Boolean {
if (videoQualityUrls[quality] == null) {
return false
}
return true
}
fun getVideoQualityUrl(quality: String?): String? {
return videoQualityUrls[quality]
}
}
exoplayer实现方法
private fun initializePlayer() {
if (exoPlayer == null) {
val loadControl = DefaultLoadControl.Builder()
.setBufferDurationsMs(2 * VideoPlayerConfig.MIN_BUFFER_DURATION, 2 * VideoPlayerConfig.MAX_BUFFER_DURATION, VideoPlayerConfig.MIN_PLAYBACK_START_BUFFER, VideoPlayerConfig.MIN_PLAYBACK_RESUME_BUFFER)
.createDefaultLoadControl()
//Create a default TrackSelector
val videoTrackSelectionFactory = AdaptiveTrackSelection.Factory()
val trackSelector = DefaultTrackSelector(videoTrackSelectionFactory)
exoPlayer = ExoPlayerFactory.newSimpleInstance(itemView.context, DefaultRenderersFactory(itemView.context), trackSelector, loadControl)
exoPlayer!!.addListener(PlayEventListener())
val videoQualityInfo:VideoQuality = videoListVideoDataHolderData!!.videoQualityUrls //Just an object that i created and stored in a dataHolder for this view.
val url = videoQualityInfo.getVideoQualityUrl(videoQualityInfo.defaultVideoQuality) ?: ""
preparePlayer(url)
}
}
private fun preparePlayer(url: String) {
if (url.isNotEmpty()) {
val mediaSource = buildMediaSource(StringUtils.makeHttpUrl(url))
exoPlayer?.prepare(mediaSource)
videoView.player = exoPlayer
} else {
Log.d(APPTAG, "NO DEFAULT URL")
}
}
private fun buildMediaSource(url: String): ProgressiveMediaSource {
val mUri: Uri = Uri.parse(url)
val dataSourceFactory = DefaultDataSourceFactory(
itemView.context,
Util.getUserAgent(itemView.context, getStringResource(R.string.app_name))
)
val videoSource = ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(mUri)
return videoSource
}
然后在Spinner/QualitySelector的OnItemSelectedListener
videoQualitySpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {
}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
val currentTime = exoPlayer?.currentPosition
val isReadyToPlay = exoPlayer?.playWhenReady
val urlToBuild = when (videoQualityUrls.qualityArray[position]) {
VideoQuality.LOW -> videoQualityUrls.lowQuality
VideoQuality.MEDIUM -> videoQualityUrls.mediumQuality
else -> videoQualityUrls.highQuality
}
Log.d(APPTAG, "VIDEO DETAILS :::: ${currentTime} ${isReadyToPlay} ${urlToBuild}")
if (!urlToBuild.isNullOrEmpty()) {
val mediaSource = buildMediaSource(StringUtils.makeHttpUrl(urlToBuild))
exoPlayer?.prepare(mediaSource)
exoPlayer?.playWhenReady = isReadyToPlay ?: false
exoPlayer?.seekTo(currentTime ?: 0)
}
}
}
我在我的应用程序中使用 exoplayer。我用它来播放来自 url 的视频。我想做的是我有三个不同的 urls 用于相同视频的高、中和低质量,我想让用户能够手动更改视频质量。
{
"lowQualityUrl":"string url",
"mediumQualityUrl":"string url",
"highQualityUrl":"string url"
}
在 JWplayer 中,有一个选项可以为不同的质量添加不同的 sources/url。有没有类似的东西可以在exoplayer中完成...?
编辑:我不想一个接一个地播放视频。我只想切换到同一视频的不同质量,就像在 YouTube 中一样。但是我没有使用单个 url 作为源,而是 3 种不同的 urls 用于同一视频的 3 种质量(低、中、高).
我找到了该问题的解决方案或变通方法。我在 recylcerview 中使用 exoplayer。此代码可能需要一些优化。
我从这个问题下的另一个答案得到了这个想法,其中有this github link,但我认为作者删除了它。 我所做的是创建一个 class 来保存特定视频的所有 url。然后在 exoplayer 上方显示一个 Spinner,当用户选择特定质量时,我会用新的 URL 准备 exoplayer,然后搜索到之前播放的位置。您可以忽略 StringUtils 方法。
VideoPlayerConfig.kt
object VideoPlayerConfig {
//Minimum Video you want to buffer while Playing
val MIN_BUFFER_DURATION = 3000
//Max Video you want to buffer during PlayBack
val MAX_BUFFER_DURATION = 5000
//Min Video you want to buffer before start Playing it
val MIN_PLAYBACK_START_BUFFER = 1500
//Min video You want to buffer when user resumes video
val MIN_PLAYBACK_RESUME_BUFFER = 5000
}
VideoQuality.kt
您可以根据需要更改此 class。我需要为低、中和高质量 url 存储 3 个 url。我还需要在微调器中按此顺序显示它们。
class VideoQuality {
private val videoQualityUrls = HashMap<String, String>()
companion object {
val LOW = getStringResource(R.string.low)
val MEDIUM = getStringResource(R.string.medium)
val HIGH = getStringResource(R.string.high)
}
val qualityArray
get() = arrayListOf<String>().apply {
if (hasQuality(LOW)) add(LOW)
if (hasQuality(MEDIUM)) add(MEDIUM)
if (hasQuality(HIGH)) add(HIGH)
}
var defaultVideoQuality: String? = HIGH
var lowQuality: String?
set(value) {
setVideoQualityUrl(LOW, value)
}
get() = videoQualityUrls[LOW] ?: ""
var mediumQuality: String?
set(value) {
setVideoQualityUrl(MEDIUM, value)
}
get() = videoQualityUrls[MEDIUM] ?: ""
var highQuality: String?
set(value) {
setVideoQualityUrl(HIGH, value)
}
get() = videoQualityUrls[HIGH] ?: ""
private fun setVideoQualityUrl(quality: String?, url: String?) {
if (url != null && quality != null) {
videoQualityUrls[quality] = url
}
}
private fun hasQuality(quality: String?): Boolean {
if (videoQualityUrls[quality] == null) {
return false
}
return true
}
fun getVideoQualityUrl(quality: String?): String? {
return videoQualityUrls[quality]
}
}
exoplayer实现方法
private fun initializePlayer() {
if (exoPlayer == null) {
val loadControl = DefaultLoadControl.Builder()
.setBufferDurationsMs(2 * VideoPlayerConfig.MIN_BUFFER_DURATION, 2 * VideoPlayerConfig.MAX_BUFFER_DURATION, VideoPlayerConfig.MIN_PLAYBACK_START_BUFFER, VideoPlayerConfig.MIN_PLAYBACK_RESUME_BUFFER)
.createDefaultLoadControl()
//Create a default TrackSelector
val videoTrackSelectionFactory = AdaptiveTrackSelection.Factory()
val trackSelector = DefaultTrackSelector(videoTrackSelectionFactory)
exoPlayer = ExoPlayerFactory.newSimpleInstance(itemView.context, DefaultRenderersFactory(itemView.context), trackSelector, loadControl)
exoPlayer!!.addListener(PlayEventListener())
val videoQualityInfo:VideoQuality = videoListVideoDataHolderData!!.videoQualityUrls //Just an object that i created and stored in a dataHolder for this view.
val url = videoQualityInfo.getVideoQualityUrl(videoQualityInfo.defaultVideoQuality) ?: ""
preparePlayer(url)
}
}
private fun preparePlayer(url: String) {
if (url.isNotEmpty()) {
val mediaSource = buildMediaSource(StringUtils.makeHttpUrl(url))
exoPlayer?.prepare(mediaSource)
videoView.player = exoPlayer
} else {
Log.d(APPTAG, "NO DEFAULT URL")
}
}
private fun buildMediaSource(url: String): ProgressiveMediaSource {
val mUri: Uri = Uri.parse(url)
val dataSourceFactory = DefaultDataSourceFactory(
itemView.context,
Util.getUserAgent(itemView.context, getStringResource(R.string.app_name))
)
val videoSource = ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(mUri)
return videoSource
}
然后在Spinner/QualitySelector的OnItemSelectedListener
videoQualitySpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {
}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
val currentTime = exoPlayer?.currentPosition
val isReadyToPlay = exoPlayer?.playWhenReady
val urlToBuild = when (videoQualityUrls.qualityArray[position]) {
VideoQuality.LOW -> videoQualityUrls.lowQuality
VideoQuality.MEDIUM -> videoQualityUrls.mediumQuality
else -> videoQualityUrls.highQuality
}
Log.d(APPTAG, "VIDEO DETAILS :::: ${currentTime} ${isReadyToPlay} ${urlToBuild}")
if (!urlToBuild.isNullOrEmpty()) {
val mediaSource = buildMediaSource(StringUtils.makeHttpUrl(urlToBuild))
exoPlayer?.prepare(mediaSource)
exoPlayer?.playWhenReady = isReadyToPlay ?: false
exoPlayer?.seekTo(currentTime ?: 0)
}
}
}