Baseflow / ExoPlayerXamarin

Xamarin bindings library for the Google ExoPlayer library
https://baseflow.com
MIT License
152 stars 67 forks source link

Java.Lang.AbstractMethodError: 'abstract method " Player$Listener.onTimelineChanged #137

Closed ScriptSun closed 1 year ago

ScriptSun commented 1 year ago

🐛 Bug Report

Attaching an event to Exoplayer.Addlistener() will cause and throw such an error no event will be working Java.Lang.AbstractMethodError: 'abstract method "void com.google.android.exoplayer2.Player$Listener.onTimelineChanged(com.google.android.exoplayer2.Timeline, int)"'

Our project is on hold now and it's very critical I wish you can tell me how to solve it or release a simple update

Expected behavior

Should work without any issues

Reproduction steps

Just create Exoplayer and attach the event by using Addlistener() Then use player.SetMediaItem(mediaItem); >> BOOM Here is the error throwing!

Configuration

Version: 2.18.3

Platform: Android

ScriptSun commented 1 year ago

@ArchangelWTF @martijn00

ArchangelWTF commented 1 year ago

I do believe I have a fix for it, would you have a sample for me to test it out in? As I currently do not have an app that I use internally ready for testing it (As it seemingly works in my environment)

ScriptSun commented 1 year ago

Thanks @ArchangelWTF i would be really happy if you can fix it soon as you can as our project is on hold now ;) i have tried yesterday but I am not soo good at binding jave to c#

I would be able to send my full code for you but it's a big social app and it will be complicated for you to navigate between all of the classes.

What I did was I add your Exo solutions all on my new xamarin .net 6 android target 33 projects and used the code below which you can copy directly and run the app.

MediaItem mediaItem = MediaItem.FromUri(Android.Net.Uri.Parse("https://wowonder.fra1.cdn.digitaloceanspaces.com/upload/videos/2022/11/PRjaipBn2Gmj4CEV1O4e_11_a509a30225863eb0c2e5cf08e343a826_video_720p_converted.mp4"));

var HttpDataSourceFactory = new DefaultHttpDataSource.Factory().SetAllowCrossProtocolRedirects(true);          
var MainDataSource = new ProgressiveMediaSource.Factory(HttpDataSourceFactory);
var  Exoplayer = new IExoPlayer.Builder(MainContext).SetMediaSourceFactory(MainDataSource).Build();

MediaItem mediaItem = MediaItem.FromUri(Android.Net.Uri.Parse(trackurl));
Exoplayer.SetMediaItem(mediaItem);
Exoplayer.AddListener(new PlayerGlobalEventListner());
ExoPlayer.Player.RepeatMode = 2;
ExoPlayer.SetKeepContentOnPlayerReset(true);
ExoPlayer.Prepare();
ExoPlayer.PlayWhenReady = true; 

 // Note: The interface is not automatically showing the implementation! 
public class PlayerGlobalEventListner : = Java.Lang.Object, IPlayer.IListener 
{
       // it's empty you can add any function you like 
      public  void OnIsPlayingChanged(bool isPlaying)
        {
            if (isPlaying)
               // do somthing

        }
}
ArchangelWTF commented 1 year ago

So the fix I tried didn't work, however then I compared as to why my ExoPlayer works in the apps that I'm using and the answer to that is I've generated empty methods like so:

public class PlayerListener : Java.Lang.Object, IPlayer.IListener
    {
        public void OnLoadingChanged(bool isLoading)
        {
        }

        public void OnRepeatModeChanged(int repeatMode)
        {
        }

        public void OnSeekProcessed()
        {
        }

        public void OnPlayerStateChanged(bool playWhenReady, int playbackState)
        {
        }

        public void OnShuffleModeEnabledChanged(bool p0)
        {
        }

        public void OnPlaybackParametersChanged(PlaybackParameters playbackParameters)
        {
        }

        public void OnPositionDiscontinuity(int reason)
        {
        }

        public void OnPositionDiscontinuity(IPlayer.PositionInfo oldPosition, IPlayer.PositionInfo newPosition, int reason)
        {

        }

        public void OnPlayerError(ExoPlaybackException e)
        {
        }

        public void OnTracksChanged(Com.Google.Android.Exoplayer2.Tracks tracks)
        {
        }

        public void OnTimelineChanged(Timeline timeline, int time)
        {
        }

        public void OnIsPlayingChanged(bool isPlaying)
        {
            Console.WriteLine("Playing!");
        }

        public void OnMediaItemTransition(MediaItem MediaItem, int reason)
        {
        }

        public void OnPlaybackStateChanged(int state)
        {
        }

        public void OnPlayWhenReadyChanged(bool playWhenReady, int reason)
        {
        }

        public void OnEvents(IPlayer Player, IPlayer.Events Events)
        {
        }

        public void OnIsLoadingChanged(bool changed)
        {
        }

        public void OnStaticMetadataChanged(System.Collections.Generic.IList<Metadata> metadataList)
        {
        }

        public void OnMediaMetadataChanged(MediaMetadata mediaMetadata)
        {
        }

        public void OnMetadata(Metadata metadata)
        {

        }

        public void OnDeviceVolumeChanged(int Volume, bool Muted)
        {
        }

        public void OnAvailableCommandsChanged(global::Com.Google.Android.Exoplayer2.IPlayer.Commands? availableCommands)
        {
        }

       //Video related methods
       public void OnSurfaceSizeChanged(int width, int hight)
       {

       }

       public void OnVideoSizeChanged(VideoSize size)
       {

       }

       public void OnRenderedFirstFrame()
       {

       }
    }

It's not great, ideally it would be better to be able to define them as an override and for it not to throw exceptions when a method is not implemented but I've got no idea as to how to do that in the bindings without causing further regressions in ExoPlayer.

ScriptSun commented 1 year ago

Thanks @ArchangelWTF I will be proceeding your fix by adding these 3 other void functions.

if you don't add them and the video size is changed the app will crash also or display a black screen here are the extra functions

 public void OnSurfaceSizeChanged(int width, int hight)
        {

        }

        public void OnVideoSizeChanged(VideoSize size)
        {

        }

        public void OnRenderedFirstFrame()
        {

        }
ArchangelWTF commented 1 year ago

Thanks for that! I've edited my comment to include those, didn't use a video related stream so that's why I didn't get the exception(s) 😛

ScriptSun commented 1 year ago

Just not a related question what can cause this issue on the video Exo player on recylerview each video has it own size so some will be long some will be small when you scroll some of them will be showing the old player hight

image

ArchangelWTF commented 1 year ago

I'll be honest I have no idea on that, I mostly use ExoPlayer for mostly Audio only.. So I'm afraid you're out of luck there 😅

janwiebe-jump commented 1 year ago

@ArchangelWTF I have created my own PlayerListener, but it doesn't seem to be called on 2.18.6. Even in Xamarin.MediaManager its used PlayerListener isn't called. Does it work at your side? I use this Xamarin version of ExoPlayer: https://github.com/janwiebe-jump/ExoPlayerXamarin/tree/feature/xamarin-android Any ideas?

It looks like the generated IPlayerListener interface in the binding project (ExoPlayer.Core) is empty, whereas the old IPlayerEventListener in earlier versions wasn't empty. But the IPlayerListener isn't stripped out in the metadata as far as I can see.

ArchangelWTF commented 1 year ago

@janwiebe-jump

I think IPlayerListener is supposed to be empty in ExoPlayer.Core due to it existing in ExoPlayer.Common now, seemingly however my PlayerListener seems to be called in projects that I'm running (At least OnPlayerStateChanged seems to be calling back as I have a writeline on that event)

I think I might abstract IPlayerListener in the bindings though, considering it throws an exception anyway if we don't have implemented methods and it saves the hassle of there being open issues because Visual Studio will complain if people don't implement it properly.

janwiebe-jump commented 1 year ago

@ArchangelWTF Thanks for looking into it. Is public void OnTracksChanged(Com.Google.Android.Exoplayer2.Tracks tracks) being called in your project? That's failing in my version of XamarinMediaManager, and it breaks the plugin for me.. https://github.com/janwiebe-jump/XamarinMediaManager/blob/xamarin-android-12/MediaManager/Platforms/Android/Player/PlayerEventListener.cs

ArchangelWTF commented 1 year ago

@janwiebe-jump It seems to be called for me, I have a sample here: https://github.com/ArchangelWTF/ExoTestApp where it works just fine.

janwiebe-jump commented 1 year ago

@ArchangelWTF Thanks! I found the difference, I had a different signature of the OnTracksChanged method. Pulled the latest ExoPlayerXamarin changes into my versionto support Xamarin, and now it works: https://github.com/janwiebe-jump/ExoPlayerXamarin/commit/7cc20c99ec9b47ada51c9b092f239537e2e7d960

The weird thing is that I don't have an IPlayer.IListener but IPlayerListener interface in my generated ExoPlayer, but the important thing is that it works!

ArchangelWTF commented 1 year ago

Glad to hear that it works now!

The weird thing is that I don't have an IPlayer.IListener but IPlayerListener interface in my generated ExoPlayer, but the important thing is that it works! Yeah I imagine it's because MAUI is changing the bindings differently over Xamarin, I looked weirdly at this too in my projects when I had to change it.

janwiebe-jump commented 1 year ago

Thanks, that makes sense. I have made my own Xamarin version of ExoPlayer, based on 2.18.7.

I run into a few little build issues, that don't matter for my use of ExoPlayer. It's here: https://github.com/janwiebe-jump/ExoPlayerXamarin

Nuget: https://www.nuget.org/packages/Xamarin.Plugins.Android.ExoPlayer/