添加画中画模式支持,优化直播播放体验
This commit is contained in:
@@ -56,9 +56,11 @@
|
||||
<activity
|
||||
android:name=".live.LivePlayActivity"
|
||||
android:exported="false"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|screenLayout|uiMode"
|
||||
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboardHidden"
|
||||
android:screenOrientation="fullSensor"
|
||||
android:theme="@style/Theme.AVDemo.NoActionBar"
|
||||
android:resizeableActivity="true"
|
||||
android:supportsPictureInPicture="true"
|
||||
android:parentActivityName=".FeatureHubActivity" />
|
||||
|
||||
<activity
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.graphics.Typeface
|
||||
@@ -44,6 +45,7 @@ import com.sellycloud.sellycloudsdk.SellyLatencyChasingUpdate
|
||||
import com.sellycloud.sellycloudsdk.SellyLiveMode
|
||||
import com.sellycloud.sellycloudsdk.SellyLiveVideoPlayer
|
||||
import com.sellycloud.sellycloudsdk.SellyLiveVideoPlayerDelegate
|
||||
import com.sellycloud.sellycloudsdk.SellyPipController
|
||||
import com.sellycloud.sellycloudsdk.SellyPlayerState
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -64,6 +66,7 @@ class LivePlayActivity : AppCompatActivity() {
|
||||
private lateinit var args: Args
|
||||
|
||||
private lateinit var playerClient: SellyLiveVideoPlayer
|
||||
private lateinit var pipController: SellyPipController
|
||||
|
||||
private var isPlaying: Boolean = false
|
||||
private var isMuted: Boolean = false
|
||||
@@ -82,6 +85,7 @@ class LivePlayActivity : AppCompatActivity() {
|
||||
private var logDialog: AlertDialog? = null
|
||||
private var logSummaryView: TextView? = null
|
||||
private var logContentView: TextView? = null
|
||||
private var logFloatingButton: View? = null
|
||||
|
||||
private val storagePermissionLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.RequestPermission()
|
||||
@@ -99,6 +103,7 @@ class LivePlayActivity : AppCompatActivity() {
|
||||
addLogFloatingButton()
|
||||
|
||||
envStore = LiveEnvSettingsStore(this)
|
||||
pipController = SellyPipController(this)
|
||||
val env = envStore.read().also { it.applyToSdkRuntimeConfig(this) }
|
||||
args = Args.from(intent, env)
|
||||
Log.d(TAG, "init liveMode=${args.liveMode} input=${args.streamIdOrUrl} autoStart=${args.autoStart}")
|
||||
@@ -184,7 +189,7 @@ class LivePlayActivity : AppCompatActivity() {
|
||||
binding.actionPlay.setOnClickListener { togglePlay() }
|
||||
binding.actionMute.setOnClickListener { toggleMute() }
|
||||
binding.actionScreenshot.setOnClickListener { captureCurrentFrame() }
|
||||
binding.actionSeek10.setOnClickListener { seekForward10s() }
|
||||
binding.actionPip.setOnClickListener { enterPipMode() }
|
||||
|
||||
playerClient.attachRenderView(binding.renderContainer)
|
||||
|
||||
@@ -203,6 +208,22 @@ class LivePlayActivity : AppCompatActivity() {
|
||||
uiScope.cancel()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
if (isInPictureInPictureMode) return
|
||||
}
|
||||
|
||||
override fun onUserLeaveHint() {
|
||||
super.onUserLeaveHint()
|
||||
if (!isPlaying) return
|
||||
enterPipMode()
|
||||
}
|
||||
|
||||
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration) {
|
||||
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
|
||||
updatePipUi(isInPictureInPictureMode)
|
||||
}
|
||||
|
||||
private fun togglePlay() {
|
||||
if (isPlaying) {
|
||||
logEvent("用户操作: 暂停")
|
||||
@@ -224,15 +245,11 @@ class LivePlayActivity : AppCompatActivity() {
|
||||
logEvent(if (isMuted) "用户操作: 静音" else "用户操作: 取消静音")
|
||||
}
|
||||
|
||||
private fun seekForward10s() {
|
||||
logEvent("用户操作: 快进10秒")
|
||||
if (args.liveMode == SellyLiveMode.RTC) {
|
||||
Toast.makeText(this, "RTC 暂不支持快进", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
val ok = playerClient.seekBy(10_000L)
|
||||
if (!ok) Toast.makeText(this, "当前流不支持快进", Toast.LENGTH_SHORT).show()
|
||||
logEvent(if (ok) "快进结果: 成功" else "快进结果: 失败")
|
||||
private fun enterPipMode() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
|
||||
if (!isPlaying) return
|
||||
val renderView = playerClient.getRenderView() ?: binding.renderContainer
|
||||
pipController.enterPictureInPictureMode(renderView)
|
||||
}
|
||||
|
||||
private fun startPlayback() {
|
||||
@@ -327,10 +344,26 @@ class LivePlayActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun updatePreviewVisibility() {
|
||||
if (isInPictureInPictureMode) {
|
||||
binding.ivPreview.visibility = View.GONE
|
||||
return
|
||||
}
|
||||
val shouldShow = !previewImageUrl.isNullOrBlank() && !hasFirstVideoFrameRendered
|
||||
binding.ivPreview.visibility = if (shouldShow) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
private fun updatePipUi(isInPip: Boolean) {
|
||||
val controlsVisibility = if (isInPip) View.GONE else View.VISIBLE
|
||||
binding.controlBar.visibility = controlsVisibility
|
||||
binding.btnClose.visibility = controlsVisibility
|
||||
logFloatingButton?.visibility = controlsVisibility
|
||||
if (isInPip) {
|
||||
binding.ivPreview.visibility = View.GONE
|
||||
} else {
|
||||
updatePreviewVisibility()
|
||||
}
|
||||
}
|
||||
|
||||
private fun captureCurrentFrame() {
|
||||
logEvent("用户操作: 截图")
|
||||
val view = playerClient.getRenderView()
|
||||
@@ -425,6 +458,7 @@ class LivePlayActivity : AppCompatActivity() {
|
||||
bottomMargin = marginBottomPx
|
||||
}
|
||||
addContentView(button, params)
|
||||
logFloatingButton = button
|
||||
}
|
||||
|
||||
private fun showLogDialog() {
|
||||
|
||||
10
example/src/main/res/drawable/ic_av_pip.xml
Normal file
10
example/src/main/res/drawable/ic_av_pip.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M19,7H5c-1.1,0-2,0.9-2,2v8c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V9c0-1.1-0.9-2-2-2zm0,10h-6v-4h6v4z" />
|
||||
</vector>
|
||||
@@ -143,7 +143,7 @@
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/actionSeek10"
|
||||
android:id="@+id/actionPip"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
@@ -158,15 +158,15 @@
|
||||
<ImageView
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:contentDescription="@string/play_ctrl_seek_10"
|
||||
android:src="@drawable/ic_av_replay_10"
|
||||
android:contentDescription="@string/play_ctrl_pip"
|
||||
android:src="@drawable/ic_av_pip"
|
||||
app:tint="@color/brand_primary_text_on" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="@string/play_ctrl_seek_10"
|
||||
android:text="@string/play_ctrl_pip"
|
||||
android:textColor="@color/brand_primary_text_on"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
<string name="play_ctrl_mute">静音</string>
|
||||
<string name="play_ctrl_unmute">取消静音</string>
|
||||
<string name="play_ctrl_screenshot">截图</string>
|
||||
<string name="play_ctrl_seek_10">快进10秒</string>
|
||||
<string name="play_ctrl_pip">画中画</string>
|
||||
|
||||
<string name="live_stats_title">直播数据</string>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user