Baseflow / XamarinMediaManager

Cross platform Xamarin plugin to play and control Audio and Video
https://baseflow.com
MIT License
769 stars 306 forks source link

Unable to play some videos. #516

Open AlenToma opened 5 years ago

AlenToma commented 5 years ago

🐛 Bug Report

unable to play some videos, as it throw an exception

Expected behavior

The video should play normaly, as its type is an mp4

Reproduction steps

The video is of type mp4, just by playing it, it throw some kind of java exception.

Exception detalj

Com.Google.Android.Exoplayer2.ExoPlaybackException: Exception of type 'Com.Google.Android.Exoplayer2.ExoPlaybackException' was thrown. ---> Java.Lang.NullPointerException: Attempt to invoke interface method 'com.google.android.exoplayer2.PlayerMessage com.google.android.exoplayer2.ExoPlayer.createMessage(com.google.android.exoplayer2.PlayerMessage$Target)' on a null object reference
   --- End of inner exception stack trace ---
  --- End of managed Com.Google.Android.Exoplayer2.ExoPlaybackException stack trace ---
com.google.android.exoplayer2.ExoPlaybackException
    at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:354)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:214)
    at android.os.HandlerThread.run(HandlerThread.java:65)
Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'com.google.android.exoplayer2.PlayerMessage com.google.android.exoplayer2.ExoPlayer.createMessage(com.google.android.exoplayer2.PlayerMessage$Target)' on a null object reference
    at com.google.android.exoplayer2.source.ConcatenatingMediaSource.scheduleListenerNotification(ConcatenatingMediaSource.java:513)
    at com.google.android.exoplayer2.source.ConcatenatingMediaSource.handleMessage(ConcatenatingMediaSource.java:495)
    at com.google.android.exoplayer2.ExoPlayerImplInternal.deliverMessage(ExoPlayerImplInternal.java:861)
    at com.google.android.exoplayer2.ExoPlayerImplInternal.sendMessageToTarget(ExoPlayerImplInternal.java:829)
    at com.google.android.exoplayer2.ExoPlayerImplInternal.sendMessageInternal(ExoPlayerImplInternal.java:811)
    at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:328)
    ... 3 more

I could post the video if you like.

Configuration

Version: 0.6.8

Platform:

martijn00 commented 5 years ago

Yes, can you post the url?

AlenToma commented 5 years ago

The video is a local video. Here you can retrive it https://mega.nz/#!loxVQAzI!PPfTuR8xiCJYyTaxm-HPVqAFEX9bDX-oksVDqrZygF8

The name of the file is #جميلة - #دمدومة | Jamila - #Damdouma[I=exdfhcEjbwY][F=18][P=6].mp4 It may be related to the name, i will check it out.

AlenToma commented 5 years ago

Was able to resolve the issue by creatng new URI

new Uri("#جميلة - #دمدومة | Jamila - #Damdouma[I=exdfhcEjbwY][F=18][P=6].mp4").AbsoluteUri;

This is where a Resolver could do its work.

I dont know if this issue should be handled by the library or the user.

martijn00 commented 5 years ago

I'm not sure if it is possible to use a url resolver right before playing. Would it be useful to add a resolver that works on the beginning, when you add the url to play, or to the queue?

AlenToma commented 5 years ago

In this case it easy to do it as you said. But there is other cases, when the url need decrypting like i mentioned in MediaItem Resolver #510

I was thinking of something like this

in IMediaPlayer have a property. IMediaItemResolver ItemResolver { get; set; }

The IMediaItemResolver look like below

   /// <summary>
    /// Validate & resolve Mediaitem before playing it
    /// </summary>
    public interface IMediaItemResolver
    {
        /// <summary>
        /// Return null will force the MediaPlayer, to stop playing the item. 
        /// Validate and Make changes to mediaItem like MediaUrl
        /// </summary>
        /// <param name="mediaItem"></param>
        /// <returns></returns>
        IMediaItem Resolve(IMediaItem mediaItem);

        /// <summary>
        /// When the resolver fail, you could handle it. like playing the next video , or even change to a 
        /// different MediaPlayer that can handle the MediaItem. for example try to decrypt a youtube URI 
        /// when it failed then play the item in youtube player instead eg changing the MediaPlayer.
        /// </summary>
        Action<IMediaManager> OnFailed { get; set; }
    }

And then having an extension like below

        public static bool ResolveMediaItem(this IMediaPlayer mediaPlayer, IMediaItem mediaItem)
        {
            IMediaItemResolver itemResolver = null;
#if ANDROID
            // this is only temporary as the itemResolver exist only in Android
            itemResolver = mediaPlayer.ItemResolver;
#endif
            if (itemResolver != null)
            {
                var resolvedMediaItem = itemResolver.Resolve(mediaItem);
                if (resolvedMediaItem == null)
                {
                    itemResolver.OnFailed?.Invoke(CrossMediaManager.Current);
                    return false; // will prevent setting the source to the mediaplayer
                }
                if (resolvedMediaItem != mediaItem) /// new created IMediaItem from resolver, try and get all the changes 
                {
                    foreach (var p in typeof(IMediaItem).GetType().GetProperties())
                    {
                        var value = p.GetValue(resolvedMediaItem);
                        var currentValue = p.GetValue(mediaItem);
                        if (value != currentValue && value != null)
                        {
                            p.SetValue(mediaItem, value);
                        }
                    }
                }
            }
            return true;
        }

If this is a very big change then forget, its alright i will find another way :)