ryanheise / audio_service

Flutter plugin to play audio in the background while the screen is off.
803 stars 480 forks source link

Metadata in MediaItems? #132

Open hacker1024 opened 4 years ago

hacker1024 commented 4 years ago

Is your feature request related to a problem? Please describe. Due to having some local file:// urls as my mediaUri, (because of #131), I'd like to also be able to store the online URLs in my MediaItem for use with cached_network_image in my UI.

Describe the solution you'd like I propose we take advantage of the MediaItemDescription.Builder.setExtras and [MediaDescriptionCompat.getExtras()](https://developer.android.com/reference/android/support/v4/media/MediaDescriptionCompat.html#getExtras()) methods to store key-value pairs.

Describe alternatives you've considered Right now, I'm using the genre field to store the URL.

ryanheise commented 4 years ago

Seems like a good idea. I don't see any problem with adding an extras field to media item.

hacker1024 commented 4 years ago

Thanks. Also, can it support ratings as values? The Java RatingCompat class is parcelable, and the Rating class in this plugin is a direct copy of it.

hacker1024 commented 4 years ago

I've already written raw conversion functions for it, as they were needed to support the Android media rating APIs.

ryanheise commented 4 years ago

I don't have a good feel for the ratings code having not used it, but it seems that you've already wrote the code to put the rating into the extras on the Android side, so wouldn't this require no additional code to handle ratings in extras? On the Flutter side, I would reserve the use of extras just for any additional fields that are not among the predefined ones, but rating is a predefined one.

hacker1024 commented 4 years ago

Rating is predefined, but ratings are stored on my app's backend. I'd like to store a rating that's uploading in the extras bundle, while leaving the actuall rating field as the rating that's set at any given time.

hacker1024 commented 4 years ago

If you'd like, I can implement it myself?

ryanheise commented 4 years ago

I'd be happy to accept a pull request. So basically if we add an extras field to MediaItem, it should normally be restricted to data allowed by StandardMessageCodec. i.e. primitive types, strings, and lists/maps recursively built out of those primitives. What you're proposing is to add a special case where a Rating can also be encoded?

hacker1024 commented 4 years ago

Exactly. The functions already exist to break Rating objects down into primitive types.

ryanheise commented 4 years ago

OK, sounds good.

hacker1024 commented 4 years ago

I've run into an issue in the implementation.

This plugin uses MediaMetadataCompat, but MediaMetadataCompat.Builder has no setExtras method. Such a method only exists in MediaMedata.Builder.

Switching to the non-compat objects would work only on Lollipop and above.

hacker1024 commented 4 years ago

Unfortunately, as of May 7, over 6% of android devices still run versions older than Lollipop.

hacker1024 commented 4 years ago

It also just occurred to me that iOS may not have any feature like this at all, which means that this issue may not be easily solved.

As a result, we may need to rethink #131. Also, my rating usecase would be pretty much impossible.

ryanheise commented 4 years ago

I don't think a setExtras method is required to implement this. The way I would envisage it working is that each key/value pair in these extras can just translate to an individual builder.putX call in createMediaMetadata. In reverse, mediaMetadata2raw can enumerate the key set and detect any additional keys beyond the standard ones, and put them back into the extras.

hacker1024 commented 4 years ago

Yeah, I'm trying that. I though you couldn't put arbitrary keys into the metadata, but we'll see how it goes.

ryanheise commented 4 years ago

Hi @hacker1024

I've just implemented an extras field in MediaItem, currently supporting only integer and string values, and it seems to work. Since I don't have a use case for ratings myself, I thought I'd leave it to you to build on that base if you would like this feature to work with ratings, but it seems like it should be possible.

hacker1024 commented 4 years ago

Awesome, thanks! (Btw, I just started working on my app again after a rather long break, and I'm really impressed with how well you're handing all these open issues - it looks pretty overwhelming to me.)

ryanheise commented 4 years ago

Haha, I wouldn't say I'm keeping up, but I'm at least happy that the number of pull requests is growing with the scale of the project.

nt4f04uNd commented 3 years ago

I stumbled upon this issue and became interested in what is MediaDescriptionCompat. Is that basically an old version of MediaMetadataCompat from v4.media? If yes, I think it's safe to close this in favor of https://github.com/ryanheise/audio_service/issues/665

EDIT: Ahhh, it's like a view on MediaMetadataCompat, should have read this thoroughly

hacker1024 commented 3 years ago

I haven't touched native Android audio development since I switched to Flutter (aside from the small contributions I've made to this plugin here and there), and I'm unfamiliar with the Media v2 APIs. Is putting ratings in MediaItems still doable? If so, I think we should keep this issue open. I would like to implement it eventually...

nt4f04uNd commented 3 years ago

There's a putRating method which is a part of MediaMetadataCompat. #665 is about using all metadata keys, so addressing it would also mean we include the usage of METADATA_KEY_RATING and METADATA_KEY_USER_RATING. I think that https://github.com/ryanheise/audio_service/issues/665 has more details on what exactly we need to do and how.

ryanheise commented 3 years ago

I stumbled upon this issue and became interested in what is MediaDescriptionCompat. Is that basically an old version of MediaMetadataCompat from v4.media?

I admit when I created the early version of this plugin, I saw a whole bunch of similar classes and decided to roll them all into MediaItem for simplicity without caring too much about losing anything. There's yet another one called MediaBrowserCompat.MediaItem which is also rolled into my MediaItem, and its purpose is Android Auto.

But it is worth taking another look at this. I still want to keep everything rolled into MediaItem but on the Android side we just need to be careful with AudioService.mediaMetadataCache. I originally needed this since all callbacks were serviced through the media session callbacks and here we only got the MediaDescriptionCompat and I needed to recover the original MediaMetadataCompat (and may need this again once I add back in the ability for remote clients to connect to the media session). MediaDescriptionCompat has an extras property but as I recall, it didn't preserve all of the original data.

One other important difference is that when it comes to displaying artwork on the lock screen, you supply a high res bitmap (in memory) and store it in the MediaMetadataCompat, but I don't actually want this to go into the cache. So @nt4f04uNd with your recent pull request, the good thing is that this does not happen. You essentially make a clone of the original one that is in the cache, and attach the bitmap to the clone, then pass that clone into session.setMedatadata. In theory, that huge bitmap should be garbage collected when session.setMetadata is called again (although I do have my concerns about whether the media session manages that memory well.)