Xamarin Forms: Native Crash Reporting in ios platform due to MediaElement feature

后端 未结 1 551
一生所求
一生所求 2021-01-28 05:42

I am getting the below message on my output box in ios platform. After this message, the app is hanged and not able to move to any other page. Recently I have implemented the

相关标签:
1条回答
  • 2021-01-28 06:43

    This is a known issue: https://github.com/xamarin/Xamarin.Forms/issues/9525

    And it still doesn't work in Xamarin Forms 4.8. Try the workaround here: https://github.com/xamarin/Xamarin.Forms/issues/9525#issuecomment-629995589

    Moreover, MediaElement is currently experimental and will be moved to Xamarin Community Toolkit: https://github.com/xamarin/Xamarin.Forms/issues/11857

    Add the below Mediaelement renderer on ios project for solving this issue. For Sample project check my XF thread.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using AVFoundation;
    using AVKit;
    using CoreMedia;
    using Foundation;
    using MediaelementDemo.iOS;
    using UIKit;
    using Xamarin.Forms;
    using Xamarin.Forms.Internals;
    using Xamarin.Forms.Platform.iOS;
    using IOPath = System.IO.Path;
    
    [assembly: ExportRenderer(typeof(MediaElement), typeof(MyMediaElementRenderer))]
    namespace MediaelementDemo.iOS
    {
        public class MyMediaElementRenderer : ViewRenderer<MediaElement, UIView>
        {
            IMediaElementController Controller => Element as IMediaElementController;
    
            AVPlayerViewController _avPlayerViewController = new AVPlayerViewController();
            NSObject _playedToEndObserver;
            NSObject _statusObserver;
            NSObject _rateObserver;
    
            bool _idleTimerDisabled = false;
    
            [Xamarin.Forms.Internals.Preserve(Conditional = true)]
            public MyMediaElementRenderer()
            {
                Xamarin.Forms.MediaElement.VerifyMediaElementFlagEnabled(nameof(MediaElementRenderer));
    
                _playedToEndObserver = NSNotificationCenter.DefaultCenter.AddObserver(AVPlayerItem.DidPlayToEndTimeNotification, PlayedToEnd);
            }
    
            void SetKeepScreenOn(bool value)
            {
                if (value)
                {
                    if (!UIApplication.SharedApplication.IdleTimerDisabled)
                    {
                        _idleTimerDisabled = true;
                        UIApplication.SharedApplication.IdleTimerDisabled = true;
                    }
                }
                else if (_idleTimerDisabled)
                {
                    _idleTimerDisabled = false;
                    UIApplication.SharedApplication.IdleTimerDisabled = false;
                }
            }
    
            void UpdateSource()
            {
                if (Element.Source != null)
                {
                    AVAsset asset = null;
    
                    var uriSource = Element.Source as UriMediaSource;
                    if (uriSource != null)
                    {
                        if (uriSource.Uri.Scheme == "ms-appx")
                        {
                            if (uriSource.Uri.LocalPath.Length <= 1)
                                return;
    
                            // used for a file embedded in the application package
                            asset = AVAsset.FromUrl(NSUrl.FromFilename(uriSource.Uri.LocalPath.Substring(1)));
                        }
                        else if (uriSource.Uri.Scheme == "ms-appdata")
                        {
                            string filePath = ResolveMsAppDataUri(uriSource.Uri);
    
                            if (string.IsNullOrEmpty(filePath))
                                throw new ArgumentException("Invalid Uri", "Source");
    
                            asset = AVAsset.FromUrl(NSUrl.FromFilename(filePath));
                        }
                        else
                        {
                            asset = AVUrlAsset.Create(NSUrl.FromString(uriSource.Uri.AbsoluteUri));
                        }
                    }
                    else
                    {
                        var fileSource = Element.Source as FileMediaSource;
                        if (fileSource != null)
                        {
                            asset = AVAsset.FromUrl(NSUrl.FromFilename(fileSource.File));
                        }
                    }
    
                    var item = new AVPlayerItem(asset);
                    RemoveStatusObserver();
    
                    _statusObserver = (NSObject)item.AddObserver("status", NSKeyValueObservingOptions.New, ObserveStatus);
    
    
                    if (_avPlayerViewController.Player != null)
                    {
                        _avPlayerViewController.Player.ReplaceCurrentItemWithPlayerItem(item);
                    }
                    else
                    {
                        _avPlayerViewController.Player = new AVPlayer(item);
                        _rateObserver = (NSObject)_avPlayerViewController.Player.AddObserver("rate", NSKeyValueObservingOptions.New, ObserveRate);
                    }
    
                    if (Element.AutoPlay)
                        Play();
                }
                else
                {
                    if (Element.CurrentState == MediaElementState.Playing || Element.CurrentState == MediaElementState.Buffering)
                    {
                        _avPlayerViewController.Player.Pause();
                        Controller.CurrentState = MediaElementState.Stopped;
                    }
                }
            }
    
            protected override void Dispose(bool disposing)
            {
                if (_playedToEndObserver != null)
                {
                    NSNotificationCenter.DefaultCenter.RemoveObserver(_playedToEndObserver);
                    _playedToEndObserver = null;
                }
    
                if (_rateObserver != null)
                {
                    _avPlayerViewController?.Player?.RemoveObserver(_rateObserver, "rate");
                    _rateObserver = null;
                }
    
                RemoveStatusObserver();
    
                _avPlayerViewController?.Player?.Pause();
                _avPlayerViewController?.Player?.ReplaceCurrentItemWithPlayerItem(null);
    
                base.Dispose(disposing);
            }
    
            void RemoveStatusObserver()
            {
                if (_statusObserver != null)
                {
                    try
                    {
                        _avPlayerViewController?.Player?.CurrentItem?.RemoveObserver(_statusObserver, "status");
                    }
                    finally
                    {
    
                        _statusObserver = null;
                    }
                }
            }
    
            void ObserveRate(NSObservedChange e)
            {
                if (Controller is object)
                {
                    switch (_avPlayerViewController.Player.Rate)
                    {
                        case 0.0f:
                            Controller.CurrentState = MediaElementState.Paused;
                            break;
    
                        case 1.0f:
                            Controller.CurrentState = MediaElementState.Playing;
                            break;
                    }
    
                    Controller.Position = Position;
                }
            }
    
            void ObserveStatus(NSObservedChange e)
            {
                Controller.Volume = _avPlayerViewController.Player.Volume;
    
                switch (_avPlayerViewController.Player.Status)
                {
                    case AVPlayerStatus.Failed:
                        Controller.OnMediaFailed();
                        break;
    
                    case AVPlayerStatus.ReadyToPlay:
                        var duration = _avPlayerViewController.Player.CurrentItem.Duration;
    
                        if (duration.IsIndefinite)
                            Controller.Duration = TimeSpan.Zero;
                        else
                            Controller.Duration = TimeSpan.FromSeconds(duration.Seconds);
    
                        Controller.VideoHeight = (int)_avPlayerViewController.Player.CurrentItem.Asset.NaturalSize.Height;
                        Controller.VideoWidth = (int)_avPlayerViewController.Player.CurrentItem.Asset.NaturalSize.Width;
                        Controller.OnMediaOpened();
                        Controller.Position = Position;
                        break;
                }
            }
    
            TimeSpan Position
            {
                get
                {
                    if (_avPlayerViewController.Player.CurrentTime.IsInvalid)
                        return TimeSpan.Zero;
    
                    return TimeSpan.FromSeconds(_avPlayerViewController.Player.CurrentTime.Seconds);
                }
            }
    
            void PlayedToEnd(NSNotification notification)
            {
                if (Element == null)
                {
                    return;
                }
    
                if (Element.IsLooping)
                {
                    _avPlayerViewController.Player.Seek(CMTime.Zero);
                    Controller.Position = Position;
                    _avPlayerViewController.Player.Play();
                }
                else
                {
                    SetKeepScreenOn(false);
                    Controller.Position = Position;
    
                    try
                    {
                        Device.BeginInvokeOnMainThread(Controller.OnMediaEnded);
                    }
                    catch (Exception e)
                    {
                        Log.Warning("MediaElement", $"Failed to play media to end: {e}");
                    }
                }
            }
    
            protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
            {
                switch (e.PropertyName)
                {
                    case nameof(MediaElement.Aspect):
                        _avPlayerViewController.VideoGravity = AspectToGravity(Element.Aspect);
                        break;
    
                    case nameof(MediaElement.KeepScreenOn):
                        if (!Element.KeepScreenOn)
                        {
                            SetKeepScreenOn(false);
                        }
                        else if (Element.CurrentState == MediaElementState.Playing)
                        {
                            // only toggle this on if property is set while video is already running
                            SetKeepScreenOn(true);
                        }
                        break;
    
                    case nameof(MediaElement.ShowsPlaybackControls):
                        _avPlayerViewController.ShowsPlaybackControls = Element.ShowsPlaybackControls;
                        break;
    
                    case nameof(MediaElement.Source):
                        UpdateSource();
                        break;
    
                    case nameof(MediaElement.Volume):
                        _avPlayerViewController.Player.Volume = (float)Element.Volume;
                        break;
                }
            }
    
            void MediaElementSeekRequested(object sender, SeekRequested e)
            {
                if (_avPlayerViewController.Player.Status != AVPlayerStatus.ReadyToPlay || _avPlayerViewController.Player.CurrentItem == null)
                    return;
    
                NSValue[] ranges = _avPlayerViewController.Player.CurrentItem.SeekableTimeRanges;
                CMTime seekTo = new CMTime(Convert.ToInt64(e.Position.TotalMilliseconds), 1000);
                foreach (NSValue v in ranges)
                {
                    if (seekTo >= v.CMTimeRangeValue.Start && seekTo < (v.CMTimeRangeValue.Start + v.CMTimeRangeValue.Duration))
                    {
                        _avPlayerViewController.Player.Seek(seekTo, SeekComplete);
                        break;
                    }
                }
            }
    
            void Play()
            {
                var audioSession = AVAudioSession.SharedInstance();
                NSError err = audioSession.SetCategory(AVAudioSession.CategoryPlayback);
                if (!(err is null))
                    Log.Warning("MediaElement", "Failed to set AVAudioSession Category {0}", err.Code);
    
                audioSession.SetMode(AVAudioSession.ModeMoviePlayback, out err);
                if (!(err is null))
                    Log.Warning("MediaElement", "Failed to set AVAudioSession Mode {0}", err.Code);
    
                err = audioSession.SetActive(true);
                if (!(err is null))
                    Log.Warning("MediaElement", "Failed to set AVAudioSession Active {0}", err.Code);
    
                if (_avPlayerViewController.Player != null)
                {
                    _avPlayerViewController.Player.Play();
                    Controller.CurrentState = MediaElementState.Playing;
                }
    
                if (Element.KeepScreenOn)
                {
                    SetKeepScreenOn(true);
                }
            }
    
            void MediaElementStateRequested(object sender, StateRequested e)
            {
                MediaElementVolumeRequested(this, EventArgs.Empty);
    
                switch (e.State)
                {
                    case MediaElementState.Playing:
                        Play();
                        break;
    
                    case MediaElementState.Paused:
                        if (Element.KeepScreenOn)
                        {
                            SetKeepScreenOn(false);
                        }
    
                        if (_avPlayerViewController.Player != null)
                        {
                            _avPlayerViewController.Player.Pause();
                            Controller.CurrentState = MediaElementState.Paused;
                        }
                        break;
    
                    case MediaElementState.Stopped:
                        if (Element.KeepScreenOn)
                        {
                            SetKeepScreenOn(false);
                        }
                        //ios has no stop...
                        _avPlayerViewController?.Player.Pause();
                        _avPlayerViewController?.Player.Seek(CMTime.Zero);
                        Controller.CurrentState = MediaElementState.Stopped;
    
                        NSError err = AVAudioSession.SharedInstance().SetActive(false);
                        if (!(err is null))
                            Log.Warning("MediaElement", "Failed to set AVAudioSession Inactive {0}", err.Code);
                        break;
                }
    
                Controller.Position = Position;
            }
    
            static AVLayerVideoGravity AspectToGravity(Aspect aspect)
            {
                switch (aspect)
                {
                    case Aspect.Fill:
                        return AVLayerVideoGravity.Resize;
    
                    case Aspect.AspectFill:
                        return AVLayerVideoGravity.ResizeAspectFill;
    
                    default:
                        return AVLayerVideoGravity.ResizeAspect;
                }
            }
    
            void SeekComplete(bool finished)
            {
                if (finished)
                {
                    Controller.OnSeekCompleted();
                }
            }
    
            private void MediaElementVolumeRequested(object sender, EventArgs e)
            {
                Controller.Volume = _avPlayerViewController.Player.Volume;
            }
    
            void MediaElementPositionRequested(object sender, EventArgs e)
            {
                Controller.Position = Position;
            }
    
            protected override void OnElementChanged(ElementChangedEventArgs<MediaElement> e)
            {
                base.OnElementChanged(e);
    
                if (e.OldElement != null)
                {
                    e.OldElement.PropertyChanged -= OnElementPropertyChanged;
                    e.OldElement.SeekRequested -= MediaElementSeekRequested;
                    e.OldElement.StateRequested -= MediaElementStateRequested;
                    e.OldElement.PositionRequested -= MediaElementPositionRequested;
                    e.OldElement.VolumeRequested -= MediaElementVolumeRequested;
    
                    if (_playedToEndObserver != null)
                    {
                        NSNotificationCenter.DefaultCenter.RemoveObserver(_playedToEndObserver);
                        _playedToEndObserver = null;
                    }
    
                    // stop video if playing
                    if (_avPlayerViewController?.Player?.CurrentItem != null)
                    {
                        RemoveStatusObserver();
    
                        _avPlayerViewController.Player.Pause();
                        _avPlayerViewController.Player.Seek(CMTime.Zero);
                        _avPlayerViewController.Player.ReplaceCurrentItemWithPlayerItem(null);
                        AVAudioSession.SharedInstance().SetActive(false);
                    }
                }
    
                if (e.NewElement != null)
                {
                    SetNativeControl(_avPlayerViewController.View);
    
                    Element.PropertyChanged += OnElementPropertyChanged;
                    Element.SeekRequested += MediaElementSeekRequested;
                    Element.StateRequested += MediaElementStateRequested;
                    Element.PositionRequested += MediaElementPositionRequested;
                    Element.VolumeRequested += MediaElementVolumeRequested;
    
                    _avPlayerViewController.ShowsPlaybackControls = Element.ShowsPlaybackControls;
                    _avPlayerViewController.VideoGravity = AspectToGravity(Element.Aspect);
                    if (Element.KeepScreenOn)
                    {
                        SetKeepScreenOn(true);
                    }
    
                    _playedToEndObserver = NSNotificationCenter.DefaultCenter.AddObserver(AVPlayerItem.DidPlayToEndTimeNotification, PlayedToEnd);
    
                    UpdateBackgroundColor();
                    UpdateSource();
                }
            }
    
            void UpdateBackgroundColor()
            {
                BackgroundColor = Element.BackgroundColor.ToUIColor();
            }
    
            internal string ResolveMsAppDataUri(Uri uri)
            {
                if (uri.Scheme == "ms-appdata")
                {
                    string filePath = string.Empty;
    
                    if (uri.LocalPath.StartsWith("/local"))
                    {
                        var libraryPath = NSFileManager.DefaultManager.GetUrls(NSSearchPathDirectory.LibraryDirectory, NSSearchPathDomain.User)[0].Path;
                        filePath = IOPath.Combine(libraryPath, uri.LocalPath.Substring(7));
                    }
                    else if (uri.LocalPath.StartsWith("/temp"))
                    {
                        filePath = IOPath.Combine(IOPath.GetTempPath(), uri.LocalPath.Substring(6));
                    }
                    else
                    {
                        throw new ArgumentException("Invalid Uri", "Source");
                    }
    
                    return filePath;
                }
                else
                {
                    throw new ArgumentException("uri");
                }
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题