diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 92f4a5f1..9fe33d2e 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -4,13 +4,12 @@ plugins {
}
android {
- namespace = "com.mux.video.media3"
+ namespace = "com.mux.player.media3"
compileSdk = 34
defaultConfig {
- applicationId = "com.mux.video.media3"
minSdk = 21
- targetSdk = 33
+ targetSdk = 34
versionCode = 1
versionName = "1.0"
@@ -38,9 +37,9 @@ android {
dependencies {
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.appcompat:appcompat:1.6.1")
- implementation("com.google.android.material:material:1.9.0")
+ implementation("com.google.android.material:material:1.10.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
- implementation("androidx.recyclerview:recyclerview:1.3.1")
+ implementation("androidx.recyclerview:recyclerview:1.3.2")
implementation(project(":library"))
testImplementation("junit:junit:4.13.2")
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index aa79be7a..bfd7f572 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -14,10 +14,17 @@
tools:targetApi="31">
+ android:exported="false"
+ android:label="Basic Foreground Player" />
+
+ android:exported="true"
+ android:label="Examples"
+ android:theme="@style/Theme.MuxVideoMedia3.NoActionBar">
diff --git a/app/src/main/java/com/mux/player/media3/MainActivity.kt b/app/src/main/java/com/mux/player/media3/MainActivity.kt
index eb9e80fa..c701676e 100644
--- a/app/src/main/java/com/mux/player/media3/MainActivity.kt
+++ b/app/src/main/java/com/mux/player/media3/MainActivity.kt
@@ -3,15 +3,17 @@ package com.mux.player.media3
import android.content.Context
import android.content.Intent
import android.os.Bundle
+import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
+import com.mux.player.media3.databinding.ActivityMainBinding
+import com.mux.player.media3.databinding.ListitemExampleBinding
import com.mux.player.media3.examples.BasicPlayerActivity
-import com.mux.video.media3.databinding.ActivityMainBinding
-import com.mux.video.media3.databinding.ListitemExampleBinding
+import com.mux.player.media3.examples.MaxResActivity
class MainActivity : AppCompatActivity() {
@@ -24,6 +26,10 @@ class MainActivity : AppCompatActivity() {
setContentView(binding.root)
examplesView.layoutManager = LinearLayoutManager(this)
+ binding.mainExampleTb.apply {
+ setLogo(R.drawable.mux_logo)
+ }
+
setUpExampleList()
}
@@ -36,6 +42,10 @@ class MainActivity : AppCompatActivity() {
Example(
title = "Basic Foreground Player",
destination = Intent(this@MainActivity, BasicPlayerActivity::class.java)
+ ),
+ Example(
+ title = "Max-Resolution Modifier",
+ destination = Intent(this@MainActivity, MaxResActivity::class.java)
)
)
}
diff --git a/app/src/main/java/com/mux/player/media3/examples/BasicPlayerActivity.kt b/app/src/main/java/com/mux/player/media3/examples/BasicPlayerActivity.kt
index 0f6bed53..e06c032d 100644
--- a/app/src/main/java/com/mux/player/media3/examples/BasicPlayerActivity.kt
+++ b/app/src/main/java/com/mux/player/media3/examples/BasicPlayerActivity.kt
@@ -17,9 +17,9 @@ import com.mux.stats.sdk.core.model.CustomerViewData
import com.mux.stats.sdk.core.util.UUID
import com.mux.player.MuxPlayer
import com.mux.player.media.MediaItems
-import com.mux.player.media.PlaybackMaxResolution
+import com.mux.player.media.PlaybackResolution
import com.mux.player.media3.PlaybackIds
-import com.mux.video.media3.databinding.ActivityBasicPlayerBinding
+import com.mux.player.media3.databinding.ActivityBasicPlayerBinding
/**
* A simple example that uses the normal media3 player UI to play a video in the foreground from
@@ -59,8 +59,6 @@ class BasicPlayerActivity : AppCompatActivity() {
val player = createPlayer(this)
val mediaItem = MediaItems.builderFromMuxPlaybackId(
PlaybackIds.TEARS_OF_STEEL,
-// PlaybackResolution.FHD_1080,
- PlaybackMaxResolution.HD_720,
)
.setMediaMetadata(
MediaMetadata.Builder()
diff --git a/app/src/main/java/com/mux/player/media3/examples/MaxResActivity.kt b/app/src/main/java/com/mux/player/media3/examples/MaxResActivity.kt
new file mode 100644
index 00000000..5820ce08
--- /dev/null
+++ b/app/src/main/java/com/mux/player/media3/examples/MaxResActivity.kt
@@ -0,0 +1,122 @@
+package com.mux.player.media3.examples
+
+import android.content.Context
+import android.os.Bundle
+import android.util.Log
+import android.widget.Toast
+import androidx.annotation.OptIn
+import androidx.appcompat.app.AppCompatActivity
+import androidx.media3.common.MediaMetadata
+import androidx.media3.common.PlaybackException
+import androidx.media3.common.Player
+import androidx.media3.common.util.UnstableApi
+import com.mux.stats.sdk.core.model.CustomData
+import com.mux.stats.sdk.core.model.CustomerData
+import com.mux.stats.sdk.core.model.CustomerVideoData
+import com.mux.stats.sdk.core.model.CustomerViewData
+import com.mux.stats.sdk.core.util.UUID
+import com.mux.player.MuxPlayer
+import com.mux.player.media.MediaItems
+import com.mux.player.media.PlaybackResolution
+import com.mux.player.media3.PlaybackIds
+import com.mux.player.media3.databinding.ActivityBasicPlayerBinding
+
+/**
+ * A simple example that uses the normal media3 player UI to play a video in the foreground from
+ * Mux Video, using a Playback ID
+ */
+class MaxResActivity : AppCompatActivity() {
+
+ private lateinit var binding: ActivityBasicPlayerBinding
+ private val playerView get() = binding.player
+
+ private var player: MuxPlayer? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding = ActivityBasicPlayerBinding.inflate(layoutInflater)
+ setContentView(binding.root)
+ }
+
+ override fun onStart() {
+ super.onStart()
+
+ playSomething()
+ }
+
+ override fun onStop() {
+ tearDownPlayer()
+
+ super.onStop()
+ }
+
+ private fun tearDownPlayer() {
+ playerView.player = null
+ player?.release()
+ }
+
+ private fun playSomething() {
+ val player = createPlayer(this)
+ val mediaItem = MediaItems.builderFromMuxPlaybackId(
+ PlaybackIds.TEARS_OF_STEEL,
+ maxResolution = PlaybackResolution.HD_720,
+ )
+ .setMediaMetadata(
+ MediaMetadata.Builder()
+ .setTitle("Basic MuxExoPlayer Example")
+ .build()
+ )
+ .build()
+ player.setMediaItem(mediaItem)
+ player.prepare()
+ player.playWhenReady = true
+
+ this.playerView.player = player
+ this.player = player
+ }
+
+ @OptIn(UnstableApi::class)
+ private fun createPlayer(context: Context): MuxPlayer {
+ val out: MuxPlayer = MuxPlayer.Builder(context)
+ .addMonitoringData(
+ CustomerData().apply {
+ customerViewData = CustomerViewData().apply {
+ viewSessionId = UUID.generateUUID()
+ }
+ customerVideoData = CustomerVideoData().apply {
+ videoSeries = "My Series"
+ videoId = "abc1234zyxw"
+ }
+ customData = CustomData().apply {
+ customData1 = "my custom metadata field"
+ customData2 = "another custom metadata field"
+ customData10 = "up to 10 custom fields"
+ }
+ }
+ )
+ .applyExoConfig {
+ // Call ExoPlayer.Builder methods here
+ setHandleAudioBecomingNoisy(true)
+ setSeekBackIncrementMs(10_000)
+ setSeekForwardIncrementMs(30_000)
+ }
+ .build()
+
+ out.addListener(object : Player.Listener {
+ override fun onPlayerError(error: PlaybackException) {
+ Log.e(TAG, "player error!", error)
+ Toast.makeText(
+ this@MaxResActivity,
+ "Playback error! ${error.localizedMessage}",
+ Toast.LENGTH_LONG
+ ).show()
+ }
+ })
+
+ return out
+ }
+
+ companion object {
+ val TAG = MaxResActivity::class.simpleName
+ }
+}
diff --git a/app/src/main/res/drawable/mux_logo.xml b/app/src/main/res/drawable/mux_logo.xml
new file mode 100644
index 00000000..a931f7cb
--- /dev/null
+++ b/app/src/main/res/drawable/mux_logo.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index adf2e57b..d373461e 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -6,10 +6,33 @@
android:layout_height="match_parent"
tools:context=".MainActivity">
+
+
+
+
diff --git a/app/src/main/res/layout/listitem_example.xml b/app/src/main/res/layout/listitem_example.xml
index 50176dec..2c9cfb1b 100644
--- a/app/src/main/res/layout/listitem_example.xml
+++ b/app/src/main/res/layout/listitem_example.xml
@@ -1,7 +1,8 @@
\ No newline at end of file
diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml
index f1c26087..eb71b2c4 100644
--- a/app/src/main/res/values-night/themes.xml
+++ b/app/src/main/res/values-night/themes.xml
@@ -2,15 +2,32 @@
-
\ No newline at end of file
+
+
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index f8c6127d..54b2cfaf 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -7,4 +7,19 @@
#FF018786
#FF000000
#FFFFFFFF
+
+
+ #FF00AA3C
+ #FF00802D
+ #FF005C20
+ #FFB2BAC2
+ #FF808C99
+ #FF565E67
+ #FF3E4247
+ #FF242628
+ #FF0A0A0B
+ #FFFB65D5
+ #FFFFFFFF
+
+ #FF1E1E1E
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index e922c55d..bd4d9f94 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -2,15 +2,33 @@
+
+
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index 7cf1462f..0932b142 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,8 +1,8 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
- id("com.android.application") version "8.1.0" apply false
+ id("com.android.application") version "8.1.3" apply false
id("org.jetbrains.kotlin.android") version "1.8.0" apply false
- id("com.android.library") version "8.1.0" apply false
+ id("com.android.library") version "8.1.3" apply false
id("com.mux.gradle.android.mux-android-distribution") version "1.1.2" apply false
}
\ No newline at end of file
diff --git a/library/build.gradle b/library/build.gradle
index ab34241f..f5d9add9 100644
--- a/library/build.gradle
+++ b/library/build.gradle
@@ -6,11 +6,12 @@ plugins {
}
android {
- namespace 'com.mux.video'
+ namespace 'com.mux.player'
compileSdk 34
defaultConfig {
minSdk 21
+ targetSdk 34
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
diff --git a/library/src/main/java/com/mux/player/MuxPlayer.kt b/library/src/main/java/com/mux/player/MuxPlayer.kt
index f356426c..0719b26e 100644
--- a/library/src/main/java/com/mux/player/MuxPlayer.kt
+++ b/library/src/main/java/com/mux/player/MuxPlayer.kt
@@ -7,9 +7,9 @@ import androidx.media3.exoplayer.ExoPlayer
import com.mux.stats.sdk.core.model.CustomerData
import com.mux.stats.sdk.muxstats.MuxStatsSdkMedia3
import com.mux.stats.sdk.muxstats.monitorWithMuxData
-import com.mux.player.internal.LogcatLogger
+import com.mux.player.internal.createLogcatLogger
import com.mux.player.internal.Logger
-import com.mux.player.internal.NoLogger
+import com.mux.player.internal.createNoLogger
import com.mux.player.media.MuxMediaSourceFactory
/**
@@ -93,9 +93,9 @@ class MuxPlayer private constructor(
@Suppress("unused")
fun enableLogcat(enableLogcat: Boolean): Builder {
logger = if (enableLogcat) {
- LogcatLogger()
+ createLogcatLogger()
} else {
- NoLogger()
+ createNoLogger()
}
return this
}
@@ -146,7 +146,7 @@ class MuxPlayer private constructor(
context = context,
exoPlayer = this.playerBuilder.build(),
muxDataKey = this.dataEnvKey,
- logger = logger ?: NoLogger(),
+ logger = logger ?: createNoLogger(),
initialCustomerData = customerData,
)
}
diff --git a/library/src/main/java/com/mux/player/internal/Logger.kt b/library/src/main/java/com/mux/player/internal/Logger.kt
index c0935026..1f88f005 100644
--- a/library/src/main/java/com/mux/player/internal/Logger.kt
+++ b/library/src/main/java/com/mux/player/internal/Logger.kt
@@ -18,13 +18,13 @@ internal interface Logger {
* Creates a new [Logger] that logs to Logcat
*/
@JvmSynthetic
-internal fun LogcatLogger(): Logger = DeviceLogger()
+internal fun createLogcatLogger(): Logger = DeviceLogger()
/**
* Creates a new [Logger] that doesn't log anything at all
*/
@JvmSynthetic
-internal fun NoLogger(): Logger = SilentLogger()
+internal fun createNoLogger(): Logger = SilentLogger()
private class SilentLogger: Logger {
override fun e(tag: String, message: String, exception: Exception?) { }
diff --git a/library/src/main/java/com/mux/player/media/MediaItems.kt b/library/src/main/java/com/mux/player/media/MediaItems.kt
index 01542515..776ef40d 100644
--- a/library/src/main/java/com/mux/player/media/MediaItems.kt
+++ b/library/src/main/java/com/mux/player/media/MediaItems.kt
@@ -33,12 +33,16 @@ object MediaItems {
@JvmOverloads
fun fromMuxPlaybackId(
playbackId: String,
- maxResolution: PlaybackMaxResolution? = null,
+ maxResolution: PlaybackResolution? = null,
+ minResolution: PlaybackResolution? = null,
+ renditionOrder: RenditionOrder? = null,
domain: String = MUX_VIDEO_DEFAULT_DOMAIN,
playbackToken: String? = null,
): MediaItem = builderFromMuxPlaybackId(
playbackId,
maxResolution,
+ minResolution,
+ renditionOrder,
domain,
playbackToken,
).build()
@@ -56,7 +60,9 @@ object MediaItems {
@JvmOverloads
fun builderFromMuxPlaybackId(
playbackId: String,
- maxResolution: PlaybackMaxResolution? = null,
+ maxResolution: PlaybackResolution? = null,
+ minResolution: PlaybackResolution? = null,
+ renditionOrder: RenditionOrder? = null,
domain: String = MUX_VIDEO_DEFAULT_DOMAIN,
playbackToken: String? = null,
): MediaItem.Builder {
@@ -66,6 +72,8 @@ object MediaItems {
playbackId = playbackId,
domain = domain,
maxResolution = maxResolution,
+ minResolution = minResolution,
+ renditionOrder = renditionOrder,
playbackToken = playbackToken,
)
)
@@ -79,23 +87,36 @@ object MediaItems {
playbackId: String,
domain: String = MUX_VIDEO_DEFAULT_DOMAIN,
subdomain: String = MUX_VIDEO_SUBDOMAIN,
- maxResolution: PlaybackMaxResolution? = null,
+ maxResolution: PlaybackResolution? = null,
+ minResolution: PlaybackResolution? = null,
+ renditionOrder: RenditionOrder? = null,
playbackToken: String? = null,
): String {
val base = Uri.parse("https://$subdomain.$domain/$playbackId.m3u8").buildUpon()
+ minResolution?.let { base.appendQueryParameter("min_resolution", resolutionValue(it)) }
maxResolution?.let { base.appendQueryParameter("max_resolution", resolutionValue(it)) }
+ renditionOrder?.let { base.appendQueryParameter("rendition_order", resolutionValue(it)) }
playbackToken?.let { base.appendQueryParameter("token", it) }
return base.build().toString()
}
- private fun resolutionValue(playbackMaxResolution: PlaybackMaxResolution): String {
- return when (playbackMaxResolution) {
- PlaybackMaxResolution.HD_720 -> "720p"
- PlaybackMaxResolution.FHD_1080 -> "1080p"
- PlaybackMaxResolution.QHD_1440 -> "1440p"
- PlaybackMaxResolution.FOUR_K_2160 -> "2160p"
+ private fun resolutionValue(renditionOrder: RenditionOrder): String {
+ return when (renditionOrder) {
+ RenditionOrder.Ascending -> "asc"
+ RenditionOrder.Descending -> "desc"
+ }
+ }
+
+ private fun resolutionValue(playbackResolution: PlaybackResolution): String {
+ return when (playbackResolution) {
+ PlaybackResolution.LD_480 -> "480p"
+ PlaybackResolution.LD_540 -> "540p"
+ PlaybackResolution.HD_720 -> "720p"
+ PlaybackResolution.FHD_1080 -> "1080p"
+ PlaybackResolution.QHD_1440 -> "1440p"
+ PlaybackResolution.FOUR_K_2160 -> "2160p"
}
}
}
@@ -104,9 +125,16 @@ object MediaItems {
* A resolution for playing back Mux assets. If specified in [MediaItems.fromMuxPlaybackId], or
* similar methods, the video's resolution will be limited to the given value
*/
-enum class PlaybackMaxResolution {
+enum class PlaybackResolution {
+ LD_480,
+ LD_540,
HD_720,
FHD_1080,
QHD_1440,
FOUR_K_2160,
}
+
+enum class RenditionOrder {
+ Ascending,
+ Descending,
+}
\ No newline at end of file