Open jonmdev opened 4 weeks ago
This all makes sense and looks good to me! I'll add it to the agenda for our next standup to bring it to the group.
I'm no Android expert, so I have a few curious questions:
TextureView
better than SurfaceView
in every way? TextureView
the default implementation? SurfaceView
after implementing TextureView
?This all makes sense and looks good to me! I'll add it to the agenda for our next standup to bring it to the group.
I'm no Android expert, so I have a few curious questions:
Is
TextureView
better thanSurfaceView
in every way? Should we makeTextureView
the default implementation? Is there any reason to keepSurfaceView
after implementingTextureView
?
Great! Thanks. SurfaceView is the intentional default of ExoPlayer because it is more efficient for playback. In order for TextureView to have the opacity and overlapping behaviors, it functions behind the scenes by essentially copying the video playback onto a regular view. This lets it behave like a regular view but at the cost of copying the data each frame.
So Android recommends using SurfaceView in all typical circumstances, and TextureView only when overlapping or opacity or other features are needed. This is discussed a bit more in these threads and articles for example:
So it would be most desirable to keep SurfaceView as default. But adding the TextureView option takes nothing away from anyone, and as noted requires very little code from what I can see to make work with the already existing system design.
Adding the optional constructor to specify building as TextureView will only increase the feature-set in Android and allow us to do a lot of things we can't now. For example, I am stuck because I need overlapping videos which won't currently cooperate under SurfaceView. It will be quite brutal to build an entire new VideoPlayer class when this one is already running well and just needs a few lines of code to add this option.
I am happy to do any further work if needed to solve any issues but I think it should be pretty straightforward. Thanks again for your support on this.
This is a great idea. I fully support it! @jonmdev has a good idea and I am happy to help if any is needed. I agree that keeping surfaceview as default is recommended. Having an enum seems like a good idea as we may want to depending on circumstances have more options in the future. Not sure what but keeping our options open is a good idea.
As a further update, while I believe I have been able to think this proposal through as far as I can, I do not have the expertise and knowledge to manage the actual implementation. I have never contributed directly to a project or library like this and I am not a high level pro. Eg. I have never built a NuGet package or tested one.
So I would hope someone from the project with more experience than me could try the implementation.
But I am not trying to be lazy, so I have written the most detailed workflow I can think through for the implementation here which follows.
An XML file like "textureview.xml" must be added to Android Resources folder in the Community Toolkit project such that it will be packed into the NuGet with everything and still accessible to the following Community Toolkit code. File would contain:
<?xml version="1.0" encoding="utf-8" ?>
<com.google.android.exoplayer2.ui.StyledPlayerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:surface_type="texture_view" />
Community Toolkit namespace would need the following enum and class added to it:
public enum AndroidSurfaceType {
SurfaceView,
TextureView
}
public class MediaElementBuildParameters {
public AndroidSurfaceType androidSurfaceType = AndroidSurfaceType.SurfaceView;
//using class like this to wrap the enum allows us to add future constructor parameters later without changing MediaElement constructor API
}
So far as I can tell, there is no MediaElement constructor currently. One could be added with an optional parameter input like:
private MediaElementBuildParameters buildParameters = null;
public MediaElement(MediaElementBuildParameters buildParameters = null) {
// or public MediaElement(MediaElementBuildParameters? buildParameters) {
this.buildParameters = buildParameters; //save this parameter for when handler exists and pass it in there when you can
}
The Android ExoPlayer StyledPlayerView is constructed in the MediaManager: https://github.com/CommunityToolkit/Maui/blob/ee03326ab43f35f90d27d92a37b9df06d6663f27/src/CommunityToolkit.Maui.MediaElement/Views/MediaManager.android.cs
PlayerView = new StyledPlayerView(MauiContext.Context)
We would need something like instead (presuming buildParameters is passed through to here also first):
if (buildParameters !=null && buildParameters.androidSurfaceType == AndroidSurfaceType.textureView) {
//BUILD THE TEXTUREVIEW EXOPLAYER
System.Xml.XmlReader xmlResource = this.Resources.GetXml(Resource.Layout.textureview); //or whatever address the XML file is at
xmlResource.Read();
Android.Util.IAttributeSet attributes = Android.Util.Xml.AsAttributeSet(xmlResource);
PlayerView = new StyledPlayerView(MauiContext.Context, attributes) //StyledPlayerView has optional attributes parameter
System.Diagnostics.Debug.WriteLine("SURFACE TYPE " + styledPlayerView.VideoSurfaceView.GetType()); //just to see if working, should return TextureView here
xmlResource.Dispose();
}
else {
PlayerView = new StyledPlayerView(MauiContext.Context) //no attributes needed for SurfaceView as that is default
}
As per this reference there are slightly different events for SurfaceView and TextureView: https://medium.com/androiddevelopers/android-hdr-migrating-from-textureview-to-surfaceview-part-1-how-to-migrate-6bfd7f4b970e
onSurfaceTextureAvailable() == surfaceCreated() onSurfaceTextureDestroyed() == surfaceDestroyed() onSurfaceTextureSizeChanged() is similar to surfaceChanged()
However searching the Maui code I don't see any of these events even used so probably nothing needs to be done regarding this.
The only gaps I'm aware of then would be how to: (1) Send the build attributes from MediaElement into the MediaElementHandler, and then (2) Send them from the MediaElementHandler into the MediaManager where they would be used as above.
For (2), as far as I can see, MediaManager is created in the MediaElementHandler with code like:
mediaManager ??= new(MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} cannot be null"),
VirtualView,
Dispatcher.GetForCurrentThread() ?? throw new InvalidOperationException($"{nameof(IDispatcher)} cannot be null"));
var (_, playerView) = mediaManager.CreatePlatformView();
Thus here one could simply add a new function in the shared MediaManager class to pass them in like:
private MediaElementBuildAttributes buildAttributes;
public void setMediaElementBuildAttributes(MediaElementBuildAttributes buildAttributes){
this.buildAttributes = buildAttributes;
}
Thus one could change the above code to:
mediaManager ??= new(MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} cannot be null"),
VirtualView,
Dispatcher.GetForCurrentThread() ?? throw new InvalidOperationException($"{nameof(IDispatcher)} cannot be null"));
//pass in Build Attributes before "CreatePlatformView":
mediaManager.setBuildAttributes(buildAttributes); //just pass in the build attributes for use in the platforms if needed
var (_, playerView) = mediaManager.CreatePlatformView();
The last gap would then be getting the buildAttributes from the MediaElement into the MediaElementHandler. I have at this point lost the forest for the trees, but I presume this would be trivial for someone who understands better how the handlers are created (I am not that person I suppose).
That is unfortunately the best I can do and I would have to leave the implementation to someone better than me. If anyone tackles this and runs into any walls I'm happy to try to research find solutions to get through, but I suspect this will work.
I am happy to start working on this. We will need to create a sample to demonstrate how this works and also update the docs when we do the pull request. If you are willing to work with me and help me with the sample and testing out the project I am happy to create the code. We can get started once this is approved. I will in the meantime try and figure out a way to make this work. I have done work on this before and think I may have a way to avoid having to write any xaml.
I am happy to start working on this. We will need to create a sample to demonstrate how this works and also update the docs when we do the pull request. If you are willing to work with me and help me with the sample and testing out the project I am happy to create the code. We can get started once this is approved. I will in the meantime try and figure out a way to make this work. I have done work on this before and think I may have a way to avoid having to write any xaml.
Sure absolutely. Please let me know what to do. At least in C# it is very little code needed so I suspect it will all go smoothly.
@jonmdev have you joined the community discord? BTW I have a sample with texture viewer working and I am in the process of testing various stuff with it. I will be playing around with it today. My current implementation only allows setting it at launch of media element which I want to switch dynamically but that is for later in the process. I just want to verify that it is actually texture view and would appreciate some ideas to showcase and test if this is working. I was able to take what you had and essentially get it down to 4 lines of code which is easier to use than adding a ton of xml and rewriting the handler implementation which I don't really want to mess with at this time.
How does this look for an API
/// <summary>
/// Backing store for the <see cref="AndroidSurface"/> property.
/// </summary>
public static readonly BindableProperty AndroidSurfaceProperty =
BindableProperty.Create(nameof(AndroidSurface), typeof(AndroidSurfaceType), typeof(MediaElement), AndroidSurfaceType.SurfaceView);
/// <summary>
/// Backing store for the <see cref="AndroidColorHEX"/> property.
/// </summary>
public static readonly BindableProperty AndroidColorHEXProperty =
BindableProperty.Create(nameof(AndroidColorHEX), typeof(string), typeof(MediaElement), "#000000");
Does setting the color with alpha look like the sort of thing you want to do? Or should we also be setting the player view alpha too as we can also set the transparency of the controls? That would be separate but can be done.
Here is the enum for surface type:
namespace CommunityToolkit.Maui.Core.Primitives;
/// <summary>
/// Android surface type for the <see cref="IMediaElement"/>.
/// </summary>
public enum AndroidSurfaceType
{
/// <summary>
/// A <see cref="SurfaceView"/> is used for rendering the media.
/// </summary>
SurfaceView,
/// <summary>
/// A <see cref="TextureView"/> is used for rendering the media.
/// </summary>
TextureView,
}
@jonmdev have you joined the community discord? BTW I have a sample with texture viewer working and I am in the process of testing various stuff with it. I will be playing around with it today. My current implementation only allows setting it at launch of media element which I want to switch dynamically but that is for later in the process. I just want to verify that it is actually texture view and would appreciate some ideas to showcase and test if this is working. I was able to take what you had and essentially get it down to 4 lines of code which is easier to use than adding a ton of xml and rewriting the handler implementation which I don't really want to mess with at this time.
Wow that is phenomenal. You are very fast! I am very curious how you managed to set the ExoPlayer as TextureView without loading the XML snippet for this attribute from Resources. I spent days/weeks looking for a way to do that and posted all over the Internet with no solution found.
In any case, there are two immediate ways to test if it is successfully a TextureView:
styledPlayerView.GetType()
By default this will return "SurfaceView" but if you build a TextureView this will then return "TextureView".
https://github.com/CommunityToolkit/Maui/issues/1730
In that bug report I detail a test project I made where I have two videos on screen at once (two MediaElements). Both fill the screen, but each is clipped by Maui code to show on only one half of the screen (left for one, right for other). As you see in that project, in Windows and iOS this works fine with side by side video showing.
But in Android (because of the SurfaceView) the clipping is not respected and you get just one video showing full screen.
If this can be built as TextureView, it should automatically respect the clipping and the Android build will then look the same as Windows/iOS.
What is the Discord channel? I see two linked here: https://dotnet.microsoft.com/en-us/platform/community
One for C# community and the other .NET development. Is that it?
Regarding the Hex Color etc you are mentioning, I am not sure of the utilization or purpose. As I understand it, once this is switched to TextureView, we should be able to change the background color and opacity just like we do with any other Maui view.
Ie.
MediaElement mediaElement = new([textureviewcommand]);
mediaElement.Opacity = 0.5;
mediaElement.BackgroundColor = Colors.AliceBlue;
Please keep in mind the ExoPlayer does not support transparent video playback (ie. alpha channel) without major workarounds so don't expect that to work regardless of SurfaceView or TextureView out of the box https://medium.com/go-electra/unlock-transparency-in-videos-on-android-5dc43776cc72
I am not sure anything extra should be needed beyond that here but perhaps I am naive. Perhaps the Opacity
or other MediaElement parameters will need to passed through to the styledPlayerView
if they are not already, as I believe that is the true native platform view to manipulate here whether TextureView or SurfaceView.
Thanks again and I am very curious to see how you did it.
Or re: API, is your point about keeping the controls (play button etc) separately parameterized in terms of color/opacity from the video playback view (ie styledPlayerView)? If so, that is a great idea. Users may want to change the opacity of the styledPlayerView
separate from the MediaElement control buttons.
I did not even think of that possibility as I am not using the controls buttons (play etc) typically in my application. I think that is a smart idea to be able to control those separately though I defer to you on how to implement it as I am not sure of the mechanics.
If it was me, and that is what you mean, I might lean toward something like:
mediaElement.Opacity = 0.5; //global opacity for controls and view
mediaElement.OpacityVideo = 0.5; //specific opacity for video player view (multiplied in effect by global, so here 0.25 in effect)
mediaElement.OpacityControls = 1; //specific opacity for video player controls (multiplied in effect by global, so here 0.5 in effect)
Thanks again.
I'm mostly basing what I am doing based on your specifications. I have no real idea what you intended so every detail helps.
I'm mostly basing what I am doing based on your specifications. I have no real idea what you intended so every detail helps.
Thanks. That is fair.
Here is a list of things I can think it should be able to do if it is working correctly (ie. behaviors I would hope for):
1) Clipping
2) Opacity
styledPlayerView
as this is the native view holding the actual video playback.mediaElement.Opacity = 0.5
you would see it the two overlapping videos blend together on that half of the screen.3) Playback Controls
4) Background Color
mediaElement.BackgroundColor
should set the background color of the native view (ie. SurfaceView or TextureView) such that a mediaElement
with no video playing or loaded will look like this color.mediaElement.BackgroundColor = Colors.AliceBlue
should make a blue view until a video is playing.View.BackgroundColor = null
in Maui should use Android.Views.View.SetBackground(null)
in the platform code always (only way to null backgrounds in Android), although it seems this is sometimes not done properly in Maui.styledPlayerView.SetBackground(null)
should be invisible until a video is playing in it (invisible like any other basic View with a null background).5) GetAndroidSurface()
GetAndroidSurface()
or alternatively GetAndroidSurfaceView()
and GetAndroidTextureView()
to return a reference to the Android SurfaceView/TextureView (return null if you call the wrong one or it doesn't exist) out of the MediaElement or its handler this would save the challenge of trying to reach it by Reflection.The two main problems I am personally trying to solve are: (1) Overlapping videos not cooperating with their Maui clipping (like my bug report), and (2) Eventually implementing the above workaround for transparent video (alpha video support).
So at least from my perspective and what I would hope for, if the solution can allow the above, this would be Christmas has come early.
Does that answer what you were wondering? Hope that helps and thanks again for your efforts.
Sample of transparency and colors: Setting PlayerView color and transparency affects the controls. Setting Foreground color and transparency affects the video image.
PlayerView.SetBackgroundColor(Android.Graphics.Color.Transparent);
PlayerView.Foreground = new Android.Graphics.Drawables.ColorDrawable(Android.Graphics.Color.White);
PlayerView.Foreground.Alpha = 128;
PlayerView.Alpha = 0.50f;
Setting foreground color sets the image color property that sits on the foreground. Setting foreground alpha sets the transparency of color on top of image. Setting background color sets video controls color property. Setting the background alpha sets the video controls transparency.
This all assumes you are using TextureView. Otherwise it behaves as before and ignores all of the transparencies and colors. Or we could throw an error or create a log warning? How is this for API:
/// <summary>
/// Backing store for the <see cref="AndroidSurface"/> property.
/// </summary>
public static readonly BindableProperty AndroidSurfaceProperty =
BindableProperty.Create(nameof(AndroidSurface), typeof(AndroidSurfaceType), typeof(MediaElement), AndroidSurfaceType.SurfaceView);
/// <summary>
/// Backing store for the <see cref="PlayerBackgroundColor"/> property.
/// </summary>
public static readonly BindableProperty PlayerBackgroundColorProperty =
BindableProperty.Create(nameof(PlayerBackgroundColor), typeof(MediaElementColor), typeof(MediaElement), MediaElementColor.Default);
/// <summary>
/// Backing store for the <see cref="PlayerForegroundColor"/> property.
/// </summary>
public static readonly BindableProperty PlayerForegroundColorProperty =
BindableProperty.Create(nameof(PlayerForegroundColor), typeof(MediaElementColor), typeof(MediaElement), MediaElementColor.Default);
/// <summary>
/// Backing store for the <see cref="PlayerAlpha"/> property.
/// </summary>
public static readonly BindableProperty PlayerAlphaProperty =
BindableProperty.Create(nameof(PlayerAlpha), typeof(float), typeof(MediaElement), 1.0f);
/// <summary>
/// Backing store for the <see cref="PlayerForegroundAlpha"/> property.
/// </summary>
public static readonly BindableProperty PlayerForegroundAlphaProperty =
BindableProperty.Create(nameof(PlayerForegroundAlpha), typeof(int), typeof(MediaElement), 255);
/// <inheritdoc cref="IMediaElement.PlayerAlpha"/>
public float PlayerAlpha
{
get => (float)GetValue(PlayerAlphaProperty);
set => SetValue(PlayerAlphaProperty, value);
}
/// <inheritdoc cref="IMediaElement.PlayerForegroundAlpha"/>
public int PlayerForegroundAlpha
{
get => (int)GetValue(PlayerForegroundAlphaProperty);
set => SetValue(PlayerForegroundAlphaProperty, value);
}
/// <inheritdoc cref="IMediaElement.PlayerBackgroundColor"/>
public MediaElementColor PlayerBackgroundColor
{
get => (MediaElementColor)GetValue(PlayerBackgroundColorProperty);
set => SetValue(PlayerBackgroundColorProperty, value);
}
/// <inheritdoc cref="IMediaElement.PlayerForegroundAlpha"/>
public MediaElementColor PlayerForegroundColor
{
get => (MediaElementColor)GetValue(PlayerForegroundColorProperty);
set => SetValue(PlayerForegroundColorProperty, value);
}
/// <inheritdoc cref="IMediaElement.AndroidSurface"/>
public AndroidSurfaceType AndroidSurface
{
get => (AndroidSurfaceType)GetValue(AndroidSurfaceProperty);
set => SetValue(AndroidSurfaceProperty, value);
}
Setting foreground color sets the image color property that sits on the foreground. Setting foreground alpha sets the transparency of color on top of image. Setting background color sets video controls color property. Setting the background alpha sets the video controls transparency.
This all assumes you are using TextureView. Otherwise it behaves as before and ignores all of the transparencies and colors. Or we could throw an error or create a log warning?
Sample of transparency and colors: Setting PlayerView color and transparency affects the controls. Setting Foreground color and transparency affects the video image.
PlayerView.SetBackgroundColor(Android.Graphics.Color.Transparent); PlayerView.Foreground = new Android.Graphics.Drawables.ColorDrawable(Android.Graphics.Color.White); PlayerView.Foreground.Alpha = 128; PlayerView.Alpha = 0.50f;
Looks great that you seem to be getting it working! A few points of my thoughts to comment on for feedback or ideas.
If the design is to be split into "foreground" and "background" where one represents the controls (play button, etc) and the other represents the video view (ie. TextureView), would it not make more sense for "background" to be the video view (TextureView) and "foreground" to be the controls (play button, etc.)?
Since the controls are seen overlaying (in front of) the video, it seems more logical they should be called "foreground" and the video (which is behind the controls) should be called "background" if that is the terminology used.
Perhaps that is what you meant?
From what I would expect, there should be two color settings - one for the background of the TextureView (PlayerView.SetBackgroundColor
) and another for the color of the controls (play button etc.).
And there should be two opacity settings - one for the TextureView and another for the controls.
Based on your naming convention for the functions I would see this as:
PlayerView.setBackgroundColor
in Android code to set background of TextureViewAgain in just semantics, I would personally go with the term "Opacity" over "Alpha". I understand Android uses the term "Alpha" but Maui goes with "Opacity" everywhere else so for consistency, I would stick with "Opacity", ie. PlayerOpacity
or PlayerForegroundOpacity
.
But this is not important either way. Just my thought.
I would think it is better to just have a log message if you attempt to set opacity/alpha on a SurfaceView. If it is descriptive it will probably avoid unnecessary confusion and false bug reports. This message could say something like "Cannot set opacity of MediaElement in Android when it is built as SurfaceView. To set opacity, you must first configure MediaElement with mediaElement.AndroidSurface = AndroidSurfaceType.TextureView".
Avoiding any true errors (just showing log message) will allow us to switch back and forth in testing between TextureView and SurfaceView without having to comment out any other code.
setBackgroundColor
Just as an FYI again, please note that setBackgroundColor(Android.Graphics.Color.Transparent)
is not the same as setBackgroundColor(null)
. Using Transparent
will create overdraw. Whereas setBackgroundColor(null)
does not overdraw. This is discussed in my prior linked bug thread I mentioned above or here for example also: https://stackoverflow.com/a/43790016/10305478
So please just make sure if the system is set to null
for background color (or any relevant color) it passes this through as setBackgroundColor(null)
as well to the Android View to avoid overdraw. The default configuration should be setBackgroundColor(null)
as well.
StyledPlayerView
?As noted previously, Google has shown zero willpower in adding support for transparent video so far. They developed VP9 and released it 11 years ago and we still can't playback transparent video (support for alpha video channel). Thus the only workaround I know of is this one: https://medium.com/go-electra/unlock-transparency-in-videos-on-android-5dc43776cc72
In order to do that workaround, we need some access to a TextureView in order to render to it manually. This may be a bust, but I would appreciate a method to access the styledPlayerView
(TextureView) to experiment with. Or otherwise, do you have any suggestion for how it can be reached by Reflection?
I think one would have to reach it by MediaElementHandler > mediaManager > PlayerView through Reflection...
MediaElement.Opacity
?I am just curious - what would mediaElement.Opacity
end up doing (in any OS) currently or after these changes? Does this currently set opacity of both controls (play button etc) and view (video)? As mentioned, I am not using control buttons so don't know.
Would the new PlayerOpacity
and PlayerForegroundOpacity
be used in all OS's or just Android? ie. Would mediaElement.Opacity
become redundant?
I see no problem with mediaElement.Opacity
becoming unnecessary/redundant. Or it could set the opacity of both video and controls together. ie. mediaElement.Opacity = 0.5
would set both video and controls to 50% opacity in any OS. Whereas PlayerOpacity
and PlayerForegroundOpacity
would set the individual video or control opacities in any OS.
This change would then give 3 functions to control opacity. Or alternatively if this is more work or doesn't automatically work, we could just stop using mediaElement.Opacity
. It is not too important.
MediaElement.BackgroundColor
?Similarly, I am then just wondering what happens to this mediaElement.BackgroundColor
. Intuitively I would presume it would make sense to to have this automatically map to the same outcome as mediaElement.PlayerBackgroundColor
ie. the background of the video view.
It would then be a bit redundant. I am not sure how you have to handle things from a coding perspective on this. It is not too important in my opinion what happens to mediaElement.BackgroundColor
or what it does. We can just not use it altogether same as mediaElement.Opacity
. Whatever works.
Thanks again for all your work. Hopefully this all at least makes sense. Please let me know if any further thoughts or points to consider.
Out of interest will this be necessary once we migrate away from ExoPlayer and onto Media3? I don't know the timeline for that but I just wanted to make sure we are all aware and don't do something that might be thrown away in the future
Out of interest will this be necessary once we migrate away from ExoPlayer and onto Media3? I don't know the timeline for that but I just wanted to make sure we are all aware and don't do something that might be thrown away in the future
To my knowledge, I have looked into it and Media3 doesn't change anything except the locations of the classes. Ie. There is still SurfaceView and TextureView and these work the same. ExoPlayer is just being moved around. So these still need to be accommodated to get full function in Android.
You can see Android's documentation on PlayerView and ExoPlayer here for media3 and they describe the same system for surface_type
that exists now:
https://developer.android.com/media/media3/ui/playerview https://developer.android.com/media/media3/exoplayer
The surface_type attribute of PlayerView lets you set the type of surface used for video playback. Besides the values spherical_gl_surface_view (which is a special value for spherical video playback) and video_decoder_gl_surface_view (which is for video rendering using extension renderers), the allowed values are surface_view, texture_view and none. If the view is for audio playback only, none should be used to avoid having to create a surface because doing so can be expensive.
If the view is for regular video playback then surface_view or texture_view should be used. SurfaceView has a number of benefits over TextureView for video playback ...
On that note, to add one more point, if we are using MediaElement for audio (it looks like that is the intended design: "Play Audio and Video in .NET MAUI apps with the new MediaElement": https://devblogs.microsoft.com/dotnet/announcing-dotnet-maui-communitytoolkit-mediaelement/) then having the none
option would be good as well for audio playback to avoid creating unnecessary SurfaceView or TextureView objects as suggested by Android in the quote above.
ie. Again, as they say:
If the view is for audio playback only, none should be used to avoid having to create a surface because doing so can be expensive.
Sorry @ne0rrmatrix 🙂 one more point to consider but then will be good to have none
as SurfaceType also for audio only MediaElement option...
@jonmdev Yes it would be nice to have a surface type for audio. I would not recommend using none. Using audio if we use any controls it would default to SurfaceView
and use that to display controls. If you do not explicitly set the surface it will default to SurfaceView
and only by explicitly setting TextureView
can we use it. So although it might be nice to use none it would be an issue if they also used controls and did not realize they were using a default view for media element. I do not believe it is possible to not have a View
at all for exoplayer.
@jonmdev Yes it would be nice to have a surface type for audio. I would not recommend using none. Using audio if we use any controls it would default to
SurfaceView
and use that to display controls. If you do not explicitly set the surface it will default toSurfaceView
and only by explicitly settingTextureView
can we use it. So although it might be nice to use none it would be an issue if they also used controls and did not realize they were using a default view for media element. I do not believe it is possible to not have aView
at all for exoplayer.
Thanks! If the view is necessary for it to build and add to the hierarchy and work properly in Maui, then I would not bother adding a third option. People can decide whether they want their audio 'surface' to be texture view or surface view. There may be quirks to explore once we start playing with it in real use. Being able to use either for audio might still help in some way. I would then just leave the two options only.
Hope the work is going well. I am eager and excited to try it. 🙂
Feature name
Add TextureView Option to Android ExoPlayer
Link to discussion
https://github.com/CommunityToolkit/Maui/issues/1773
Progress tracker
Summary
ExoPlayer is the Google recommended video player in Android which is used in MediaElement.
ExoPlayer automatically constructs with the parameter
surface_type
as SurfaceView. This type of view "punches a hole" through the view hierarchy for efficiency. However, this also prevents layering/overlap of videos or usage of transparency, opacity, etc.The alternative
surface_type
offered by ExoPlayer is TextureView which allows overlapping videos, transparency, opacity, etc. at a mild cost to efficiency.Currently MediaElement only constructs as SurfaceView and there is no constructor to allow us to implement the TextureView constructor instead.
Adding a new MediaElement constructor with this option will solve this without much work and unlock many features and solutions in Android.
Motivation
As discussed here: https://github.com/CommunityToolkit/Maui/issues/1773
TextureView will fix issues like overlapping videos "punching through" one another inappropriately (eg. split screen, picture in picture, or otherwise overlapping displays).
It will also allow implementation of solutions for video transparency as per: https://medium.com/go-electra/unlock-transparency-in-videos-on-android-5dc43776cc72
Detailed Design
A TextureView ExoPlayer is constructed with the following approach in .NET:
1) Save an XML file with the
surface_type
declared astexture_view
inside it to Android Resources folder, eg. Resources\layout with a name like "textureview.xml"2) Load the XML resource and construct the ExoPlayer with it:
IMPLEMENTATION INTO COMMUNITY TOOLKIT:
1) PlayerView Constructor
The StyledPlayerView is constructed here: https://github.com/CommunityToolkit/Maui/blob/ee03326ab43f35f90d27d92a37b9df06d6663f27/src/CommunityToolkit.Maui.MediaElement/Views/MediaManager.android.cs#L43C1-L43C57
This just needs to be changed to:
Where
attributes
is the attributes file constructed above in cases where TextureView is desired, optionally taken by this constructor.2) MediaElement Constructor
MediaElement currently only has one constructor which takes no arguments. An extra new constructor would need to be added to pass the argument for the TextureView through when it is wanted.
One could either (1) add a simple enum for the
AndroidSurfaceType
, and or (2) wrap this inside a class calledMediaElementBuildParameters
or something of that nature which we instantiate to parameterize and set inside.eg.
and/or then:
Then we would create MediaElement using this new constructor when wanted (old constructor would still also be available for typical usage) as either:
If we anticipate there may be other construction related parameters that may come up over time, having the class to pass in (
MediaElementBuildParameters
) containing the enum (AndroidSurfaceType
) makes more sense as it then keeps the API generalizable. Further construction parameters could be added toMediaElementBuildParameters
for other purposes if needed down the line.This input would then need to be passed along through to the
StyledPlayerView
constructor of course as well so it can happen.Usage Syntax
As noted above for C#.
I do not program Maui in XAML (I only use C#) so I am not familiar with how it actually works but I presume it would be simple to extrapolate from what I wrote above if you understand XAML.
Drawbacks
I cannot think of any negative issues from this solution. It would take very few lines of code and it only expands the usability so we can solve currently unsolvable problems.
Otherwise users need to build their own video players from scratch to solve this which is the nightmare I am looking at otherwise. It makes no good sense to re-engineer the whole MediaElement just for one missing feature when it can be added so easily with so few lines of code.
Alternatives
There is no alternative except to tell all Maui users they must build their own video player from scratch if they want to use the full features of Android video playback.
Unresolved Questions
It is debatable whether it makes sense to just have an enum as the argument for the constructor and/or a class that contains the parameters (including the enum) for construction.
Having a "build parameters" class containing the "surface type" enum rather than just the enum alonewould open up possibilities for easily solving any other constructor related issues that might come up down the line perhaps. If it was me, for that reason, that's how I'd probably personally do it. But either will work fine.