Медленная реализация AVPlayerLayer

Примечание. Я принимаю любой ответ в Xamarin.iOS, Objective-C или Swift. Если вам нужна помощь в чтении кода Xamarin, я могу прояснить это для вас, но это должно быть просто прекрасно, учитывая «сложность» кода здесь.

Я подозреваю, что мой AVPlayerLayer замедляет мой процесс.

Я встраиваю видео/аудиоплеер в свой контроллер, используя следующий код в ViewDidLoad :

    void SetupLayer()
    {
        _layer = AVPlayerLayer.FromPlayer(_player);
        _layer.Frame = PlayerViewContainer.Bounds;
        _layer.Player = _player;
        _layer.BackgroundColor = UIColor.Black.CGColor;
        _layer.VideoGravity = AVLayerVideoGravity.ResizeAspect;

        //This is a UIView
        PlayerViewContainer.Layer.InsertSublayer(_layer, 0);
    }

Кроме того, в конструкторе моего собственного контроллера (содержащего плеер):

        public void SetUrls(string mediaUrl, string coverUrl = null)
        {
            CoverUrl = string.IsNullOrEmpty(coverUrl) ? string.Empty : coverUrl;
            MediaUrl = mediaUrl;

            AVUrlAsset asset = new AVUrlAsset(new NSUrl(MediaUrl));
            AVPlayerItem item = new AVPlayerItem(asset);

            _isVideo = item.Asset.Tracks.Length != 1;

            _player = new AVPlayer(item);
        }

Это единственный соответствующий код, вызываемый в ViewDidLoad и конструкторе. Удаленные части не должны иметь никакого влияния (тексты настроек, загрузка ресурсов и так далее).

Теперь проблема, с которой я столкнулся, заключается в том, что загрузка «большого» (под большим я не имею в виду более 4-часового видеопотока. Обычно они длятся около 4 минут) файла занимает МНОГО времени. До 4 секунд. От 0,5 до 1,5 в конструкторе (функция SetUrls) и от 1,5 до 2,5 в ViewDidLoad (функция SetupLayer).

Я рассчитал время, и удаление этих методов резко сократило отставание до чего-то приличного (но у меня, очевидно, нет изображения или плохих результатов).

Я сравнил родной плеер, используя следующий код:

            _item = AVPlayerItem.FromUrl(url);
            _player = new AVPlayer(_item);

            _controller = new AVPlayerViewController();
            _controller.Player = _player;

            this.PresentViewController(_controller, true, _player.Play);

И загружается мгновенно. Дело даже не в том, что 4 секунды загрузки распределены более равномерно, а в том, что в целом это занимает меньше 4 секунд.

Учитывая, что он, вероятно, загружается во время анимации, и что экран по-прежнему черный и ничего не воспроизводится, когда отображается проигрыватель, я бы сказал, что родной контроллер выполняет эту работу примерно за 1-2 секунды (в то время как половина этого, вероятно, во время анимация).

Мой делает все, прежде чем анимировать/показывать что-либо. Единственная приятная вещь в этом заключается в том, что видео воспроизводится, как только может, без черного экрана. Недостатком является ужасное время загрузки контроллера.

Примечание. Во время загрузки мой пользовательский интерфейс полностью заблокирован. Это наводит меня на мысль, что это происходит в потоке пользовательского интерфейса и что, возможно, этого не должно быть.

Мои вопросы:

  • Где здесь мои узкие места? Я явно делаю что-то не так.
  • Что мне делать по-другому? Может быть, я загружаю плеер в свой собственный контроллер?
  • Как я могу загрузить эти компоненты асинхронно, чтобы избежать блокировки потока пользовательского интерфейса? Будет ли этого достаточно, чтобы сократить время загрузки? Или просто разрешено ли это делать?

person Gil Sand    schedule 29.06.2016    source источник


Ответы (2)


Ответ Радина помог мне, и у меня была такая же проблема. Дополнительная информация доступна по этой ссылке — https://developer.apple.com/reference/avfoundation/avasynchronouskeyvalueloading/1387321-loadvaluesasynchronously

import UIKIT
import AVFoundation

typealias VideoDownloadResponse = (Bool, AVPlayerLayer?) -> Void

func loadVideoFromURL(urlString: String, onCompletion: @escaping VideoDownloadResponse) {

    if let url = URL(string: urlString) as URL? {
        let asset = AVURLAsset(url: url)
        // Load the asset's "playable" key
        asset.loadValuesAsynchronously(forKeys: ["playable"]) {
            var error: NSError? = nil
            let status = asset.statusOfValue(forKey: "playable", error: &error)
            switch status {

            case .loaded:
                // Sucessfully loaded, continue processing
                // Build the player
                let playerItem = AVPlayerItem(asset: asset)
                let player = AVPlayer(playerItem: playerItem)
                let layer = AVPlayerLayer(player: player)
                //send it back to be shown in the UI
                DispatchQueue.main.async(execute: {
                    onCompletion(true, layer)
                })

            case .failed:
                // Examine NSError pointer to determine failure
                DispatchQueue.main.async(execute: {
                    onCompletion(false, nil)
                })
            default:
                // Handle all other cases
                DispatchQueue.main.async(execute: {
                    onCompletion(false, nil)
                })
            }
        }
    }

}
person Geoff Glaeser    schedule 15.05.2017

Преобразуйте свой метод в асинхронный и вызовите await asset.LoadValuesTaskAsync(new string[] { @"playable" }); сразу после создания актива. Это предварительно загрузит видео, не заблокирует пользовательский интерфейс.

person Radin Gospodinov    schedule 29.06.2016
comment
Это ничего не изменило :( - person Gil Sand; 29.06.2016