Goal
I am trying to create an ExoPlayer using Android code within .NET (MAUI) that renders to a TextureView (rather than the default SurfaceView).
Background
This is discussed in terms of Android in this thread.
In .NET/MAUI we have the following constructor which would apply for the ExoPlayer:
public unsafe StyledPlayerView (global::Android.Content.Context? context, global::Android.Util.IAttributeSet? attrs)
So you could run this as:
var PlayerView = new StyledPlayerView(androidContext, attributes);
//presuming you have a working 'attributes' file...
The second ‘attributes’ argument global::Android.Util.IAttributeSet? attrs
is where we are supposed to specify we want a TextureView.
From the link above, we must pass in an XML file which is first converted into the IAttributeSet
object to specify the TextureView as a target surface.
An example XML given is:
<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/player_view"
app:surface_type="texture_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
The XML file of this nature must be converted into the attribute needed for the constructor by:
val xmlAttributes = context.resources.getXml(R.xml.player_view).let {
try {
it.next()
it.nextTag()
} catch (e: Exception) {
// do something, log, whatever
}
Xml.asAttributeSet(it)
}
Question
If I want to work completely in a C# script, and I want to have no XML files to load from anywhere, but rather want to just declare this minimal short XML snippet as a string or written in my C# code directly, can I do this?
Can I then pass that into the code for creating the attribute? Or alternatively, can we make the IAttributeSet
for the constructor directly in C# without even bothering with creating the XML first?
My Best Attempt
My best attempt shown provides an error which I don’t understand.
In Visual Studio 2022, if one goes:
- File > New > Android Application
- Add the ExoPlayer NuGet
Xam.Plugins.Android.ExoPlayer
- Then in MainActivity.cs, one can try the following code:
protected override void OnCreate(Bundle? savedInstanceState) {
base.OnCreate(savedInstanceState);
string xmlString =
"<?xml version="1.0" encoding="utf-8"?>n" +
"<Com.Google.Android.Exoplayer2.UI.PlayerView " +
"android:id="@+id/player_view" " +
"app:surface_type="texture_view" " + //KEY NEEDED LINE FOR GOAL
"android:layout_width="match_parent" " +
"android:layout_height="match_parent"/>";
XmlReader xmlReader = XmlReader.Create(new StringReader(xmlString));
xmlReader.Read();
System.Diagnostics.Debug.WriteLine("XML READER " + xmlReader.AttributeCount);
Android.Util.IAttributeSet attributes = Android.Util.Xml.AsAttributeSet(xmlReader);
System.Diagnostics.Debug.WriteLine("ATTRIBUTES " + attributes.AttributeCount);
Com.Google.Android.Exoplayer2.UI.StyledPlayerView styledPlayerView = new(this, attributes);
Issues
No errors are given in Visual Studio on coding. However two problems are evident on running it:
- If you attempt to debug out the
xmlReader
with the following code, it says**System.Xml.XmlException:** ''android' is an undeclared prefix. Line 2, position 52.'
:
while (xmlReader.Read()) {
switch (xmlReader.NodeType) {
case XmlNodeType.Element:
System.Diagnostics.Debug.WriteLine("<{0}>", xmlReader.Name);
break;
case XmlNodeType.Text:
System.Diagnostics.Debug.WriteLine(xmlReader.Value);
break;
case XmlNodeType.CDATA:
System.Diagnostics.Debug.WriteLine("<![CDATA[{0}]]>", xmlReader.Value);
break;
case XmlNodeType.ProcessingInstruction:
System.Diagnostics.Debug.WriteLine("<?{0} {1}?>", xmlReader.Name, xmlReader.Value);
break;
case XmlNodeType.Comment:
System.Diagnostics.Debug.WriteLine("<!--{0}-->", xmlReader.Value);
break;
case XmlNodeType.XmlDeclaration:
System.Diagnostics.Debug.WriteLine("<?xml version='1.0'?>");
break;
case XmlNodeType.Document:
break;
case XmlNodeType.DocumentType:
System.Diagnostics.Debug.WriteLine("<!DOCTYPE {0} [{1}]", xmlReader.Name, xmlReader.Value);
break;
case XmlNodeType.EntityReference:
System.Diagnostics.Debug.WriteLine(xmlReader.Name);
break;
case XmlNodeType.EndElement:
System.Diagnostics.Debug.WriteLine("</{0}>", xmlReader.Name);
break;
}
}
Debug taken from: https://learn.microsoft.com/en-us/dotnet/api/system.xml.xmlreader.read?view=net-8.0
- The ExoPlayer constructor line returns the error
**Java.Lang.ClassCastException:** 'android.util.XmlPullAttributes cannot be cast to android.content.res.XmlBlock$Parser'
Why is this code is failing on these two points? The only other place I see the error from (2) is here and I am not sure of how this works.
In Short …
Can anyone provide a working sample of C# code that can make the attribute needed for the StyledPlayerView constructor to allow a TextureView Exoplayer to be created in .NET?
Thanks for any help.
2
Answers
After further research there is no way to do this in .NET except:
This does work. But there is no way to programmatically create the XML whether in Kotlin or C#. It must be pre-stored in Resources and loaded from there.
This seems to be just bad Android design.
Class
com.google.android.exoplayer2.ui.PlayerView
is deprecated; useandroidx.media3.ui.PlayerView
instead. And constructing a view from code also works differently then you might imagine (the XML is being processed and merely provides a reference to the class). The documentation would provide all the relevant attribute-names which one can pass into the constructor, otherwise located below theapp
namespace in XML. One basically does not need any XML to construct aView
, but still somehow needs to indicate where the component shall appear in the layout.