Unable to add media item to the queue using Media Controller - android

I am trying to create a Podcast player. So following the Android Universal Media Player source code. The problem I am facing is, I do not know my playlist ahead of time, and the playlist creation is upon the user interaction. So I need to add/ remove Media Items from my Activity/ Fragment. My code is very alike the UMP sample app, also I have modified the MusicService's mediaSession
from
// Create a new MediaSession.
mediaSession = MediaSessionCompat(this, "MusicService")
.apply {
setSessionActivity(sessionActivityPendingIntent)
isActive = true
}
to
// Create a new MediaSession.
mediaSession = MediaSessionCompat(this, "MusicService")
.apply {
setSessionActivity(sessionActivityPendingIntent)
isActive = true
setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS
or MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS
or MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS)
}
And I am trying to Add an item to the queue like below
fun addRandomMedia() {
if (mediaSessionConnection.isConnected.value == true) {
val mediaDescription = MediaDescriptionCompat.Builder()
.setMediaId("wake_up_01")
.setTitle("Intro - The Way Of Waking Up (feat. Alan Watts)")
.setMediaUri(Uri.parse("https://storage.googleapis.com/uamp/The_Kyoto_Connection_-_Wake_Up/01_-_Intro_-_The_Way_Of_Waking_Up_feat_Alan_Watts.mp3"))
.build()
mediaSessionConnection.mediaController.addQueueItem(mediaDescription)
}
}
But I am always getting this below exception
Process: com.example.android.uamp.next, PID: 8446
java.lang.UnsupportedOperationException: This session doesn't support queue management operations
at android.support.v4.media.session.MediaControllerCompat$MediaControllerImplApi21.addQueueItem(MediaControllerCompat.java:1988)
at android.support.v4.media.session.MediaControllerCompat.addQueueItem(MediaControllerCompat.java:316)
at com.example.android.uamp.viewmodels.MainActivityViewModel.addItemToQueue(MainActivityViewModel.kt:118)
at com.example.android.uamp.MainActivity$onCreate$3.onClick(MainActivity.kt:68)
at android.view.View.performClick(View.java:6294)
at android.view.View$PerformClick.run(View.java:24770)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
Here is the addQueueItem code form support v4 media package
#Override
public void addQueueItem(MediaDescriptionCompat description) {
long flags = getFlags();
if ((flags & MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS) == 0) {
throw new UnsupportedOperationException(
"This session doesn't support queue management operations");
}
Bundle params = new Bundle();
params.putParcelable(COMMAND_ARGUMENT_MEDIA_DESCRIPTION, description);
sendCommand(COMMAND_ADD_QUEUE_ITEM, params, null);
}
So, I feel must be issues with getFlags method or I am missing something here, as the if check is always true.
To reproduce this issue, I have forked the sample and added the above code in the app. Here is the full code link
Here is the code to reproduce

Are u using MediaSessionConnector?
The problem is the flags are overridden in constructor of MediaSessionConnector.
So u need to call mediaSession.setFlags(flags) after creating an instance of MediaSessionConnector.
mediaSessionConnector = MediaSessionConnector(mediaSession).also {
...
...
it.mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS
or MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS
or MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS)
}

Related

Adding MediaItem when using the media3 library caused an error

I am using the latest Android Media3 library, but I found a problem in using it...
I created a MediaSessionService, and then got the MediaController in the Activity, and then when I tried to call the media controller and add some MediaItems, an error occurred:
java.lang.NullPointerException
at androidx.media3.common.util.Assertions.checkNotNull(Assertions.java:155)
at androidx.media3.exoplayer.source.DefaultMediaSourceFactory.createMediaSource(DefaultMediaSourceFactory.java:338)
at androidx.media3.exoplayer.ExoPlayerImpl.createMediaSources(ExoPlayerImpl.java:1164)
at androidx.media3.exoplayer.ExoPlayerImpl.addMediaItems(ExoPlayerImpl.java:463)
at androidx.media3.exoplayer.SimpleExoPlayer.addMediaItems(SimpleExoPlayer.java:1146)
at androidx.media3.common.BasePlayer.addMediaItems(BasePlayer.java:69)
at androidx.media3.common.BasePlayer.addMediaItem(BasePlayer.java:64)
at androidx.media3.common.ForwardingPlayer.addMediaItem(ForwardingPlayer.java:90)
at androidx.media3.session.PlayerWrapper.addMediaItem(PlayerWrapper.java:346)
at androidx.media3.session.MediaSessionStub.lambda$addMediaItem$28(MediaSessionStub.java:1052)
at androidx.media3.session.MediaSessionStub$$ExternalSyntheticLambda8.run(Unknown Source:2)
at androidx.media3.session.MediaSessionStub.lambda$getSessionTaskWithPlayerCommandRunnable$2$androidx-media3-session-MediaSessionStub(MediaSessionStub.java:234)
at androidx.media3.session.MediaSessionStub$$ExternalSyntheticLambda52.run(Unknown Source:14)
at androidx.media3.session.MediaSessionStub.lambda$flushCommandQueue$50(MediaSessionStub.java:1479)
at androidx.media3.session.MediaSessionStub$$ExternalSyntheticLambda58.run(Unknown Source:2)
at androidx.media3.common.util.Util.postOrRun(Util.java:517)
at androidx.media3.session.MediaSessionStub.flushCommandQueue(MediaSessionStub.java:1473)
at androidx.media3.session.MediaControllerImplBase$FlushCommandQueueHandler.handleMessage(MediaControllerImplBase.java:3035)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7813)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
So I checked the createMediaSource function of DefaultMediaSourceFactory and found that it is checking whether the localConfiguration of MediaItem is null:
#Override
public MediaSource createMediaSource(MediaItem mediaItem) {
checkNotNull(mediaItem.localConfiguration);
...
}
And this is localConfiguration:
/**
* Optional configuration for local playback. May be {#code null} if shared over process
* boundaries.
*/
#Nullable public final LocalConfiguration localConfiguration;
I am pretty sure that there is no problem with the way I created the MediaItem, and it works well inside the Service, but when I try to insert the MediaItem in the Activity, an error occurs. According to the comments, I guess this may be a cross-process communication problem, but I don't have any clue about this. Does anyone have experience with Media3?
When you add/set MediaItems from a controller, the localConfiguration (uri, mimeType, drm config, etc) of MediaItem is removed for security/privacy reasons. Without localConfiguration the player can't play the media item. We need to add the missing information back to the MediaItem.
Updated answer (media3 1.0.0-beta01 or higher)
Open the Callback you defined when creating the MediaLibrarySession in your Service.
// My MediaLibraryService
// onCreate()
mediaLibrarySession = MediaLibrarySession.Builder(
this,
player,
librarySessionCallback // <--
).build()
// NOTE: If you are using MediaSessionService instead of MediaLibraryService,
// use `setCallback(librarySessionCallback)` from the MediaSession.Builder.
Override onAddMediaItems inside your MediaLibrarySession.Callback. Every time you use setMediaItem/addMediaItem from a controller, your onAddMediaItems will be called and the MediaItems returned there are the ones that will be played.
class CustomMediaLibrarySessionCallback : MediaLibraryService.MediaLibrarySession.Callback {
// [...]
override fun onAddMediaItems(
mediaSession: MediaSession,
controller: MediaSession.ControllerInfo,
mediaItems: MutableList<MediaItem>
): ListenableFuture<List<MediaItem>> {
// NOTE: You can use the id from the mediaItems to look up missing
// information (e.g. get URI from a database) and return a Future with
// a list of playable MediaItems.
// If your use case is really simple and the security/privacy reasons
// mentioned earlier don't apply to you, you can add the URI to the
// MediaItem request metadata in your activity/fragment and use it
// to rebuild the playable MediaItem.
val updatedMediaItems = mediaItems.map { mediaItem ->
mediaItem.buildUpon()
.setUri(mediaItem.requestMetadata.mediaUri)
.build()
}
return Futures.immediateFuture(updatedMediaItems)
}
}
Create and play your MediaItem from the activity/fragment.
// My Activity
val mmd = MediaMetadata.Builder()
.setTitle("Example")
.setArtist("Artist name")
.build()
// Request metadata. New in (1.0.0-beta01)
// This is optional. I'm adding a RequestMetadata to the MediaItem so I
// can get the mediaUri from my `onAddMediaItems` simple use case (see
// onAddMediaItems for more info).
// If you are going to get the final URI from a database, you can move your
// query to your `MediaLibrarySession.Callback#onAddMediaItems` and skip this.
val rmd = RequestMetadata.Builder()
.setMediaUri("...".toUri())
.build()
val mediaItem = MediaItem.Builder()
.setMediaId("123")
.setMediaMetadata(mmd)
.setRequestMetadata(rmd)
.build()
browser.setMediaItem(mediaItem)
browser.prepare()
browser.play()
Old answer (media3 1.0.0-alpha)
When you create the MediaLibrarySession inside your MediaLibraryService, you can add a MediaItemFiller. This MediaItemFiller has a fillInLocalConfiguration method that will be "Called to fill in the MediaItem.localConfiguration of the media item from controllers."
Knowing this, you need to:
Add a MediaItemFiller to your MediaLibrarySession builder inside your service.
// My MediaLibraryService
// onCreate()
mediaLibrarySession = MediaLibrarySession.Builder(this, player, librarySessionCallback)
.setMediaItemFiller(CustomMediaItemFiller()) // <--
.setSessionActivity(pendingIntent)
.build()
Create a custom MediaSession.MediaItemFiller. Any time you use a setMediaItem/addMediaItem from a controller this will be called and the MediaItem returned here will be the one played.
class CustomMediaItemFiller : MediaSession.MediaItemFiller {
override fun fillInLocalConfiguration(
session: MediaSession,
controller: MediaSession.ControllerInfo,
mediaItem: MediaItem
): MediaItem {
// Return the media item to be played
return mediaItem.buildUpon()
// Use the metadata values to fill our media item
.setUri(mediaItem.mediaMetadata.mediaUri)
.build()
}
}
And finally, create and play your MediaItem from the activity.
// My Activity
// Fill some metadata that the MediaItemFiller
// will use to create the new MediaItem
val mmd = MediaMetadata.Builder()
.setTitle("Example")
.setArtist("Artist name")
.setMediaUri("...".toUri())
.build()
val mediaItem: MediaItem =
MediaItem.Builder()
.setMediaMetadata(mmd)
.build()
browser.setMediaItem(mediaItem)
browser.prepare()
browser.play()
I don't know why it has to be this awkward, but if you have a look to the CustomMediaItemFiller they use in the official repo, you will see that they use the mediaItem.mediaId to fetch a valid MediaItem from a media catalog. That's why their demo works when they use setMediaItem from an activity.
Also, as far as I know, anything you do inside fillInLocalConfiguration has to block the main thread (I believe setMediaItem has to be called from main) so, if you can, try to move any heavy work (ie, get media info from your database) to your Activity/ViewModel where you have more control, fill all the metadata you need there, and use your MediaSession.MediaItemFiller to do a simple transformation. Or move everything to your service and forget about everything.
I hope the flow is understood. I don't have much experience with media3 and maybe I'm missing something, but with the limitations of MediaItemFiller I found it a bit useless and I would really like to know more about its purpose.

NPE in 'android.media.session.ISession.getController()'

I've noticed this log in my crashlytics:
Caused by java.lang.NullPointerException: Attempt to invoke interface method 'android.media.session.ISessionController android.media.session.ISession.getController()' on a null object reference
at android.media.session.MediaSession.<init>(MediaSession.java:199)
at android.support.v4.media.session.MediaSessionCompat$MediaSessionImplApi29.createFwkMediaSession(MediaSessionCompat.java:4457)
at android.support.v4.media.session.MediaSessionCompat$MediaSessionImplApi21.<init>(MediaSessionCompat.java:3821)
at android.support.v4.media.session.MediaSessionCompat$MediaSessionImplApi22.<init>(MediaSessionCompat.java:4405)
at android.support.v4.media.session.MediaSessionCompat$MediaSessionImplApi28.<init>(MediaSessionCompat.java:4422)
at android.support.v4.media.session.MediaSessionCompat$MediaSessionImplApi29.<init>(MediaSessionCompat.java:4447)
at android.support.v4.media.session.MediaSessionCompat.<init>(MediaSessionCompat.java:576)
at android.support.v4.media.session.MediaSessionCompat.<init>(MediaSessionCompat.java:539)
at android.support.v4.media.session.MediaSessionCompat.<init>(MediaSessionCompat.java:503)
at android.support.v4.media.session.MediaSessionCompat.<init>(MediaSessionCompat.java:477)
at com.mypackage.API21VersionFactory.getMediaSessionCompat(API21VersionFactory.kt:16)
at com.mypackage.AudioPlayerService.onCreate(AudioPlayerService.kt:97)
at android.app.ActivityThread.handleCreateService(ActivityThread.java:4319)
at android.app.ActivityThread.access$1500(ActivityThread.java:263)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2010)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:240)
at android.app.ActivityThread.main(ActivityThread.java:8000)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:603)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
When trying create Audio notification style within my AudioService (an Android Service which plays audio files).
I'm building that notification in this way:
NotificationCompat.Builder(this, versionFactory.getAudioPlayerNotificationChannelId(this))
.setSmallIcon(R.drawable.ic_icon3)
.setColor(ContextCompat.getColor(this, R.color.color_2))
.setColorized(true)
.setCategory(NotificationCompat.CATEGORY_TRANSPORT)
.setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.cover_art))
.setSubText(getString(R.string.audio))
.apply {
mediaSessionCompat?.let {
setStyle(
androidx.media.app.NotificationCompat.MediaStyle()
.setMediaSession(it.sessionToken)
.setShowActionsInCompactView(0)
)
}
}
In order to get my mediaSessionCompat, I've declared a factory of android versions, so I get it this way:
open class API21VersionFactory : DefaultAndroidVersionFactory() {
...
override fun getMediaSessionCompat(context: Context) =
MediaSessionCompat(context, "TAG")
.apply {
setMetadata(
MediaMetadataCompat.fromMediaMetadata(
MediaMetadata.Builder()
.putLong(MediaMetadata.METADATA_KEY_DURATION, -1)
.build()
)
)
}
...
I'm not being able to replicate it because in every test I do I can play audios and notification is visible (and AudioService has been created successfully).
What's wrong in my code? What is the right way to prevent this and play audios within a Services in versions above API 21?
Thanks in advance!

How to put media buttons on the lockscreen with ExoPlayer

I am using ExoPlayer (https://github.com/google/ExoPlayer) and custom notifications.
I want to access my music player from lock screen and headphone like in google play music and wync.
Please help me.
For playback controls on the lock screen you need to do a MediaStyle notification.
If you want to have an artwork as the lockscreen background you need to support MediaSession and maintain the metadata of the session properly:
new MediaMetadata.Builder(track)
.putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, bitmap)
.putBitmap(MediaMetadata.METADATA_KEY_DISPLAY_ICON, bitmap)
.build();
I am also using Exoplayer with PlayerNotificationManager, And I used MediaSessionConnector and TimelineQueueNavigator to build notifications for the lock screen and background image for the lock screen.
Here, is my question with the Exoplayer team which is resolved for android 11 and above, regarding how to use MediaSessionConnector:
Why PlayerNotificationManager not showing Notification on startForeground in Android 11(R)?
Now, I just added a bitmap (using putParcelable()) for the current session track for MediaDescriptionCompact which will be set to the current MediaSession internally.
Here is the code for that:
val mediaSession = MediaSessionCompat(serviceContext, "DPS_APP")
mediaSession.isActive = true
mediaSessionConnector = MediaSessionConnector(mediaSession).also {
it.setQueueNavigator(
object : TimelineQueueNavigator(mediaSession) {
override fun getMediaDescription(
player: Player,
windowIndex: Int
): MediaDescriptionCompat {
val data: MediaMetaData =
getEmptyOfNullMedia(player)
isBitmapAvailable(getCurrentMediaArt(data))
val extras = Bundle().apply {
putString(
MediaMetadataCompat.METADATA_KEY_TITLE,
getCurrentTitle(data)
)
putString(
MediaMetadataCompat.METADATA_KEY_ARTIST,
getMediaTitle(data)
)
putParcelable(
MediaMetadataCompat.METADATA_KEY_ALBUM_ART,
sessionCurrentBitmap
)
}
return MediaDescriptionCompat.Builder()
.setIconBitmap(sessionCurrentBitmap)
.setExtras(extras)
.build()
}
it.setPlayer(mPlayer)
}
Here is a small brief on that how to refresh or invalidate MediaSession in the given below issue on GitHub:
https://github.com/google/ExoPlayer/issues/5494

How to integrate Google Cast v3 with ExoPlayer v2?

How to fully integrate Google Cast v3 with ExoPlayer v2? The activity will contain a FrameLayout with a com.google.android.exoplayer2.ui.SimpleExoPlayerView in it. The Google tutorial only covers integration with VideoView.
The code below is available in a Kotlin class in this Gist that should help people trying to set up their CastPlayer for the first time:
https://gist.github.com/stefan-zh/fd52e0ee06088ac4086d2ea3fb7d7f3e
Also, going through this tutorial from Google will help you: https://codelabs.developers.google.com/codelabs/cast-videos-android/index.html#0
I also used this tutorial to get started: https://android.jlelse.eu/sending-media-to-chromecast-has-never-been-easier-c331eeef1e0a
Here is a breakdown how to achieve this using ExoPlayer and its Cast extension.
1. You will need these dependencies:
// ExoPlayer is an advanced media player for playing media files
implementation "com.google.android.exoplayer:exoplayer-core:$exoplayer_version"
implementation "com.google.android.exoplayer:exoplayer-ui:$exoplayer_version"
implementation "com.google.android.exoplayer:extension-cast:$exoplayer_version"
2. You will need the Cast button
The Cast button can be added in the options menu for the activities. This is the recommended way to do it.
Add the following to res/menu/browse.xml (in my case the menu file is called browse.xml):
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" >
<item
android:id="#+id/media_route_menu_item"
android:title="#string/media_route_menu_title"
app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
app:showAsAction="always"/>
</menu>
Then add the following code to your Activity to enable the castButton:
/**
* We need to populate the Cast button across all activities as suggested by Google Cast Guide:
* https://developers.google.com/cast/docs/design_checklist/cast-button#sender-cast-icon-available
*/
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
val result = super.onCreateOptionsMenu(menu)
menuInflater.inflate(R.menu.browse, menu)
castButton = CastButtonFactory.setUpMediaRouteButton(applicationContext, menu, R.id.media_route_menu_item)
return result
}
3. Declare your Options Provider for the Cast context
You need this so that you get the options dialog with the list of devices that you can cast to. Add this to your AndroidManifest.xml in the application tag:
<meta-data
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="com.google.android.exoplayer2.ext.cast.DefaultCastOptionsProvider" />
4. Your Activity needs to implement ExoPlayer's Cast Extension interface SessionAvailabilityListener
This interface will allow you to listen for changes in the Cast session availability. Based on whether a Cast session is available you can direct playback to the local player or the remote player.
override fun onCastSessionAvailable() {
playOnPlayer(castPlayer)
}
override fun onCastSessionUnavailable() {
playOnPlayer(exoPlayer)
}
5. You will need logic to initialize the players:
Notice how we are calling castPlayer?.setSessionAvailabilityListener(this) where this refers to your Activity that implements the SessionAvailabilityListener interface. The listener's methods will be called when the Cast session availability changes.
private fun initializePlayers() {
exoPlayer = SimpleExoPlayer.Builder(this).build()
playerView.player = exoPlayer
if (castPlayer == null) {
castPlayer = CastPlayer(castContext)
castPlayer?.setSessionAvailabilityListener(this)
}
// start the playback
if (castPlayer?.isCastSessionAvailable == true) {
playOnPlayer(castPlayer)
} else {
playOnPlayer(exoPlayer)
}
}
6. You need logic to play on the selected player:
This method allows you to store the playback state (playbackPosition, playWhenReady, or windowIndex)
Create the correct media type for the local or remote players
Select which player should start playback
playOnPlayer() method:
private fun playOnPlayer(player: Player?) {
if (currentPlayer == player) {
return
}
// save state from the existing player
currentPlayer?.let {
if (it.playbackState != Player.STATE_ENDED) {
it.rememberState()
}
it.stop(true)
}
// set the new player
currentPlayer = player
// set up the playback
// if the current player is the ExoPlayer, play from it
if (currentPlayer == exoPlayer) {
// build the MediaSource from the URI
val uri = Uri.parse(videoClipUrl)
val dataSourceFactory = DefaultDataSourceFactory(this#SampleCastingPlayerActivity, "exoplayer-agent")
val mediaSource = ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(uri)
// use stored state (if any) to resume (or start) playback
exoPlayer?.playWhenReady = playWhenReady
exoPlayer?.seekTo(currentWindow, playbackPosition)
exoPlayer?.prepare(mediaSource, false, false)
}
// if the current player is the CastPlayer, play from it
if (currentPlayer == castPlayer) {
val metadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE)
metadata.putString(MediaMetadata.KEY_TITLE, "Title")
metadata.putString(MediaMetadata.KEY_SUBTITLE, "Subtitle")
metadata.addImage(WebImage(Uri.parse("any-image-url")))
val mediaInfo = MediaInfo.Builder(videoClipUrl)
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
.setContentType(MimeTypes.VIDEO_MP4)
.setMetadata(metadata)
.build()
val mediaItem = MediaQueueItem.Builder(mediaInfo).build()
castPlayer?.loadItem(mediaItem, playbackPosition)
}
}
7. Remember state and clean up resources
Each time you switch your application between background or foreground you would need to release or request resources. Each time you release the Player's resources back to the system you would need to save its state.
/**
* Remembers the state of the playback of this Player.
*/
private fun Player.rememberState() {
this#SampleCastingPlayerActivity.playWhenReady = playWhenReady
this#SampleCastingPlayerActivity.playbackPosition = currentPosition
this#SampleCastingPlayerActivity.currentWindow = currentWindowIndex
}
/**
* Releases the resources of the local player back to the system.
*/
private fun releaseLocalPlayer() {
exoPlayer?.release()
exoPlayer = null
playerView.player = null
}
/**
* Releases the resources of the remote player back to the system.
*/
private fun releaseRemotePlayer() {
castPlayer?.setSessionAvailabilityListener(null)
castPlayer?.release()
castPlayer = null
}
Google Cast SDK is independent of Local Player, you can use ExoPlayer or MediaPlayer ( VideoView )
Once your APP has an active session, place the url in MediaInfo
val movieMetadata = MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE)
movieMetadata.putString(MediaMetadata.KEY_TITLE, "Title")
movieMetadata.putString(MediaMetadata.KEY_SUBTITLE, "Sub")
val mediaLoadOptions = MediaInfo.Builder( < URL > )
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
.setContentType(< Content Type of Media>)
.setMetadata(movieMetadata)
.setStreamDuration(<Media Duration >)
.build()
mCastSession.remoteMediaClient.load(buildMediaInfo(url), mediaLoadOptions)
If you need to stream a local media, you will need to stream it for yourself, using NanoHttpd or another of your choice, and also implement a Cast Receiver

Unable to play live stream on chromecast from android sender app

I am using CastCompanionLibrary-android in my app and following CastVideos-android to play live streams on chromecast. Now the streaming of video works fine on my local player but when it comes to cast that video, it wont play. Rather it just show me my registered receiver app name and on sender app the VideoCastControllerActivity opens with only a loader which wont end.
I have registered both my receiver app (Styled Media Receiver) and the device on Google chrome cast console. Also, I tried to debug the receiver app, it show nothing on the debugger or in console.
Here is the code snippet of my sender app:
private void initMediaRouter() {
BaseCastManager.checkGooglePlayServices(this);
mCastManager = VideoCastManager.getInstance();
MediaMetadata mediaMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE); //also tried MediaMetadata.MEDIA_TYPE_GENERIC
mediaMetadata.putString(MediaMetadata.KEY_TITLE, channel.getChannelName());
mediaMetadata.putString(MediaMetadata.KEY_SUBTITLE, channel.getCatName());
info = new MediaInfo.Builder(channel.getHttpStream())
.setMetadata(mediaMetadata)
.setContentType("Video/*")
.setStreamType(MediaInfo.STREAM_TYPE_LIVE)
.build();
castConsumer = new CastConsumer();
mCastManager.addVideoCastConsumer(castConsumer);
}
this function is called on the player activity's OnCreate() function.
The Listener (I'm getting true value for wasLaunched param)
private class CastConsumer extends VideoCastConsumerImpl {
#Override
public void onApplicationConnected(ApplicationMetadata appMetadata, String sessionId, boolean wasLaunched) {
mCastManager.startVideoCastControllerActivity(PlayerActivity.this, info, 0, true);
}
}
And in the onCreate() function of Application:
String applicationId = getString(R.string.cast_app_id);
CastConfiguration options = new CastConfiguration.Builder(applicationId)
.enableAutoReconnect()
.enableCaptionManagement()
.enableDebug()
.enableLockScreen()
.enableNotification()
.enableWifiReconnection()
.setCastControllerImmersive(true)
.setLaunchOptions(false, Locale.getDefault())
.addNotificationAction(CastConfiguration.NOTIFICATION_ACTION_PLAY_PAUSE, true)
.addNotificationAction(CastConfiguration.NOTIFICATION_ACTION_DISCONNECT, true)
.build();
VideoCastManager.initialize(this, options);
can you please tell me where i am going wrong? Thanks.
The content type might be an issue. Try properly setting it to something like video/mp4 if that's the case.

Categories

Resources