xamarin / AndroidX

AndroidX bindings for .NET for Android
MIT License
179 stars 45 forks source link

Cannot replace a media item or seek in Exoplayer when adding a listener #929

Closed ne0rrmatrix closed 1 week ago

ne0rrmatrix commented 1 month ago

Android application type

Not applicable

Affected platform version

VS 2022, Dotnet Maui 8.0.21, Dotnet 8.x

Description

When enabling PlayerListener interface I am having issues with **Java.Lang.AbstractMethodError:** 'abstract method "void androidx.media3.common.Player$Listener.onPositionDiscontinuity(int)"' on adding a media item or seeking. If I never add the listener exoplayer has no issues. This worked previously with version 2 and I have updated it to support version 3. Adding any media item using any available method and then attempting to change it crashes the app. Seeking using timeline or by implementing seek method has same result.

Note: The log output below contains the crash info for when you scrub the timeline.

**Java.Lang.AbstractMethodError:** 'abstract method "void androidx.media3.common.Player$Listener.onPositionDiscontinuity(int)"'

Steps to Reproduce

  1. Create a new dotnet maui app.
  2. Add support for exoplayer using handlers
  3. Add the interface IPlayerListener
  4. Run app and try to scrub timeline or replace a playing item.

Did you find any workaround?

Do not implement the listener or do not enable it when changing media or scrubbing the timeline. Either will solve issue.

Relevant log output

[0:] CommunityToolkit.Maui.Sample.Pages.Views.MediaElementPage: Information: Position changed to 00:01:29.3220000
[EGL_emulation] app_time_stats: avg=177.06ms min=88.00ms max=215.46ms count=6
[BufferPoolAccessor2.0] bufferpool2 0x779648ecc7f8 : 5(31457280 size) total buffers - 4(25165824 size) used buffers - 295/300 (recycle/alloc) - 5/297 (fetch/transfer)
[0:] CommunityToolkit.Maui.Sample.Pages.Views.MediaElementPage: Information: Position changed to 00:01:29.5250000
[0:] CommunityToolkit.Maui.Sample.Pages.Views.MediaElementPage: Information: Position changed to 00:01:29.7400000
[BufferPoolAccessor2.0] bufferpool2 0x779648f03218 : 5(40960 size) total buffers - 1(8192 size) used buffers - 477/482 (recycle/alloc) - 5/474 (fetch/transfer)
[0:] CommunityToolkit.Maui.Sample.Pages.Views.MediaElementPage: Information: Position changed to 00:01:29.9480000
[0:] CommunityToolkit.Maui.Sample.Pages.Views.MediaElementPage: Information: Position changed to 00:01:30.1550000
[0:] CommunityToolkit.Maui.Sample.Pages.Views.MediaElementPage: Information: Position changed to 00:01:30.3690000
[EGL_emulation] app_time_stats: avg=42.01ms min=1.40ms max=206.80ms count=21
[0:] CommunityToolkit.Maui.Sample.Pages.Views.MediaElementPage: Information: Position changed to 00:01:30.5760000
[MediaCodec] keep callback message for reclaim
[CCodecConfig] query failed after returning 12 values (BAD_INDEX)
[Codec2Client] query -- param skipped: index = 1342179345.
[Codec2Client] query -- param skipped: index = 2415921170.
[Codec2Client] query -- param skipped: index = 1610614798.
[CCodecBufferChannel] [c2.goldfish.h264.decoder#230] Ignoring stale input buffer done callback: last flush index = 323, frameIndex = 319
[CCodecBufferChannel] [c2.goldfish.h264.decoder#230] flushed work; ignored.
[CCodecBufferChannel] [c2.goldfish.h264.decoder#230] Discard frames from previous generation.
[MediaCodec] keep callback message for reclaim
[CCodecConfig] query failed after returning 20 values (BAD_INDEX)
[Codec2Client] query -- param skipped: index = 1342179345.
[Codec2Client] query -- param skipped: index = 2415921170.
[Codec2Client] query -- param skipped: index = 1610614798.
[TrafficStats] tagSocket(120) with statsTag=0xffffffff, statsUid=-1
**Java.Lang.AbstractMethodError:** 'abstract method "void androidx.media3.common.Player$Listener.onPositionDiscontinuity(int)"'

[AudioTrack] getTimestamp_l(716): device stall time corrected using current time 157609651497100
[BufferPoolAccessor2.0] bufferpool2 0x779648ecc7f8 : 5(31457280 size) total buffers - 4(25165824 size) used buffers - 430/435 (recycle/alloc) - 5/435 (fetch/transfer)
[BufferPoolAccessor2.0] bufferpool2 0x779648f03218 : 5(40960 size) total buffers - 1(8192 size) used buffers - 694/699 (recycle/alloc) - 5/690 (fetch/transfer)
ne0rrmatrix commented 1 month ago

Here is an example implementation. It is in sample app under views. MediaElement page. https://github.com/CommunityToolkit/Maui/pull/2076

ne0rrmatrix commented 1 month ago

Stacktrace:

   at Java.Interop.JniEnvironment.InstanceMethods.CallNonvirtualVoidMethod(JniObjectReference instance, JniObjectReference type, JniMethodInfo method, JniArgumentValue* args) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/obj/Release/net7.0/JniEnvironment.g.cs:line 20830
   at Java.Interop.JniPeerMembers.JniInstanceMethods.InvokeVirtualVoidMethod(String encodedMember, IJavaPeerable self, JniArgumentValue* parameters) in /Users/runner/work/1/s/xamarin-android/external/Java.Interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods_Invoke.cs:line 75
   at AndroidX.Media3.Common.IPlayerListener.OnPositionDiscontinuityDeprecated(Int32 reason) in D:\a\_work\1\s\generated\androidx.media3.media3-common\obj\Release\net8.0-android\generated\src\AndroidX.Media3.Common.IPlayer.cs:line 1716
   at AndroidX.Media3.Common.IPlayerListener.n_OnPositionDiscontinuityDeprecated_I(IntPtr jnienv, IntPtr native__this, Int32 reason) in D:\a\_work\1\s\generated\androidx.media3.media3-common\obj\Release\net8.0-android\generated\src\AndroidX.Media3.Common.IPlayer.cs:line 1703
   at Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PPI_V(_JniMarshal_PPI_V callback, IntPtr jnienv, IntPtr klazz, Int32 p0) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs:line 55
  --- End of managed Java.Lang.AbstractMethodError stack trace ---
java.lang.AbstractMethodError: abstract method "void androidx.media3.common.Player$Listener.onPositionDiscontinuity(int)"
    at crc64ceb75e76f4b66147.MediaManager.n_onPositionDiscontinuity(Native Method)
    at crc64ceb75e76f4b66147.MediaManager.onPositionDiscontinuity(MediaManager.java:260)
    at androidx.media3.exoplayer.ExoPlayerImpl.lambda$updatePlaybackInfo$13(ExoPlayerImpl.java:1946)
    at androidx.media3.exoplayer.ExoPlayerImpl$$ExternalSyntheticLambda18.invoke(Unknown Source:8)
    at androidx.media3.common.util.ListenerSet$ListenerHolder.invoke(ListenerSet.java:332)
    at androidx.media3.common.util.ListenerSet.lambda$queueEvent$0(ListenerSet.java:216)
    at androidx.media3.common.util.ListenerSet$$ExternalSyntheticLambda1.run(Unknown Source:6)
    at androidx.media3.common.util.ListenerSet.flushEvents(ListenerSet.java:238)
    at androidx.media3.exoplayer.ExoPlayerImpl.updatePlaybackInfo(ExoPlayerImpl.java:2028)
    at androidx.media3.exoplayer.ExoPlayerImpl.setMediaSourcesInternal(ExoPlayerImpl.java:2244)
    at androidx.media3.exoplayer.ExoPlayerImpl.setMediaSources(ExoPlayerImpl.java:608)
    at androidx.media3.exoplayer.ExoPlayerImpl.setMediaItems(ExoPlayerImpl.java:571)
    at androidx.media3.common.BasePlayer.setMediaItems(BasePlayer.java:56)
    at androidx.media3.common.BasePlayer.setMediaItem(BasePlayer.java:41)
    at androidx.appcompat.app.AlertDialog_IDialogInterfaceOnClickListenerImplementor.n_onClick(Native Method)
    at androidx.appcompat.app.AlertDialog_IDialogInterfaceOnClickListenerImplementor.onClick(AlertDialog_IDialogInterfaceOnClickListenerImplementor.java:31)
    at androidx.appcompat.app.AlertController$AlertParams$3.onItemClick(AlertController.java:1068)
    at android.widget.AdapterView.performItemClick(AdapterView.java:330)
    at android.widget.AbsListView.performItemClick(AbsListView.java:1265)
    at android.widget.AbsListView$PerformClick.run(AbsListView.java:3278)
    at android.widget.AbsListView.onTouchUp(AbsListView.java:4256)
    at android.widget.AbsListView.onTouchEvent(AbsListView.java:3997)
    at android.view.View.dispatchTouchEvent(View.java:15655)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3114)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2787)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3120)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2801)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3120)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2801)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3120)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2801)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3120)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2801)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3120)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2801)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3120)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2801)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3120)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2801)
    at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:490)
    at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1904)
    at android.app.Dialog.dispatchTouchEvent(Dialog.java:910)
    at androidx.appcompat.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:69)
    at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:448)
    at android.view.View.dispatchPointerEvent(View.java:15919)
    at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:7021)
    at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:6815)
    at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:6229)
    at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:6286)
    at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:6252)
    at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:6417)
    at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:6260)
    at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:6474)
    at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:6233)
    at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:6286)
    at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:6252)
    at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:6260)
    at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:6233)
    at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:9211)
    at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:9162)
    at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:9131)
    at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:9337)
    at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:267)
    at android.os.MessageQueue.nativePollOnce(Native Method)
    at android.os.MessageQueue.next(MessageQueue.java:335)
    at android.os.Looper.loopOnce(Looper.java:162)
    at android.os.Looper.loop(Looper.java:294)
    at android.app.ActivityThread.main(ActivityThread.java:8177)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
ne0rrmatrix commented 1 month ago

This appears to be an issue with an incompatible class library. The common library is throwing errors on IPlayerIlistener image

ne0rrmatrix commented 1 month ago

Media3Sample.zip

Added minimal reproduction. Unzip folder. Load top level project in Visual Studio or VS Code. Launch in Android emulator or create apk and put on device. Run program and click either stop or scrub timeline by using any control.

brminnick commented 3 weeks ago

Hey @moljac! Thanks for doing the Media3 bindings! Any chance you could help us resolve this?

My guess is that we're missing a binding in the new Media3 libraries.

We're blocked from porting CommunityToolkit.Maui.MediaElement to Media3 until we can get this resolved or find a workaround.

jpobst commented 3 weeks ago

Do you need the #region IPlayer.IListener implementation method stubs section in MediaManager.android.cs? The interface methods have default implementations so I don't think you need to provide additional empty implementations.

The issue may be you are providing:

public void OnPositionDiscontinuity (int reason) { }

while the IPlayerListener method is called something else:

void OnPositionDiscontinuityDeprecated (int reason) { ... }
ne0rrmatrix commented 3 weeks ago

Yes we need the iPlayer listener. It is critical. We use it extensively and will continue to need it while we support legacy APIs and for monitoring many things currently.

jpobst commented 3 weeks ago

I'm not suggesting you remove IPlayerListener, just that you remove the "method stubs" that aren't currently doing anything. If this is just a simple sample and you need them in your real project I would suggest trying it with the correct method name.

ne0rrmatrix commented 3 weeks ago

Yes I would be happy to do that! Sorry I was confused there. It is the Maui Community toolkit. I did not add the stubs. They have been there since I started helping out last year. I will go through them and remove the ones we are not using. The sample I provided was just a ripped sample that I made up in an afternoon as I did not want to link a huge sample app with many other things listed. I am currently working on migrating the Dotnet Maui Community toolkit to use media 3.

ne0rrmatrix commented 3 weeks ago

Do you need the #region IPlayer.IListener implementation method stubs section in MediaManager.android.cs? The interface methods have default implementations so I don't think you need to provide additional empty implementations.

The issue may be you are providing:

public void OnPositionDiscontinuity (int reason) { }

while the IPlayerListener method is called something else:

void OnPositionDiscontinuityDeprecated (int reason) { ... }

The implementation stubs are required or the app will crash. Even when empty they do something. BTW I have tried other media 3 nugets which with very minor changes to the code I have do work 100 percent of the time. I have tested https://www.nuget.org/packages/Anjo.Android.Media3.ExoPlayer and it works out of the box with little to no modifications. I can't use that as it would not be allowed at all. The source is unavailable and not on GitHub in a public repo. But if it is indeed media 3 and the same as what you have done I have to think that this is a binding issue and not an issue with my code.

ArchangelWTF commented 2 weeks ago

Do you need the #region IPlayer.IListener implementation method stubs section in MediaManager.android.cs? The interface methods have default implementations so I don't think you need to provide additional empty implementations. The issue may be you are providing:

public void OnPositionDiscontinuity (int reason) { }

while the IPlayerListener method is called something else:

void OnPositionDiscontinuityDeprecated (int reason) { ... }

The implementation stubs are required or the app will crash. Even when empty they do something. BTW I have tried other media 3 nugets which with very minor changes to the code I have do work 100 percent of the time. I have tested https://www.nuget.org/packages/Anjo.Android.Media3.ExoPlayer and it works out of the box with little to no modifications. I can't use that as it would not be allowed at all. The source is unavailable and not on GitHub in a public repo. But if it is indeed media 3 and the same as what you have done I have to think that this is a binding issue and not an issue with my code.

It's not an issue with your implementation, it's because Anjo's and the others are mostly copies of the work I've done here: https://github.com/Baseflow/ExoPlayerXamarin/pull/166

I think I had this issue too all the way back when Media3 was still ExoPlayer, I ended up removing the deprecated methods (https://github.com/ArchangelWTF/ExoPlayerXamarin/blob/22591a98b8448f1b716c1cb74d2c700164c54361/Media3.Common/Transforms/Metadata.xml#L11) as well as setting the entire listener class to abstract(https://github.com/ArchangelWTF/ExoPlayerXamarin/blob/22591a98b8448f1b716c1cb74d2c700164c54361/Media3.Common/Transforms/Metadata.xml#L17) to force them to be fully implemented by people trying to use the Listener (because otherwise the app would crash anyway)

There was a reason I removed the deprecated methods over renaming but I cant remember what it was, it might be related to the crash.

Maybe that gives some pointers for the AndroidX maintainers to fix the issue.

ne0rrmatrix commented 2 weeks ago

This needs to be implemented https://github.com/xamarin/AndroidX/blob/d126bba06db14db94517403df0f3c7dd31d863d7/source/androidx.media3/media3-exoplayer/Transforms/Metadata.xml#L153 and https://github.com/xamarin/AndroidX/blob/d126bba06db14db94517403df0f3c7dd31d863d7/source/androidx.media3/media3-exoplayer/Transforms/Metadata.xml#L135 The second one is not used and if removed causes app to crash while seeking. It still needs to be there as a stub. The first one is used by our app to provide video dimensions to developers.