I do have my own application for media playback - it's using MediaSessionCompat (with combination with ExoPlayer and it's MediaSessionConnector plugin).
On Samsung phone I experience a small issue with AOD (Always On Display) and lock screen. Both contains a small media controller (three buttons) and track title and album - I assume it works with MediaSession.
My problem is that it always shows Unknown / Unknown for title / album (but buttons are working correctly). I'm sure that I'm feeding correctly MediaSession metadata, as it is used in Activities, where using onMetadataChange callback and it contains correct title.
I'm somehow lost, not sure where to look for issue and fix. It's clearly in my app, because other players are working fine (showing title on AOD), but I do not know what else I need to do apart of settings metadata in MediaSession?
So shortly
Exoplayer uses MediaDescriptionCompat to get meta from play queue. Then it maps it to MediaMetadataCompat and title is mapped to key MEDIA_KEY_DISPLAY_TITLE , where Samsung is using key MEDIA_KEY_TITLE only.
Solution was to add MEDIA_KEY_TITLE to MediaDescriptionCompat.extras.
Another item displayed in Samsung AOD, lock screen is MediaMetadataCompat.METADATA_KEY_ARTIST
Another example of useless complexity in Android - why we need to two classes for metadata, which are almost the same MediaMetadataCompat vs MediaDescriptionCompat?
#Ivan's answer worked for me. Here is an example in Kotlin:
mediaSessionConnector = MediaSessionConnector(mediaSession).also {
it.setPlayer(player)
}
mediaSessionConnector.setQueueNavigator(object : TimelineQueueNavigator(mediaSession){
override fun getMediaDescription(player: Player, windowIndex: Int): MediaDescriptionCompat {
val extra = Bundle()
extra.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, "podcastEpisode")
return MediaDescriptionCompat.Builder().setExtras(extra).setTitle("podcastTitle").build()
}
})
This issue was also very helpful.
Related
I'm having trouble solving this bug and hope someone can guide me. I created the share screen function in android with WebRTC (EglBase). it works perfectly fine until the user stops sharing and then shares again, the participant's screen will be a black screen, and can't see the content.
I have already check the SurfaceViewRenderer is assigned correctly. I believe the problem may be on how I implemented the init() and release() functions.
In this function, I init the screen share video
EglBase root = EglBase.create();
screenShare.init(root.getEglBaseContext(), null);
screenShare.setZOrderMediaOverlay(true);
remoteParticipant.setView(screenShareVideoView);
In this function, I release the screen share
screenShare.clearImage();
screenShare.release();
screenShare.setVisibility(View.INVISIBLE);
Maybe I re-init the view after release() incorrectly.
I am using exo-player and media session extension like this.
mediaSessionCompat = new MediaSessionCompat(activity, activity.getPackageName());
MediaSessionConnector sessionConnector = new MediaSessionConnector(mediaSessionCompat);
sessionConnector.setPlayer(exoPlayer);
mediaSessionCompat.setActive(true);
When I enter into PIP window, it shows 3 control buttons (play/pause, skip-to-next, skip-to-previous). I only want the play/pause button and want to remove skip-to-next and skip-to-previous buttons.
How would I do that?
While I'm not extremely familiar with the way PIP displays supported actions, you may want to review precisely which actions your MediaSession is reporting.
For some background, I provided a snippet for declaring supported actions in my MediaSession codelab.
In your case, MediaSessionConnector seems to have discovered that there is a playback queue. You may want to instead review how the content of your queue is being provided into your ExoPlayer's media source.
There are two ways of validating that the MediaSession only contains the right actions:
Media Controller Test is mobile app that allows you to see which actions are reported and test them in a nifty UI.
You can instead inspect the output of adb shell dumpsys media_session:
Note that in this example of YouTube, the playback queue contains 5 items and therefore the action ACTION_SKIP_TO_NEXT is included in the bitwise mask:
For details about these action constants, I can recommend reading more about the actions as declared in PlaybackState.
Once you've confirmed that the MediaSession is reporting the right actions, the PIP display should no longer show skipping next and previous.
I'm working on an app where one part of the process is shooting a video, then uploading it. I'm using react-native-video to display the preview after the user has finished recording, and react-native-camera for the capturing process. I also use react-navigation to move between screens.
Currently I can get to the preview screen and set the video component's source uri from Redux. However, there is no player to be seen. The uri is in format "file:///path/video.mp4", so apparently it should be in the app cache as intended.
First the user is presented with a camera, where s/he can capture the video.
const recordVideo = async () => {
if (camera) {
const data = await camera.current.recordAsync()
if (data) {
dispatch(saveVideo(data)) <-- CONTAINS THE URI
navigation.navigate(CONFIRM)
}
}
When stopRecording() is called, the promise obviously resolves and the video's URI will be dispatched to Redux. Afterwards we navigate to the "confirmation screen", where the user can preview the video and choose whether to shoot another or go with this one.
My problem is, I can't get that preview video to play at all. I think I've tried pretty much everything within my power by now and I'm getting really tired of something so seemingly simple being so overly difficult to do. I've gotten the video to play a few times for some odd reason, so it's not the player's fault. At best what I've achieved is show the preview once, but when you go back and shoot another, there's no video preview anymore. Also, the "confirm" screen loads photos normally (that were taken in the same manner: camera -> confirm), but when it's the video's turn, it just doesn't work. The video component's onError handler also gives me this: {"error": {"extra": -2147483648, "what": 1}} which seems like just gibberish.
PS. yes, I've read through every related post here without finding a proper solution.
Use Exoplayer
Instead of using the older Media Player on Android, try using the more modern Exoplayer. If you're on React Native 0.60+, you can specify this in your react-native.config.js by doing the following:
module.exports = {
dependencies: {
"react-native-video": {
platforms: {
android: {
sourceDir: "../node_modules/react-native-video/android-exoplayer"
}
}
}
}
};
I was experiencing the same issue and this solution worked for us. Note, we're only supporting Android 5+ so not sure if this will work with devices older than that.
https://github.com/react-native-video/react-native-video/issues/1747#issuecomment-572512595
Having all sorts of fun trying to get the desired metadata, lockscreen and cast popup behaviour working in a music app that utilizes a custom receiver on Chromecast. Here's where I've got to..
private void _dummyRemotePlay(long id,String mUrl,String artist,String title,String iUrl) {
// CODE THAT CHANGES BEHAVIOUR
MediaTrack.Builder b = new MediaTrack.Builder(id,MediaTrack.TYPE_AUDIO);
b.setSubtype(MediaTrack.SUBTYPE_METADATA);
// END CODE THAT CHANGES BEHAVIOUR
MediaMetadata mdata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MUSIC_TRACK);
mdata.putString(MediaMetadata.KEY_ARTIST, artist);
mdata.putString(MediaMetadata.KEY_TITLE, title);
mdata.addImage(new WebImage(Uri.parse(iUrl)));
MediaInfo mi = new MediaInfo.Builder(mUrl)
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
.setContentType("audio/mpeg")
.setMetadata(mdata)
.build();
MediaQueueItem qi = new
MediaQueueItem.Builder(mi).setAutoplay(true).setPreloadTime(20).build();
MediaQueueItem[] qa = new MediaQueueItem[]{qi,qi}; // Two so SKIP_NEXT works...
_remoteMediaClient.queueLoad(qa,0,MediaStatus.REPEAT_MODE_REPEAT_OFF, null);}
Without the call to b.setSubType(MediaTrack.SUBTYPE_METADATA) the cast popup works (image, controls etc), a cast notification appears and the cast volume is adjusted by the volume keys. What doesn't work is the lockscreen metadata and controls. The artist is unknown and the controls do nothing.
With the b.setSubType call the lockscreen works fine (artist correctly displayed, controls work). However, there's no cast notification and the cast popup has no media selected (no image, no controls, just volume slider and end cast). Also the volume keys are not bound to cast volume.
Arrived at this point just playing around. Originally called b.build() to create a MediaTrack, added it to a list and called .setMediaTracks(list) on the MediaInfo.builder. Distilled it down to just the above though. The b.setSubType call on just a builder without a MediaTrack being built is enough to switch behaviour.
What I'm looking for is correct lockscreen behaviour, correct cast dialog image and control, volume keys working. I'm indifferent to a cast notification as the app has one anyway. It seems I can't achieve all of this.
I just wanted to know how to disable the youtube icon that comes in the YouTubeStandalonePlayer. Is it possible?
I don't want to take the user to youtube on clicking the icon. Is there any way to disable the youtube icon?
The below is the code how I am playing the the video using the YouTubeStandalonePlayer.
String videoId = getVideoId(cardsInfo.getCard_video_url());
Intent intent = YouTubeStandalonePlayer.createVideoIntent(getActivity(), getResources().getString(R.string.android_key), videoId, 0, true, false);
startActivity(intent);
According to the API, you can't.
But, you can try and use the other (not standalone) classes, which might give you better control as suggested in the docs: There are two ways to play videos. The first option is to place a YouTubePlayerFragment or YouTubePlayerView in your View hierarchy and then use the YouTubePlayer to control video playback in the View. **This gives a fine control of the experience**. If they don't let it, you could atleast have better control of the flow/UI-visibility because you are controling the Activity (in contrast of giving the control to the standalone activity)