AVPlayer Pan Gesture не работает на iOS 13 - PullRequest
0 голосов
/ 30 марта 2020

Я работаю над проектом Xamarin Forms, где пользователи могут перейти к списку плейлистов, а затем просмотреть их. Во время предварительного просмотра пользователи могут перемещаться между видео. На iOS 12 все работает нормально, но на iOS 13 не работает.

В режиме отладки мы наблюдали ошибку в консоли

Ошибка

[] nw_endpoint_flow_copy_multipath_subflow_counts Called on non-Multipath connection
0000000A:  0100  4          4           66
00000016:  0101  4          4           66
00000022:  0102  3          6          110
0000002E:  011A  5          8          116
0000003A:  011B  5          8          124
00000046:  0128  3          2            3
00000052:  0131  2         12          132
0000005E:  0132  2         20          144
000000A6:  0100  4          4          256
000000B2:  0101  4          4          256
000000BE:  0102  3          6          266
000000CA:  0103  3          2            6
000000D6:  0106  3          2            6
000000E2:  0115  3          2            3
000000EE:  0201  4          4          272
000000FA:  0202  4          4         7818*

Вот код пользовательского рендерера для AV Player

using AVFoundation;
using AVKit;
using CoreFoundation;
using CoreMedia;
using Project.Controls.Video;
using Project.iOS.Renderers;
using Foundation;
using System;
using System.ComponentModel;
using System.IO;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(VideoPlayer), typeof(VideoPlayerRenderer))]
namespace Project.iOS.Renderers
{
    public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, UIView>
    {
        AVPlayer player;
        AVPlayerItem playerItem;
        AVPlayerViewController _playerViewController;       // solely for ViewController property
        AVAsset asset;
        NSObject didPlayToEndObserver;

        public override UIViewController ViewController => _playerViewController;

        protected override void OnElementChanged(ElementChangedEventArgs<VideoPlayer> e)
        {
            base.OnElementChanged(e);

            if (e.NewElement != null)
            {
                if (Control == null)
                {
                    // Create AVPlayerViewController
                    _playerViewController = new AVPlayerViewController();

                    // Set Player property to AVPlayer
                    player = new AVPlayer();
                    _playerViewController.Player = player;

                    UITapGestureRecognizer uiTapGestureRecognizer = new UITapGestureRecognizer(TapAction);
                    _playerViewController.ContentOverlayView.GestureRecognizers = new UIGestureRecognizer[] { uiTapGestureRecognizer };

                    //UIPanGestureRecognizer uIPanGestureRecognizer = new UIPanGestureRecognizer(TapAction);
                    //_playerViewController.ContentOverlayView.GestureRecognizers = new UIGestureRecognizer[] { uIPanGestureRecognizer };

                    var x = _playerViewController.View;

                    // Use the View from the controller as the native control
                    SetNativeControl(_playerViewController.View);

                    didPlayToEndObserver = AVPlayerItem.Notifications.ObserveDidPlayToEndTime(AVPlayerItem_DidPlayToEndTimeNotification);
                }

                SetAreTransportControlsEnabled();
                SetSource();

                e.NewElement.UpdateStatus += OnUpdateStatus;
                e.NewElement.PlayRequested += OnPlayRequested;
                e.NewElement.PauseRequested += OnPauseRequested;
                e.NewElement.StopRequested += OnStopRequested;
                e.NewElement.VolumeMute += OnMute;
                e.NewElement.VolumeUnmute += OnUnmute;
            }

            if (e.OldElement != null)
            {
                e.OldElement.UpdateStatus -= OnUpdateStatus;
                e.OldElement.PlayRequested -= OnPlayRequested;
                e.OldElement.PauseRequested -= OnPauseRequested;
                e.OldElement.StopRequested -= OnStopRequested;
                e.OldElement.VolumeMute -= OnMute;
                e.OldElement.VolumeUnmute -= OnUnmute;
            }
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            if (e.PropertyName == VideoPlayer.AreTransportControlsEnabledProperty.PropertyName)
            {
                SetAreTransportControlsEnabled();
            }
            else if (e.PropertyName == VideoPlayer.SourceProperty.PropertyName)
            {
                SetSource();
            }
            else if (e.PropertyName == VideoPlayer.PositionProperty.PropertyName)
            {
                TimeSpan controlPosition = ConvertTime(player.CurrentTime);

                if (Math.Abs((controlPosition - Element.Position).TotalSeconds) > 1)
                {
                    player.Seek(CMTime.FromSeconds(Element.Position.TotalSeconds, 1));
                }
            }
        }

        void SetAreTransportControlsEnabled()
        {
            ((AVPlayerViewController)ViewController).ShowsPlaybackControls = Element.AreTransportControlsEnabled;
        }

        void SetSource()
        {
            if (Element.Source is UriVideoSource)
            {
                string uri = (Element.Source as UriVideoSource).Uri;
                if (!String.IsNullOrWhiteSpace(uri))
                    asset = AVAsset.FromUrl(new NSUrl(uri));
            }
            else if (Element.Source is FileVideoSource)
            {
                string uri = (Element.Source as FileVideoSource).File;
                if (!String.IsNullOrWhiteSpace(uri))
                    asset = AVAsset.FromUrl(new NSUrl(uri));
            }
            else if (Element.Source is ResourceVideoSource)
            {
                string path = (Element.Source as ResourceVideoSource).Path;
                if (!String.IsNullOrWhiteSpace(path))
                {
                    string directory = Path.GetDirectoryName(path);
                    string filename = Path.GetFileNameWithoutExtension(path);
                    string extension = Path.GetExtension(path).Substring(1);
                    NSUrl url = NSBundle.MainBundle.GetUrlForResource(filename, extension, directory);
                    asset = AVAsset.FromUrl(url);
                }
            }
            else
            {
                if (player != null)
                {
                    player.Pause();
                    player.CancelPendingPrerolls();
                    player.ReplaceCurrentItemWithPlayerItem(null);
                }
                if (playerItem != null)
                {
                    playerItem.CancelPendingSeeks();
                    playerItem.Dispose();
                    playerItem = null;
                }
                if (asset != null)
                {
                    asset.CancelLoading();
                    asset.Dispose();
                    asset = null;
                }
            }

            if (asset != null)
            {
                string[] keys = { "playable" };

                asset.LoadValuesAsynchronously(keys, () =>
                {
                    DispatchQueue.MainQueue.DispatchAsync(() =>
                    {
                        if (asset == null) return;

                        playerItem = new AVPlayerItem(asset);
                        player.ReplaceCurrentItemWithPlayerItem(playerItem);
                        if (playerItem != null && Element.AutoPlay)
                            player.Play();
                    });
                });
            }
        }

        private void TapAction()
        {
            this.Element?.InvokeOnTapEvent();
        }

        // Event handler to update status
        void OnUpdateStatus(object sender, EventArgs args)
        {
            VideoStatus videoStatus = VideoStatus.NotReady;

            switch (player.Status)
            {
                case AVPlayerStatus.ReadyToPlay:
                    switch (player.TimeControlStatus)
                    {
                        case AVPlayerTimeControlStatus.Playing:
                            videoStatus = VideoStatus.Playing;
                            break;

                        case AVPlayerTimeControlStatus.Paused:
                            videoStatus = VideoStatus.Paused;
                            break;
                    }
                    break;
            }
            ((IVideoPlayerController)Element).Status = videoStatus;

            if (playerItem != null)
            {
                ((IVideoPlayerController)Element).Duration = ConvertTime(playerItem.Duration);
                ((IElementController)Element).SetValueFromRenderer(VideoPlayer.PositionProperty, ConvertTime(playerItem.CurrentTime));
            }
        }

        TimeSpan ConvertTime(CMTime cmTime)
        {
            return TimeSpan.FromSeconds(Double.IsNaN(cmTime.Seconds) ? 0 : cmTime.Seconds);
        }

        // Event handlers to implement methods
        void OnPlayRequested(object sender, EventArgs args)
        {
            player.Play();
        }

        void OnPauseRequested(object sender, EventArgs args)
        {
            player.Pause();
        }

        void OnStopRequested(object sender, EventArgs args)
        {
            player.Pause();
            player.Seek(new CMTime(0, 1));
        }

        void OnMute(object sender, EventArgs args)
        {
            player.Volume = 0;
        }

        void OnUnmute(object sender, EventArgs args)
        {
            player.Volume = 1;
        }

        private void AVPlayerItem_DidPlayToEndTimeNotification(object sender, NSNotificationEventArgs e)
        {
            player.Pause();
            player.Seek(new CMTime(0, 1));

            Element?.InvokePlayCompleted();
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (asset != null)
                {
                    asset.CancelLoading();
                    asset.Dispose();
                    asset = null;
                }

                if (player != null)
                {
                    player.Pause();
                    player.ReplaceCurrentItemWithPlayerItem(null);
                    player.Dispose();
                    player = null;
                }

                if (playerItem != null)
                {
                    playerItem.Dispose();
                    playerItem = null;
                }

                if (_playerViewController != null)
                {
                    _playerViewController.Dispose();
                    _playerViewController = null;
                }

                if (Element != null)
                {
                    Element.UpdateStatus -= OnUpdateStatus;
                    Element.PlayRequested -= OnPlayRequested;
                    Element.PauseRequested -= OnPauseRequested;
                    Element.StopRequested -= OnStopRequested;
                    Element.VolumeMute -= OnMute;
                    Element.VolumeUnmute -= OnUnmute;
                }

                if (didPlayToEndObserver != null)
                    didPlayToEndObserver.Dispose();
            }

            base.Dispose(disposing);
        }
    }
}
...