Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Releases/v0.7.0 #17

Merged
merged 8 commits into from
Jul 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,17 @@ android {
}

dependencies {
implementation project(':library')
implementation "com.mux.stats.sdk.muxstats:data-media3-ima:${project.version}"
implementation "com.mux.stats.sdk.muxstats:data-media3-custom:${project.version}"
implementation "com.mux.stats.sdk.muxstats:data-media3:${project.version}"

implementation "androidx.media3:media3-exoplayer:${media3Version}"
implementation "androidx.media3:media3-exoplayer-dash:${media3Version}"
implementation "androidx.media3:media3-exoplayer-hls:${media3Version}"
implementation "androidx.media3:media3-exoplayer-rtsp:${media3Version}"
implementation "androidx.media3:media3-session:${media3Version}"
implementation "androidx.media3:media3-ui:${media3Version}"
implementation "androidx.media3:media3-exoplayer-ima:${media3Version}"

implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.6.0'
Expand All @@ -52,3 +56,14 @@ dependencies {
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}

configurations.all {
resolutionStrategy.dependencySubstitution {
substitute module("com.mux.stats.sdk.muxstats:data-media3-custom") using project(":library")\
because "In development, we build from src"
substitute module("com.mux.stats.sdk.muxstats:data-media3") using \
project(":library-exo") because "In development, we build from src"
substitute module("com.mux.stats.sdk.muxstats:data-media3-ima") using \
project(":library-ima") because "In development, we build from src"
}
}
33 changes: 26 additions & 7 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
Expand All @@ -13,17 +17,32 @@
android:theme="@style/Theme.MuxDataSDKForMedia3"
tools:targetApi="31">
<activity
android:name=".PlayerActivity"
android:name=".examples.ima.ImaAdsActivity"
android:exported="false" />
<activity
android:name=".examples.background.BackgroundPlayActivity"
android:exported="false" />

<service
android:name=".examples.background.BackgroundPlayService"
android:enabled="true"
android:exported="true"
>
android:foregroundServiceType="mediaPlayback">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService" />
</intent-filter>
</service>

<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<category android:name="android.intent.category.LAUNCHER"/>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER" />

<action android:name="android.intent.action.MAIN" />
</intent-filter>
<!-- <meta-data-->
<!-- android:name="android.app.lib_name"-->
<!-- android:value="" />-->
</activity>
<activity android:name=".examples.basic.BasicPlayerActivity" />
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.mux.stats.muxdatasdkformedia3

object Constants {
const val MUX_DATA_ENV_KEY = "YOUR ENV KEY HERE"
const val VOD_TEST_URL_STEVE = "http://qthttp.apple.com.edgesuite.net/1010qwoeiuryfg/sl.m3u8"
const val VOD_TEST_URL_DRAGON_WARRIOR_LADY =
"https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8"
const val VOD_TEST_URL_BIG_BUCK_BUNNY = "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8"
const val AD_TAG_SIMPLE_W_MID_ROLL = "https://pubads.g.doubleclick.net/gampad/ads?iu=/21775744923/external/vmap_ad_samples&sz=640x480&cust_params=sample_ar%3Dpremidpostoptimizedpod&ciu_szs=300x250&gdfp_req=1&ad_rule=1&output=vmap&unviewed_position_start=1&env=vp&impl=s&cmsid=496&vid=short_onecue&correlator="
const val AD_TAG_COMPLEX = "https://pubads.g.doubleclick.net/gampad/ads?iu=/21775744923/external/vmap_ad_samples&sz=640x480&cust_params=sample_ar%3Dpremidpostlongpod&ciu_szs=300x250&gdfp_req=1&ad_rule=1&output=vmap&unviewed_position_start=1&env=vp&impl=s&cmsid=496&vid=short_onecue&correlator="
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package com.mux.stats.muxdatasdkformedia3

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.stats.muxdatasdkformedia3.databinding.ActivityMainBinding
import com.mux.stats.muxdatasdkformedia3.databinding.ListitemExampleBinding
import com.mux.stats.muxdatasdkformedia3.examples.background.BackgroundPlayActivity
import com.mux.stats.muxdatasdkformedia3.examples.basic.BasicPlayerActivity
import com.mux.stats.muxdatasdkformedia3.examples.ima.ImaAdsActivity

class MainActivity : AppCompatActivity() {

private lateinit var view: ActivityMainBinding

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
view = ActivityMainBinding.inflate(layoutInflater)
setContentView(view.root)
view.recycler.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
view.recycler.adapter = ExampleListAdapter(this, examples())
}

private fun examples() = listOf(
Example(
title = "Basic playback",
destination = Intent(this, BasicPlayerActivity::class.java)
),
Example(
title = "IMA Ads",
destination = Intent(this, ImaAdsActivity::class.java)
),
Example(
title = "Live Playback",
destination = Intent(this, BasicPlayerActivity::class.java).apply {
putExtra(
BasicPlayerActivity.EXTRA_URL,
"https://stream.mux.com/v69RSHhFelSm4701snP22dYz2jICy4E4FUyk02rW4gxRM.m3u8"
)
}
),
// TODO: post-beta, add APIs for talking to a `MediaSessionService`
// Example(
// title = "Background playback",
// destination = Intent(this, BackgroundPlayActivity::class.java)
// ),
)
}

data class Example(
val title: String,
val destination: Intent
)

class ExampleListAdapter(
private val context: Context,
private val examples: List<Example>
) : RecyclerView.Adapter<ExampleListAdapter.Holder>() {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
val viewBinding = ListitemExampleBinding.inflate(
LayoutInflater.from(context),
parent,
false
)
return Holder(
viewBinding = viewBinding,
itemView = viewBinding.root
)
}

override fun getItemCount(): Int = examples.size

override fun onBindViewHolder(holder: Holder, position: Int) {
val example = examples[position]
holder.viewBinding.exampleName.text = example.title
holder.itemView.setOnClickListener { context.startActivity(example.destination) }
}

class Holder(
val itemView: View,
val viewBinding: ListitemExampleBinding
) : RecyclerView.ViewHolder(itemView)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.mux.stats.muxdatasdkformedia3.examples.background

import android.content.ComponentName
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.provider.MediaStore.Audio.Media
import android.util.Log
import androidx.media3.common.MediaItem
import androidx.media3.session.MediaController
import androidx.media3.session.SessionToken
import com.google.common.util.concurrent.ListenableFuture
import com.google.common.util.concurrent.MoreExecutors
import com.mux.stats.muxdatasdkformedia3.databinding.ActivityBackgroundPlayBinding

class BackgroundPlayActivity : AppCompatActivity() {

private lateinit var view: ActivityBackgroundPlayBinding
private var controllerFuture: ListenableFuture<MediaController>? = null
private val mediaController: MediaController? get() = controllerFuture?.get()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
view = ActivityBackgroundPlayBinding.inflate(layoutInflater)
setContentView(view.root)
}

override fun onStart() {
super.onStart()

val sessionToken = SessionToken(this, ComponentName(this, BackgroundPlayService::class.java))
val controllerFuture = MediaController.Builder(this, sessionToken).buildAsync()
controllerFuture.addListener(
{
Log.i("BackgroundPlayExample", "new MediaController created!")
val controller = controllerFuture.get()!!
view.playerView.player = controller
if (!controller.playWhenReady) {
startPlaying(controller)
}
},
MoreExecutors.directExecutor()
)
this.controllerFuture = controllerFuture
}

override fun onStop() {
// Also releases the controller
controllerFuture?.let { MediaController.releaseFuture(it) }
controllerFuture = null

super.onStop()
}

private fun startPlaying(controller: MediaController) {
val mediaItem = MediaItem.Builder().setMediaId("bunny").build()
controller.setMediaItem(mediaItem)
controller.prepare()
controller.playWhenReady = true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package com.mux.stats.muxdatasdkformedia3.examples.background

import android.os.Bundle
import androidx.annotation.OptIn
import androidx.core.app.NotificationCompat
import androidx.media3.common.MediaItem
import androidx.media3.common.MediaMetadata
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.session.DefaultMediaNotificationProvider
import androidx.media3.session.MediaNotification
import androidx.media3.session.MediaSession
import androidx.media3.session.MediaSessionService
import androidx.media3.session.MediaStyleNotificationHelper
import androidx.media3.session.SessionCommand
import androidx.media3.session.SessionResult
import com.google.common.util.concurrent.Futures
import com.google.common.util.concurrent.ListenableFuture
import com.mux.stats.muxdatasdkformedia3.Constants
import com.mux.stats.sdk.muxstats.MuxStatsSdkMedia3

class BackgroundPlayService : MediaSessionService() {

private var mediaSession: MediaSession? = null
private val player: ExoPlayer? get() = mediaSession?.player as? ExoPlayer
private var muxStats: MuxStatsSdkMedia3<ExoPlayer>? = null

override fun onCreate() {
super.onCreate()
val player = createPlayer()
muxStats = monitorPlayer(player)
mediaSession = MediaSession.Builder(this, player)
.setCallback(createMediaSessionCallback())
.build()
}

override fun onDestroy() {
mediaSession?.run {
player.release()
release()
mediaSession = null
}
super.onDestroy()
}

private fun createMediaSessionCallback(): MediaSession.Callback {
return object : MediaSession.Callback {
override fun onAddMediaItems(
mediaSession: MediaSession,
controller: MediaSession.ControllerInfo,
mediaItems: MutableList<MediaItem>
): ListenableFuture<MutableList<MediaItem>> {
val resolvedMediaItems = mediaItems
.map { item ->
val playableVideo = findUrl(item.mediaId)
val mediaMetadata = playableVideo?.let {
MediaMetadata.Builder()
.setTitle(it.title)
.setDisplayTitle(it.title)
.build()
} ?: MediaMetadata.EMPTY
item.buildUpon()
.setUri(playableVideo?.url)
.setMediaMetadata(mediaMetadata)
.build()
}
.toMutableList()
player?.addMediaItems(resolvedMediaItems)
return Futures.immediateFuture(resolvedMediaItems)
} // onAddMediaItems

override fun onCustomCommand(
session: MediaSession,
controller: MediaSession.ControllerInfo,
customCommand: SessionCommand,
args: Bundle
): ListenableFuture<SessionResult> {
// TODO: Something here to parse these commands
return super.onCustomCommand(session, controller, customCommand, args)
}
} // object : MediaSession.Callback
}

override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? {
return mediaSession
}

private fun findUrl(videoId: String): PlayableVideo? {
return VIDEO_IDS.find { it.id == videoId }
}

private fun monitorPlayer(player: ExoPlayer): MuxStatsSdkMedia3<ExoPlayer>? {
return null // TODO: not null
}

private fun createPlayer(): ExoPlayer {
return ExoPlayer.Builder(this).build()
}

data class PlayableVideo(
val id: String,
val url: String,
val title: String,
)

companion object {
val VIDEO_IDS = listOf(
PlayableVideo("bunny", Constants.VOD_TEST_URL_BIG_BUCK_BUNNY, "Big Buck Bunny"),
PlayableVideo("steve", Constants.VOD_TEST_URL_STEVE, "Apple Keynote"),
PlayableVideo("sintel", Constants.VOD_TEST_URL_DRAGON_WARRIOR_LADY, "Sintel"),
)
}
}
Loading
Loading