Сбой потоковой передачи видео при использовании AVAssetResourceLoader

#ios #swift #video #streaming

#iOS #swift #Видео #потоковая передача

Вопрос:

Мы используем потоковое видео в нашем приложении Swift для IOS, и оно работает очень хорошо. Проблема в том, что мы хотели бы использовать AVAssetResourceLoader, чтобы мы могли отправлять запросы на сервер потоковой передачи, используя наш собственный URLSession, а не тот, который использует AVPlayer (я совершенно не смог выяснить, какой сеанс использует AVPlayer или как повлиять на то, какой сеанс он использует.)

Точное поведение заключается в том, что shouldWaitForLoadingOfRequestedResource вызывается один раз для двух байтов в файле .m3u8, затем он вызывается снова с запросом всего файла, а затем (в зависимости от времени запуска) запрашивается правильный файл .ts. После того, как мы извлекаем файл .ts, видеоплеер просто больше ничего не делает.

Это происходит независимо от того, используем мы образец потокового видеофайла или нет «https://tungsten.aaplimg.com/VOD/bipbop_adv_example_v2/master.m3u8 » или наш собственный сервер. Последовательность будет идентичной, если мы не используем AVAssetResourceLoader (мы можем определить это с нашего сервера.) вплоть до запроса файла .ts. В этот момент, когда мы не используем пользовательский загрузчик, AVPlayer выводит видео и продолжает запрашивать файлы .ts. Если мы закомментируем все другие взаимодействия с AVPlayer, включая установку начального времени, поведение будет идентичным, поэтому я собираюсь включить только код из viewDidLoad и shouldWaitForLoadingOfRequestedResource .

Опять же, если мы просто удалим префикс «xyzzy», чтобы AVAssetResourceLoader не использовался, все работает. Кроме того, я думаю, что важно, если мы нацелены на видеофайл, который не является потоковым файлом, все работает в любом случае.

Еще одна вещь: наше преобразование типа mime для файла .ts привело к появлению какого-то странного динамического uti, но, похоже, это не имеет никакого отношения к проблеме, потому что даже если мы жестко закодируем uti, произойдет то же самое.

 override func viewDidLoad() {
        super.viewDidLoad()
        avPlayer = AVPlayer()
        avPlayerLayer = AVPlayerLayer(player: avPlayer)
        videoView.layer.insertSublayer(avPlayerLayer, at: 0)
        videoView.backgroundColor = UIColor.black         
        url = URL(string: "xyzzy"    currentPatient.videoURL())!
        let asset = AVURLAsset(url: url)
        asset.resourceLoader.setDelegate(self, queue: DispatchQueue.main)
        let item = AVPlayerItem(asset: asset)

        let avPlayerItem = item
        avPlayer.replaceCurrentItem(with: avPlayerItem)

        videoScrollView.delegate = self
        videoScrollView.minimumZoomScale = 1.0
        videoScrollView.maximumZoomScale = 6.0




    }

 func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool {
        let urlString = loadingRequest.request.url?.absoluteString
        let urlComponents = urlString?.components(separatedBy: "xyzzy")
        let url = URL(string: urlComponents![1])           
        let request = loadingRequest.dataRequest!         
        let infoRequest = loadingRequest.contentInformationRequest

        let task = globalSession.dataTask(with: url!) { (data, response, error) in
            self.avPlayerThread.async {
                if error == nil amp;amp; data != nil {

                    let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, response?.mimeType as! CFString, nil)

                    if let infoRequest = infoRequest {
                        infoRequest.contentType = uti?.takeRetainedValue() as? String
                        if request.requestsAllDataToEndOfResource == false {
                            infoRequest.contentLength = Int64(request.requestedLength)
                        } else {
                            infoRequest.contentLength = Int64((data?.count)!)
                        }


                        infoRequest.isByteRangeAccessSupported = true


                    }


                    if infoRequest == nil  || request.requestsAllDataToEndOfResource == true {

                            loadingRequest.dataRequest?.respond(with: data!)                          
                    }

                    loadingRequest.finishLoading()
                } else {
                    print ("error (error)")
                    loadingRequest.finishLoading(with: error)
                }

            }
        }
        task.resume()

        return true

    }
  

Комментарии:

1. Я попробовал тот же подход и увидел тот же сбой. Если вы напечатаете ошибку в AVPlayerItem, вы должны увидеть сообщение «пользовательский URL не перенаправляется». Похоже, Apple специально запрещает разработчикам самим извлекать сегменты видео HLS. См., Например, Этот пост на форумах разработчиков Apple: devforums.apple.com/message/630245#630245 . Это относится только к HLS, будь то с помощью NSURLProtocol или AVAssetResouceLoaderDelegate. Если вы пробовали это с другими носителями, он должен работать нормально (например, с файлом mp3)

2. Я считаю, что сообщение на форуме разработчиков теперь находится по адресу: developer.apple.com/forums/thread /…