exoplpayer quality selection working

This commit is contained in:
Farbod
2021-12-14 01:28:17 +04:00
parent cd31734d9a
commit 6d787fa425
10 changed files with 404 additions and 22 deletions

3
.idea/misc.xml generated
View File

@ -7,10 +7,13 @@
<entry key="app/src/main/res/drawable/ic_play.xml" value="0.17135416666666667" />
<entry key="app/src/main/res/layout/activity_main.xml" value="0.1" />
<entry key="app/src/main/res/layout/activity_player.xml" value="0.1" />
<entry key="app/src/main/res/layout/exo_styled_player_control_view.xml" value="0.25" />
<entry key="app/src/main/res/layout/fragment_home.xml" value="0.16" />
<entry key="app/src/main/res/layout/fragment_library.xml" value="0.11956521739130435" />
<entry key="app/src/main/res/layout/fragment_player.xml" value="0.15520833333333334" />
<entry key="app/src/main/res/layout/fragment_subscriptions.xml" value="0.1" />
<entry key="app/src/main/res/layout/styled_quality_list.xml" value="0.15520833333333334" />
<entry key="app/src/main/res/layout/styled_quality_list_item.xml" value="0.15520833333333334" />
<entry key="app/src/main/res/layout/trending_row.xml" value="0.33" />
<entry key="app/src/main/res/menu/bottom_menu.xml" value="0.15520833333333334" />
</map>

View File

@ -5,16 +5,19 @@ import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import com.google.android.exoplayer2.ui.PlayerView
import com.google.android.exoplayer2.ui.StyledPlayerControlView
import com.google.android.exoplayer2.ui.StyledPlayerView
internal class CustomExoPlayerView(
context: Context, attributeSet: AttributeSet? = null
) : PlayerView(context, attributeSet) {
) : StyledPlayerView(context, attributeSet) {
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
showController()
}
}
return false

View File

@ -1,28 +1,58 @@
package xyz.btcland.libretube
import android.R.attr
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.recyclerview.widget.RecyclerView
import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.MediaItem.SubtitleConfiguration
import com.google.android.exoplayer2.SimpleExoPlayer
import com.google.android.exoplayer2.source.MediaSource
import com.google.android.exoplayer2.ui.PlayerView
import com.google.android.exoplayer2.ui.StyledPlayerControlView
import com.google.android.exoplayer2.ui.StyledPlayerView
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import okhttp3.*
import java.io.IOException
import kotlin.math.abs
import com.google.android.exoplayer2.util.MimeTypes
import com.google.common.collect.ImmutableList
import android.R.attr.subtitle
import android.content.DialogInterface
import android.graphics.Color
import android.widget.*
import androidx.core.net.toUri
import com.google.android.exoplayer2.C.SELECTION_FLAG_DEFAULT
import com.google.android.exoplayer2.MediaItem.fromUri
import com.google.android.exoplayer2.Player.REPEAT_MODE_ONE
import com.google.android.exoplayer2.source.MergingMediaSource
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
import com.google.android.exoplayer2.source.ProgressiveMediaSource
import com.google.android.exoplayer2.upstream.DataSource
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource
import android.widget.PopupWindow
import android.widget.TextView
import android.graphics.drawable.Drawable
import com.google.android.exoplayer2.util.Util
import android.graphics.drawable.ColorDrawable
import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.exoplayer2.util.RepeatModeUtil
import com.google.android.exoplayer2.ui.TimeBar
import com.google.android.exoplayer2.ui.TimeBar.OnScrubListener
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
@ -41,11 +71,12 @@ class PlayerFragment : Fragment() {
private var lastProgress: Float = 0.toFloat()
private var sId: Int=0
private var eId: Int=0
private var paused =false
private lateinit var exoPlayerView: PlayerView
private lateinit var exoPlayerView: StyledPlayerView
private lateinit var motionLayout: SingleViewTouchableMotionLayout
private lateinit var exoPlayer: ExoPlayer
lateinit var exoPlayer: ExoPlayer
private lateinit var mediaSource: MediaSource
override fun onCreate(savedInstanceState: Bundle?) {
@ -95,8 +126,14 @@ class PlayerFragment : Fragment() {
val mainActivity = activity as MainActivity
val mainMotionLayout = mainActivity.findViewById<MotionLayout>(R.id.mainMotionLayout)
if (currentId==eId) {
view.findViewById<ImageButton>(R.id.quality_select).visibility =View.GONE
view.findViewById<ImageButton>(R.id.close_imageButton).visibility =View.GONE
view.findViewById<TextView>(R.id.quality_text).visibility =View.GONE
mainMotionLayout.progress = 1.toFloat()
}else if(currentId==sId){
view.findViewById<ImageButton>(R.id.quality_select).visibility =View.VISIBLE
view.findViewById<ImageButton>(R.id.close_imageButton).visibility =View.VISIBLE
view.findViewById<TextView>(R.id.quality_text).visibility =View.VISIBLE
mainMotionLayout.progress = 0.toFloat()
}
@ -116,19 +153,41 @@ class PlayerFragment : Fragment() {
playerMotionLayout.transitionToStart()
fetchJson(view)
view.findViewById<ImageView>(R.id.close_imageView).setOnClickListener{
println("wtf?")
val mainActivity = activity as MainActivity
mainActivity.supportFragmentManager.beginTransaction()
.remove(this)
.commit()
mainActivity.findViewById<FrameLayout>(R.id.container).layoutParams=ViewGroup.LayoutParams(0,0)
}
view.findViewById<ImageButton>(R.id.close_imageButton).setOnClickListener{
val mainActivity = activity as MainActivity
mainActivity.supportFragmentManager.beginTransaction()
.remove(this)
.commit()
}
val playImageView = view.findViewById<ImageView>(R.id.play_imageView)
playImageView.setOnClickListener{
paused = if(paused){
playImageView.setImageResource(R.drawable.ic_play)
exoPlayer.play()
false
}else {
playImageView.setImageResource(R.drawable.ic_pause)
exoPlayer.pause()
true
}
}
}
override fun onStop() {
super.onStop()
exoPlayer.stop()
try {
exoPlayer.stop()
}catch (e: Exception){}
}
companion object {
@ -151,13 +210,7 @@ class PlayerFragment : Fragment() {
}
}
private fun initPlayer(view: View,url: String){
exoPlayer = ExoPlayer.Builder(view.context).build()
exoPlayerView.player = exoPlayer
exoPlayer.setMediaItem(MediaItem.fromUri(url))
exoPlayer.prepare()
exoPlayer.play()
}
private fun fetchJson(view: View) {
val client = OkHttpClient()
@ -177,9 +230,77 @@ class PlayerFragment : Fragment() {
println(body)
val gson = GsonBuilder().create()
val videoInPlayer = gson.fromJson(body, VideoInPlayer::class.java)
var videosNameArray: Array<CharSequence> = arrayOf()
videosNameArray += "HLS"
for (vids in videoInPlayer.videoStreams){
val name = vids.quality +" "+ vids.format
videosNameArray += name
}
runOnUiThread {
initPlayer(view,videoInPlayer.hls)
var subtitle = mutableListOf<SubtitleConfiguration>()
subtitle?.add(SubtitleConfiguration.Builder(videoInPlayer.subtitles[0].url.toUri())
.setMimeType(videoInPlayer.subtitles[0].mimeType) // The correct MIME type (required).
.setLanguage(videoInPlayer.subtitles[0].code) // The subtitle language (optional).
.build())
val mediaItem: MediaItem = MediaItem.Builder()
.setUri(videoInPlayer.hls)
.setSubtitleConfigurations(subtitle)
.build()
exoPlayer = ExoPlayer.Builder(view.context)
.build()
exoPlayerView.setShowSubtitleButton(true)
exoPlayerView.setShowNextButton(false)
exoPlayerView.setShowPreviousButton(false)
exoPlayerView.player = exoPlayer
exoPlayer.setMediaItem(mediaItem)
///exoPlayer.getMediaItemAt(5)
exoPlayer.prepare()
exoPlayer.play()
view.findViewById<TextView>(R.id.title_textView).text = videoInPlayer.title
view.findViewById<ImageButton>(R.id.quality_select).setOnClickListener{
val builder: AlertDialog.Builder? = activity?.let {
AlertDialog.Builder(it)
}
builder!!.setTitle("Choose Quality:")
.setItems(videosNameArray,
DialogInterface.OnClickListener { dialog, which ->
// The 'which' argument contains the index position
// of the selected item
//println(which)
var subtitle = mutableListOf<SubtitleConfiguration>()
subtitle?.add(SubtitleConfiguration.Builder(videoInPlayer.subtitles[0].url.toUri())
.setMimeType(videoInPlayer.subtitles[0].mimeType) // The correct MIME type (required).
.setLanguage(videoInPlayer.subtitles[0].code) // The subtitle language (optional).
.build())
if(which==0){
val mediaItem: MediaItem = MediaItem.Builder()
.setUri(videoInPlayer.hls)
.setSubtitleConfigurations(subtitle)
.build()
exoPlayer.setMediaItem(mediaItem)
}else{
val dataSourceFactory: DataSource.Factory =
DefaultHttpDataSource.Factory()
val videoSource: MediaSource = ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(fromUri(videoInPlayer.videoStreams[which-1].url))
var audioSource: MediaSource = ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(fromUri(videoInPlayer.audioStreams[0].url))
if (videoInPlayer.videoStreams[which-1].quality=="720p" || videoInPlayer.videoStreams[which-1].quality=="1080p" || videoInPlayer.videoStreams[which-1].quality=="480p" ){
audioSource = ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(fromUri(videoInPlayer.audioStreams[getMostBitRate(videoInPlayer.audioStreams)].url))
//println("fuckkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkitttttttttttttttttttttt")
}
val mergeSource: MediaSource = MergingMediaSource(videoSource,audioSource)
exoPlayer.setMediaSource(mergeSource)
view.findViewById<TextView>(R.id.quality_text).text=videosNameArray[which]
}
})
val dialog: AlertDialog? = builder?.create()
dialog?.show()
}
}
}
@ -195,4 +316,17 @@ class PlayerFragment : Fragment() {
if (!isAdded) return // Fragment not attached to an Activity
activity?.runOnUiThread(action)
}
fun getMostBitRate(audios: List<Stream>):Int{
var bitrate =0
var index = 0
for ((i, audio) in audios.withIndex()){
val q = audio.quality.replace(" kbps","").toInt()
if (q>bitrate){
bitrate=q
index = i
}
}
return index
}
}

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M7.41,8.59L12,13.17l4.59,-4.58L18,10l-6,6 -6,-6 1.41,-1.41z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M7.41,8.59L12,13.17l4.59,-4.58L18,10l-6,6 -6,-6 1.41,-1.41z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/>
</vector>

View File

@ -0,0 +1,206 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 0dp dimensions are used to prevent this view from influencing the size of
the parent view if it uses "wrap_content". It is expanded to occupy the
entirety of the parent in code, after the parent's size has been
determined. See: https://github.com/google/ExoPlayer/issues/8726.
-->
<View android:id="@id/exo_controls_background"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/exo_black_opacity_60"/>
<FrameLayout android:id="@+id/top_bar"
android:layout_width="match_parent"
android:layout_height="@dimen/exo_styled_bottom_bar_height"
android:layout_marginBottom="@dimen/exo_styled_bottom_bar_margin_top"
android:layout_gravity="top"
android:layoutDirection="ltr">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="@dimen/exo_styled_bottom_bar_time_padding"
android:paddingEnd="@dimen/exo_styled_bottom_bar_time_padding"
android:paddingLeft="@dimen/exo_styled_bottom_bar_time_padding"
android:paddingRight="@dimen/exo_styled_bottom_bar_time_padding"
android:layout_gravity="center_vertical|start"
android:layoutDirection="ltr">
<ImageButton
android:id="@+id/close_imageButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_close2"
android:padding="@dimen/exo_icon_padding"
android:background="#00FFFFFF"
/>
</LinearLayout>
<LinearLayout
android:id="@+id/quality"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="@dimen/exo_styled_bottom_bar_time_padding"
android:paddingEnd="@dimen/exo_styled_bottom_bar_time_padding"
android:paddingLeft="@dimen/exo_styled_bottom_bar_time_padding"
android:paddingRight="@dimen/exo_styled_bottom_bar_time_padding"
android:layout_gravity="center_vertical|end"
android:layoutDirection="ltr">
<TextView
android:id="@+id/quality_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/exo_icon_padding"
android:text="HLS"
android:textColor="#FFFFFF" />
<ImageButton
android:id="@+id/quality_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_arrow_down2"
android:padding="@dimen/exo_icon_padding"
android:background="#00FFFFFF"/>
</LinearLayout>
</FrameLayout>
<FrameLayout android:id="@id/exo_bottom_bar"
android:layout_width="match_parent"
android:layout_height="@dimen/exo_styled_bottom_bar_height"
android:layout_marginTop="@dimen/exo_styled_bottom_bar_margin_top"
android:layout_gravity="bottom"
android:background="@color/exo_bottom_bar_background"
android:layoutDirection="ltr">
<LinearLayout android:id="@id/exo_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="@dimen/exo_styled_bottom_bar_time_padding"
android:paddingEnd="@dimen/exo_styled_bottom_bar_time_padding"
android:paddingLeft="@dimen/exo_styled_bottom_bar_time_padding"
android:paddingRight="@dimen/exo_styled_bottom_bar_time_padding"
android:layout_gravity="center_vertical|start"
android:layoutDirection="ltr">
<TextView android:id="@id/exo_position"
style="@style/ExoStyledControls.TimeText.Position"/>
<TextView
style="@style/ExoStyledControls.TimeText.Separator"/>
<TextView android:id="@id/exo_duration"
style="@style/ExoStyledControls.TimeText.Duration"/>
</LinearLayout>
<LinearLayout android:id="@id/exo_basic_controls"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:layoutDirection="ltr">
<ImageButton android:id="@id/exo_vr"
style="@style/ExoStyledControls.Button.Bottom.VR"/>
<ImageButton android:id="@id/exo_shuffle"
style="@style/ExoStyledControls.Button.Bottom.Shuffle"/>
<ImageButton android:id="@id/exo_repeat_toggle"
style="@style/ExoStyledControls.Button.Bottom.RepeatToggle"/>
<ImageButton android:id="@id/exo_subtitle"
style="@style/ExoStyledControls.Button.Bottom.CC"/>
<ImageButton android:id="@id/exo_settings"
style="@style/ExoStyledControls.Button.Bottom.Settings"/>
<ImageButton android:id="@id/exo_fullscreen"
style="@style/ExoStyledControls.Button.Bottom.FullScreen"/>
<ImageButton android:id="@id/exo_overflow_show"
style="@style/ExoStyledControls.Button.Bottom.OverflowShow"/>
</LinearLayout>
<HorizontalScrollView android:id="@id/exo_extra_controls_scroll_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:visibility="invisible">
<LinearLayout android:id="@id/exo_extra_controls"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layoutDirection="ltr">
<ImageButton android:id="@id/exo_overflow_hide"
style="@style/ExoStyledControls.Button.Bottom.OverflowHide"/>
</LinearLayout>
</HorizontalScrollView>
</FrameLayout>
<View android:id="@id/exo_progress_placeholder"
android:layout_width="match_parent"
android:layout_height="@dimen/exo_styled_progress_layout_height"
android:layout_gravity="bottom"
android:layout_marginBottom="@dimen/exo_styled_progress_margin_bottom"/>
<LinearLayout android:id="@id/exo_minimal_controls"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginBottom="@dimen/exo_styled_minimal_controls_margin_bottom"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layoutDirection="ltr">
<ImageButton android:id="@id/exo_minimal_fullscreen"
style="@style/ExoStyledControls.Button.Bottom.FullScreen"/>
</LinearLayout>
<LinearLayout
android:id="@id/exo_center_controls"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@android:color/transparent"
android:gravity="center"
android:padding="@dimen/exo_styled_controls_padding"
android:clipToPadding="false">
<ImageButton android:id="@id/exo_prev"
style="@style/ExoStyledControls.Button.Center.Previous"/>
<include layout="@layout/exo_styled_player_control_rewind_button" />
<ImageButton android:id="@id/exo_play_pause"
style="@style/ExoStyledControls.Button.Center.PlayPause"/>
<include layout="@layout/exo_styled_player_control_ffwd_button" />
<ImageButton android:id="@id/exo_next"
style="@style/ExoStyledControls.Button.Center.Next"/>
</LinearLayout>
</merge>

View File

@ -68,6 +68,7 @@
app:layout_constraintEnd_toEndOf="@id/main_container"
app:layout_constraintTop_toTopOf="@id/main_container"
android:src="@drawable/ic_close"
android:visibility="gone"
/>
<ImageView
@ -79,7 +80,9 @@
android:src="@drawable/ic_play"
app:layout_constraintBottom_toBottomOf="@+id/close_imageView"
app:layout_constraintEnd_toStartOf="@+id/close_imageView"
app:layout_constraintTop_toTopOf="@+id/close_imageView" />
app:layout_constraintTop_toTopOf="@+id/close_imageView"
android:visibility="gone"
/>
<TextView
android:id="@+id/title_textView"
@ -90,7 +93,6 @@
android:alpha="0"
android:ellipsize="end"
android:maxLines="1"
android:text="This is very very very very long title"
app:layout_constraintBottom_toBottomOf="@+id/play_imageView"
app:layout_constraintEnd_toStartOf="@+id/play_imageView"
app:layout_constraintStart_toEndOf="@+id/player"

View File

@ -71,7 +71,9 @@
motion:layout_constraintBottom_toBottomOf="@id/main_container"
android:layout_marginEnd="16dp"
android:alpha="1"
motion:layout_constraintTop_toTopOf="@id/main_container" />
motion:layout_constraintTop_toTopOf="@id/main_container"
android:visibility="visible"
/>
<Constraint
android:id="@+id/play_imageView"
motion:layout_constraintEnd_toStartOf="@+id/close_imageView"
@ -80,7 +82,9 @@
motion:layout_constraintBottom_toBottomOf="@+id/close_imageView"
android:layout_marginEnd="24dp"
android:alpha="1"
motion:layout_constraintTop_toTopOf="@+id/close_imageView" />
motion:layout_constraintTop_toTopOf="@+id/close_imageView"
android:visibility="visible"
/>
<Constraint
android:id="@+id/title_textView"
motion:layout_constraintEnd_toStartOf="@+id/play_imageView"