Maui
Maui copied to clipboard
Implement MediaElement
Description of Change
TBD
Linked Issues
- Fixes #113
PR Checklist
- [x] Has a linked Issue, and the Issue has been
approved(bug) orChampioned(feature/proposal) - [ ] Has tests (if omitted, state reason in description)
- [x] Has samples (if omitted, state reason in description)
- [ ] Rebased on top of
mainat time of PR - [ ] Changes adhere to coding standard
- [ ] Documentation created or updated: https://github.com/MicrosoftDocs/CommunityToolkit/pull/XYZ
Additional information
Can one of the maintainers take a look at this PR for Android MediaElement: https://github.com/xamarin/XamarinCommunityToolkit/pull/1913 before you port it to Maui toolkit? It's a fix for a critical bug that both xamarin and maui users can benefit from.
I'm also willing to fix this issue https://github.com/xamarin/XamarinCommunityToolkit/issues/408 (only for Android), but I am waiting for my previous PR to get merged to avoid annoying merge conflicts. If someone can fix it for iOS too, we can close that issue.
@anvuks don't worry about that, the implementation will be a new code here (if I'm not wrong), so there's no chance of conflicts. I'll take a look to your PR soon-sh, don't have the time to checkout and test on my end yet.
I am not 100% sure yet - still testing - but could anyone of you please have a look at the MediaElementState implementation for Android?
Here's the thing: I am using an ImageButton that shows a Pause icon when the stream is currently playing and a Play icon when it is paused. I implemented that behaviour with DataTriggers like this:
<ImageButton Grid.Row="2"
BindingContext="{x:Reference mediaElement}"
x:Name="playButton"
Clicked="OnPlayPauseButtonClicked"
HeightRequest="64"
HorizontalOptions="CenterAndExpand"
Source="player_pause.png"
VerticalOptions="Center"
WidthRequest="64">
<ImageButton.Triggers>
<DataTrigger
Binding="{Binding CurrentState}"
TargetType="ImageButton"
Value="{x:Static media:MediaElementState.Playing}">
<Setter Property="Source" Value="player_pause.png" />
</DataTrigger>
<DataTrigger
Binding="{Binding CurrentState}"
TargetType="ImageButton"
Value="{x:Static media:MediaElementState.Paused}">
<Setter Property="Source" Value="player_play.png" />
</DataTrigger>
</ImageButton.Triggers>
</ImageButton>
This works amazingly well on iOS. On Android however, the State seems to be always "Playing".
I looked at the code, and the Method PlatformUpdateStatus (MediaManager.android.cs) checks the players' PlaybackState which is always PlaybackStateCode.Playing (value 3) even when its paused. The funny thing is, I tried to get a deeper understanding of how ExoPlayer works internally and this state seems to actually refer to its inner Player.State which is like "STATE_READY" for play all the time... which is - coincidantally the value 3.
Maybe I am mixing something up here. All I can say that there's something wrong and I didn't figure it out on my own (yet).
UPDATE: It seems as if I am right. Referring to this post here: https://stackoverflow.com/questions/47731779/detect-pause-resume-in-exoplayer you cannot detect if the state is actually playing only by checking the PlaybackState. You also need to check the PlayWhenReady property.
I altered the code in my copy of this branch so that it now says:
switch ((PlaybackStateCode)player.PlaybackState)
{
case PlaybackStateCode.Playing:
videoStatus = player.PlayWhenReady ? MediaElementState.Playing : MediaElementState.Paused;
break;
Now everything works as expected.
Please remember that this is work in progress. One of the things that is very much in progress is the reporting of the states, so please don't focus on that as it will probably drastically change.
Additionally, I appreciate the PRs coming in, but please try to stick every change in a separate PR. As mentioned: the states will probably changed and now your PR is potentially blocked from merging because I do want the bugfix for the duration, but I don't want the change for the state.
I have my Tizen implementation for MediaElement now :)
Just wanted to drop a comment that I am unable to see the playback controls on iOS, even with ShowsPlaybackControls set to True. Thanks for all your hard work so far!
Just wanted to drop a comment that I am unable to see the playback controls on iOS, even with
ShowsPlaybackControlsset toTrue. Thanks for all your hard work so far!
What version of iOS? What version of this package?
Just wanted to drop a comment that I am unable to see the playback controls on iOS, even with
ShowsPlaybackControlsset toTrue. Thanks for all your hard work so far!What version of iOS? What version of this package?
iOS 16.2 Using these packages:
<PackageReference Include="CommunityToolkit.Maui" Version="[1.0.0-build-667.84928]" />
<PackageReference Include="CommunityToolkit.Maui.MediaElement" Version="[1.0.0-build-667.84928]" />
That's strange because on iOS 16.2 and the latest code in this PR it works fine for me.
That's strange because on iOS 16.2 and the latest code in this PR it works fine for me.
Have you tried when the MediaElement is inside a StackLayout? For example:
<StackLayout Orientation="Vertical" VerticalOptions="FillAndExpand">
<mediaElement:MediaElement x:Name="MediaElement" WidthRequest="300" HeightRequest="300" ShowsPlaybackControls="True" />
</StackLayout>
I am setting the Source in the code-behind file.
@AnthonyIacono this looks like something that will demand more time... Can you please open an issue or a discussion with a repro project?
@pictos I could, but before I do I just want to make sure that is what @jfversluis wishes. I fear that if I create a separate issue, it might just make it harder to track the changes.
I believe it will be easier to solve that on an issue than here. Where the flow and focus are on the code-review. But I'm ok to wait for Gerald's input
@AnthonyIacono With just this the playback controls show fine for me
<VerticalStackLayout>
<media:MediaElement
WidthRequest="300"
HeightRequest="300"
x:Name="mediaElement"
AutoPlay="True"
Source="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" />
</VerticalStackLayout>
Can you please provide a reproduction where you don't see the playback controls where they should be visible?
in toolkit:MediaPlayer ShouldShowPlaybackControls="True" blocks swipe gestures. Disabling playback controls remediates the issue.
in toolkit:MediaPlayer ShouldShowPlaybackControls="True" blocks swipe gestures. Disabling playback controls remediates the issue.
On which platform? Does that also happen on the platform without using .NET MAUI?
in toolkit:MediaPlayer ShouldShowPlaybackControls="True" blocks swipe gestures. Disabling playback controls remediates the issue.
On which platform? Does that also happen on the platform without using .NET MAUI?
Android only. Works as expected in Windows. And it is only affected when playback controls are true. Otherwise it works in android when playback controls disabled.
@ne0rrmatrix What I would be interested in is if this also happens on a Android project without .NET MAUI. I would assume it does. I can totally see how showing these controls will impact gestures. Either way, this will not be fixed with this merge. I can have a look at it later.
@ne0rrmatrix What I would be interested in is if this also happens on a Android project without .NET MAUI. I would assume it does. I can totally see how showing these controls will impact gestures. Either way, this will not be fixed with this merge. I can have a look at it later.
Sample repo to reproduce issue with comments showing mitigations. https://github.com/ne0rrmatrix/MediaPlayerBugSwipeGestures Sounds good. I have a minimal sample repo for this exact problem. I will investigate further. BTW how do we test this without using maui?
@ne0rrmatrix make sure to open an issue for it because we won't be monitoring this PR after it's done.
You can create a .NET 6 Android app by doing dotnet new android and you'll want to use the ExoPlayer bindings that I'm also using: https://github.com/Baseflow/ExoPlayerXamarin
@ne0rrmatrix make sure to open an issue for it because we won't be monitoring this PR after it's done.
You can create a .NET 6 Android app by doing
dotnet new androidand you'll want to use the ExoPlayer bindings that I'm also using: https://github.com/Baseflow/ExoPlayerXamarin
I have started an issue so it can be tracked. TY for the suggestion. I have created an android app and added the exoplayer nuget package. I have the video player working. But now I need to add a main page and then navigate to player. Then add swipe gesture to test. That should keep me busy till after it gets patched. TY for the help so far. I enjoy the challenge. I am learning a lot and I will do my best to verify if the issue exists in android with just exoplayer and xamarin and nothing else. Now I have to go learn xamarin enough to do the test.
@ne0rrmatrix make sure to open an issue for it because we won't be monitoring this PR after it's done. You can create a .NET 6 Android app by doing
dotnet new androidand you'll want to use the ExoPlayer bindings that I'm also using: https://github.com/Baseflow/ExoPlayerXamarinI have started an issue so it can be tracked. TY for the suggestion. I have created an android app and added the exoplayer nuget package. I have the video player working. But now I need to add a main page and then navigate to player. Then add swipe gesture to test. That should keep me busy till after it gets patched. TY for the help so far. I enjoy the challenge. I am learning a lot and I will do my best to verify if the issue exists in android with just exoplayer and xamarin and nothing else. Now I have to go learn xamarin enough to do the test.
@ne0rrmatrix make sure to open an issue for it because we won't be monitoring this PR after it's done.
You can create a .NET 6 Android app by doing
dotnet new androidand you'll want to use the ExoPlayer bindings that I'm also using: https://github.com/Baseflow/ExoPlayerXamarin
I checked the ExoPlayer github and gestures was listed as a closed issue. It is blocked by player controls and this is intended behavior. The only way for it to work would be to add a custom handler to implement it. I am thinking of just adjusting how my app works to deal with this. The media player that you are merging is not bugged. It is working as intended in android. I just wish i knew enough to implement the change myself. It is a new project for me now. I have to learn enough to implement the feature myself or hope someone here does it for me. I think doing it myself will be a good project for me to learn new things.
@ne0rrmatrix awesome investigation, thank you so much and that was kind of what I expected. Thank you for letting me know!
in toolkit:MediaPlayer ShouldShowPlaybackControls="True" blocks swipe gestures. Disabling playback controls remediates the issue.
On which platform? Does that also happen on the platform without using .NET MAUI?
Android only. Works as expected in Windows. And it is only affected when playback controls are true. Otherwise it works in android when playback controls disabled.
Can you try to put it in a Frame tag?
Custom Headers
Hello this is my fist time, this is my proporsal for custom headers request in Android.
MediaElement.shared.cs
/// <summary> /// Backing store for the <see cref="CustomHeaders"/> property. /// </summary> public static readonly BindableProperty CustomHeadersProperty = BindableProperty.Create(nameof(CustomHeaders), typeof(Dictionary<string, string>), typeof(MediaElement), null);
/// <inheritdoc cref="IMediaElement.CustomHeaders"/> public Dictionary<string, string> CustomHeaders { get => (Dictionary<string, string>)GetValue(CustomHeadersProperty); set => SetValue(CustomHeadersProperty, value); }
/// <summary> /// Gets or sets headers custom headers /// </summary> Dictionary<string,string> CustomHeaders { get; set; }
using Com.Google.Android.Exoplayer2.Source; using Com.Google.Android.Exoplayer2.Upstream;
if (MediaElement.Source is UriMediaSource uriMediaSource) { var uri = uriMediaSource.Uri; if (!string.IsNullOrWhiteSpace(uri?.AbsoluteUri)) { if (MediaElement.CustomHeaders != null) { var httpDataSoureFactory = new DefaultHttpDataSource.Factory(); httpDataSoureFactory.SetDefaultRequestProperties(MediaElement.CustomHeaders); httpDataSoureFactory.SetUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0"); var mediaSource = new DefaultMediaSourceFactory(httpDataSoureFactory).CreateMediaSource(MediaItem.FromUri(uri?.AbsoluteUri)); Player.SetMediaSource(mediaSource); Player.Prepare(); hasSetSource = true; } else { Player.SetMediaItem(MediaItem.FromUri(uri.AbsoluteUri)); Player.Prepare(); hasSetSource = true; } } }
I test this implementation woking fine. Maybe if Maui Team wants, i can add User Agent. P.D: Sorry for my English. @jfversluis
@Phantom-KNA please open a discussion and add your proposal there. This PR is already close, thanks