Search code examples
c#android.netexoplayertextureview

How to create TextureView programmatically with ExoPlayer in .NET/MAUI?


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:

  1. File > New > Android Application
  2. Add the ExoPlayer NuGet Xam.Plugins.Android.ExoPlayer
  3. 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:

  1. 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

  1. 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.


Solution

  • After further research there is no way to do this in .NET except:

    1. Create XML file named "test.xml" in Resources\layout
    2. Copy paste into it:
    <?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"
        android:id="@+id/test"
        app:surface_type="texture_view"
        android:layout_width= "match_parent"
        android:layout_height= "match_parent" />
    
    1. Run code in MainActivity.cs:
    XmlReader xmlResource = this.Resources.GetXml(Resource.Layout.test);
    xmlResource.Read();
    Android.Util.IAttributeSet attributes = Android.Util.Xml.AsAttributeSet(xmlResource);
    Com.Google.Android.Exoplayer2.UI.StyledPlayerView styledPlayerView = new(this, attributes); 
    

    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.