swift - не может воспроизвести видео, загруженное с помощью AVAssetResourceLoaderDelegate - PullRequest
0 голосов
/ 18 февраля 2020

Я пытаюсь воспроизвести видео с помощью AVPlayer, который был загружен внутри AVAssetResourceLoaderDelegate, но у меня всегда появляется пустой экран, и видео никогда не воспроизводится.

Это код:

let asset = AVURLAsset(url: URL(string: "fakescheme://video.mp4")!)
asset.resourceLoader.setDelegate(ResourceLoaderDelegate(), queue: DispatchQueue.main)
let item = AVPlayerItem(asset: asset)
let player = AVPlayer(playerItem: item)

let playerLayer = AVPlayerLayer(player: self.player)
playerLayer!.frame = self.view.bounds;
self.view.layer.addSublayer(self.playerLayer!)
player!.play()

...

class ResourceLoaderDelegate: NSObject, AVAssetResourceLoaderDelegate, URLSessionDelegate, URLSessionDataDelegate, URLSessionTaskDelegate {
    public func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource resourceLoadingRequest: AVAssetResourceLoadingRequest) -> Bool {

        var newRequest = URLRequest(url: URL(string: "https://example.com/video.mp4")!)
        newRequest.allHTTPHeaderFields = resourceLoadingRequest.request.allHTTPHeaderFields

        let sessionTask = URLSession.shared.dataTask(with: newRequest) { data, response, error in
            resourceLoadingRequest.contentInformationRequest?.contentType = "video/mp4"
            resourceLoadingRequest.contentInformationRequest?.isByteRangeAccessSupported = true
            resourceLoadingRequest.contentInformationRequest?.contentLength = Int64(data.count)

            resourceLoadingRequest.response = response

            resourceLoadingRequest.dataRequest?.respond(with: data)
            resourceLoadingRequest.finishLoading()
        }

        sessionTask.resume()
        return true
    }
}

Это базовая c идея:

fakescheme: // вызывает вызов делегата resourceLoader (), внутри него делается новый HTTP-запрос для загрузки реального видео.

И затем он вызывает resourceLoadingRequest.dataRequest? .respond (with: data), который должен был заставить AVPlayer воспроизводить загруженное видео. Переменная данных заполнена правильно, но я всегда получаю черный экран.

Видеофайл также в порядке, он воспроизводится, если я передаю его непосредственно в AVURLAsset ().

Я попытался миллион вещей и комбинаций, но не может заставить его играть, используя делегата. Любая помощь будет принята с благодарностью!

РЕДАКТИРОВАТЬ: Я добавлю еще немного информации.

Я попытался сделать это с зашифрованным видео AES, где используются 3 файла - .m3u8, ключ шифрования и .ts видео. Мне удалось заставить его загрузить .m3u8 и ключ с помощью делегата, но снова, когда я пытаюсь сделать это с видеофайлом, я получаю черный экран.

Это заставляет меня думать, что это может потребовать загрузки случиться по частям, но я не уверен, как правильно это сделать. Я также не могу найти какую-либо документацию по этому поводу, например, - если вы установите заголовки HTTP, как если бы они исходили от веб-сервера, если вы установили contentInformationRequest, et c. Делегат вызывается 2 раза:

// first time: 
resourceLoadingRequest.dataRequest!.requestedLength == 2,
resourceLoadingRequest.dataRequest!.requestsAllDataToEndOfResource == false

// second time: 
resourceLoadingRequest.dataRequest!.requestedLength == MAX_INT,
resourceLoadingRequest.dataRequest!.requestsAllDataToEndOfResource == true

Я не уверен, что с этим делать - я даю видео целиком оба раза, но безуспешно.

1 Ответ

0 голосов
/ 19 февраля 2020

Я наконец получил его на работу. Таким образом, есть 2 проблемы с вышеуказанным кодом:

1) contentType должен быть AVFileType.mp4.rawValue, который является "publi c .mpeg-4". Передача "video / mp4" или другого значения нарушает его

2) resourceLoadingRequest.dataRequest! .RequestedLength Действительно необходимо соблюдать, поэтому видеофайл необходимо отправлять кусками по запросу.

Это рабочий код делегата:

// this IF is an ugly way to catch the first request to the delegate
// in this request you should populate the contentInformationRequest struct with the size of the video, etc
if (resourceLoadingRequest.dataRequest!.requestedLength == 2) {
    let bytes : [UInt8] = [0x0, 0x0] // these are the first 2 bytes of the video, as requested
    let data = Data(bytes: bytes, count: bytes.count)

    resourceLoadingRequest.contentInformationRequest?.contentType = AVFileType.mp4.rawValue // this is public.mpeg-4, video/mp4 does not work
    resourceLoadingRequest.contentInformationRequest?.isByteRangeAccessSupported = true
    resourceLoadingRequest.contentInformationRequest?.contentLength = Int64(videoSize)

    resourceLoadingRequest.dataRequest!.respond(with: data)
    resourceLoadingRequest.finishLoading()

    return true
}

// here we are at the second request. the OS may request the entire file, or a portion of it 
// here we don't need to set any headers or contentInformationRequest, just reply with the requested data
// take a look at resourceLoadingRequest.dataRequest!.requestedLength, requestedOffset, currentOffset, requestsAllDataToEndOfResource
resourceLoadingRequest.dataRequest?.respond(with: data)
resourceLoadingRequest.finishLoading()

return true
...