Xamarin Forms:由于MediaElement功能,ios平台中的本机崩溃报告 [英] Xamarin Forms: Native Crash Reporting in ios platform due to MediaElement feature

查看:51
本文介绍了Xamarin Forms:由于MediaElement功能,ios平台中的本机崩溃报告的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在ios平台的输出框中收到以下消息.出现此消息后,该应用程序已挂起,无法移动到任何其他页面.最近,我已经实现了用于播放音频和视频文件的 MediaElement 功能.使用 MediaElement 播放视频后,会出现此问题.该视频将正常播放,当我按视频页面上的后退"按钮时,此问题就开始了.

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 MediaElement feature for playing audio and video files. This problem is occurring after playing the video using MediaElement. The video will play without any issue, when I press the back button from the video page, this problem is starting.

我在 App.xaml.cs构造函数中添加了 Device.SetFlags(new [] {"MediaElement_Experimental"}); .是否需要在ios平台上添加任何其他依赖于平台的代码?

I have added Device.SetFlags(new[] { "MediaElement_Experimental" }); in App.xaml.cs constructor. Is there any other platform dependent code need to be added on ios platform?

问题仅在ios平台上存在,而在android平台上没有问题.

The problem is only on the ios platform and no issue in android platform.

=================================================================
Native Crash Reporting
=================================================================
Got a segv while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries
used by your application.
=================================================================

=================================================================
Native stacktrace:
=================================================================
0x1056ef094 - /private/var/containers/Bundle/Application/36973FCE-2200-474B-8B1B-16352EB8E83D/ProjectName.iOS.app/ProjectName.iOS :
0x1056e530c - /private/var/containers/Bundle/Application/36973FCE-2200-474B-8B1B-16352EB8E83D/ProjectName.iOS.app/ProjectName.iOS :
0x1056f34bc - /private/var/containers/Bundle/Application/36973FCE-2200-474B-8B1B-16352EB8E83D/ProjectName.iOS.app/ProjectName.iOS : mono_pmip
0x19dbda894 - /usr/lib/system/libsystem_platform.dylib : <redacted>
0x19e28e5f8 - /System/Library/Frameworks/Foundation.framework/Foundation : <redacted>
0x19e28c120 - /System/Library/Frameworks/Foundation.framework/Foundation : <redacted>
0x1a80d370c - /System/Library/Frameworks/AVFoundation.framework/AVFoundation : <redacted>
0x1a80d3ac4 - /System/Library/Frameworks/AVFoundation.framework/AVFoundation : <redacted>
=================================================================
Basic Fault Address Reporting
=================================================================
Memory around native instruction pointer (0x19dbf24ac):0x19dbf249c 1f 04 00 f1 cb 00 00 54 08 00 40 f9 08 81 7d 92 .......T..@...}.
0x19dbf24ac 08 71 40 39 00 09 02 53 c0 03 5f d6 00 00 80 52 .q@9...S.._....R
0x19dbf24bc c0 03 5f d6 c0 02 00 b4 e8 03 00 aa c0 00 f8 b7 .._.............
0x19dbf24cc 08 01 40 f9 00 81 7d 92 20 02 00 b4 21 00 80 52 ..@...}. ...!..R

=================================================================
Managed Stacktrace:
=================================================================
at <unknown> <0xffffffff>
at ObjCRuntime.Messaging:void_objc_msgSend <0x00007>
at AVFoundation.AVPlayer:Pause <0x00023>
at Xamarin.Forms.Platform.iOS.MediaElementRenderer:Dispose <0x001f7>
at Foundation.NSObject:Dispose <0x00023>
at Xamarin.Forms.Platform.iOS.VisualElementPackager:Dispose <0x00273>
at Xamarin.Forms.Platform.iOS.VisualElementPackager:Dispose <0x0006f>
at Xamarin.Forms.Platform.iOS.VisualElementRenderer`1:Dispose <0x001df>
=================================================================

示例项目

为便于参考,我在此处上传了一个示例项目.

场景:通过单击首页上的标签来播放视频,播放完视频后,请使用页面顶部的后退选项返回首页.然后出现上述问题,然后该应用程序挂起,我们无法执行其他任何操作.

Scenario: Play the video by clicking the label on the first page, after playing the video come back to first page using the back option on the top of the page. Then the above problem is occur and after that the app is hang and we can't do anything else.

推荐答案

这是一个已知问题: https://github.com/xamarin/Xamarin.Forms/issues/9525

它仍然在Xamarin Forms 4.8中不起作用.在这里尝试解决方法: https://github.com/xamarin/Xamarin.Forms/issues/9525#issuecomment-629995589

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

此外,MediaElement目前处于试验阶段,将移至Xamarin社区工具包: https://github.com/xamarin/Xamarin.Forms/issues/11857

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

在ios项目上添加下面的 Mediaelement 渲染器以解决此问题.对于示例项目,请检查我的 XF线程.

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");
            }
        }
    }
}

这篇关于Xamarin Forms:由于MediaElement功能,ios平台中的本机崩溃报告的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆