skip to Main Content

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.

2

Answers


  1. Chosen as BEST ANSWER

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

    1. Create XML file named "test.xml" in Resourceslayout
    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.


  2. Class com.google.android.exoplayer2.ui.PlayerView is deprecated; use androidx.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 the app namespace in XML. One basically does not need any XML to construct a View, but still somehow needs to indicate where the component shall appear in the layout.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search