首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >swift -不能播放带有AVAssetResourceLoaderDelegate的视频

swift -不能播放带有AVAssetResourceLoaderDelegate的视频
EN

Stack Overflow用户
提问于 2020-02-18 16:54:52
回答 1查看 1.1K关注 0票数 1

我试着用AVPlayer播放一个加载在AVAssetResourceLoaderDelegate中的视频,但是我总是得到一个空白的屏幕,而这个视频从未播放过。

这是代码:

代码语言:javascript
复制
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
    }
}

这是基本的想法:

fakescheme://导致调用resourceLoader()委托,其中发出一个新的HTTP请求来下载实际的视频。

然后它调用resourceLoadingRequest.dataRequest?.respond(with:数据),这应该会导致AVPlayer播放下载的视频。数据变量已正确填充,但我始终得到一个黑色屏幕。

视频文件也很好,如果我将它直接输入到AVURLAsset()中,它就会播放。

我尝试了无数的东西和组合,但不能让它发挥使用委托。任何帮助都将不胜感激!

编辑:我会添加更多的信息。

我试着用AES加密的视频做这件事,其中它使用了3个文件-- .m3u8、加密密钥和.ts视频。我成功地让它使用委托下载了.m3u8和密钥,但是当我尝试用视频文件下载它时,我的屏幕就变黑了。

这导致我认为可能需要下载部分,但我不知道如何正确地做到这一点。我也找不到关于它的任何文档,例如-应该设置headers,就像它来自web服务器,应该设置contentInformationRequest等等。委托会被调用2次:

代码语言:javascript
复制
// first time: 
resourceLoadingRequest.dataRequest!.requestedLength == 2,
resourceLoadingRequest.dataRequest!.requestsAllDataToEndOfResource == false

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

我不知道该做什么-我给它整个视频两次,但没有成功。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-02-19 10:33:01

我终于让它开始工作了。因此,上面的代码有两个问题:

  1. contentType应为AVFileType.mp4.rawValue,即"public.mpeg-4“。传递“视频/MP4”或另一个值会破坏它的

  1. resourceLoadingRequest.dataRequest!.requestedLength确实需要得到尊重,因此视频文件需要按请求以块的形式发送。

这是工作委托代码:

代码语言:javascript
复制
// 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

这是供参考的整个文件的代码:

代码语言:javascript
复制
import Foundation
import AVKit

class ResourceLoaderDelegate: NSObject, AVAssetResourceLoaderDelegate, URLSessionDelegate, URLSessionDataDelegate, URLSessionTaskDelegate {
    public func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource resourceLoadingRequest: AVAssetResourceLoadingRequest) -> Bool {
        if ((resourceLoadingRequest.request.url?.absoluteString.contains(".mp4"))!) {
            // replace the fakeScheme and get the original video url
            var originalVideoURLComps = URLComponents(url: resourceLoadingRequest.request.url!, resolvingAgainstBaseURL: false)!
            originalVideoURLComps.scheme = "file"
            let originalVideoURL = originalVideoURLComps.url
            
            var videoSize = 0
            do {
                let value = try originalVideoURL!.resourceValues(forKeys: [.fileSizeKey])
                videoSize = value.fileSize!
            } catch {
                print("error getting video size")
            }
            
            if (resourceLoadingRequest.contentInformationRequest != nil) {
                // this is the first request where we should tell the OS what file is to be downloaded
                let bytes : [UInt8] = [0x0, 0x0]    // TODO: repeat .requestedLength times?
                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
            }
            
            // this is the second request where the actual file is to be downloaded
            
            let requestedLength = resourceLoadingRequest.dataRequest!.requestedLength
            let requestedOffset = resourceLoadingRequest.dataRequest!.requestedOffset
            let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: requestedLength)
            
            let inputStream = InputStream(url: originalVideoURL!)   // TODO: keep the stream open until a new file is requested?
            inputStream!.open()
            
            if (requestedOffset > 0) {
                // move the stream pointer to the requested position
                let buffer2 = UnsafeMutablePointer<UInt8>.allocate(capacity: Int(requestedOffset))
                inputStream!.read(buffer2, maxLength: Int(requestedOffset)) // TODO: the requestedOffset may be int64, but this gets truncated to int!
                buffer2.deallocate()
            }
            inputStream!.read(buffer, maxLength: requestedLength)
            
            // decrypt the video
            if (requestedOffset == 0) { // TODO: this == 0 may not always work?
                // if you use custom encryption, you can decrypt the video here, buffer[] holds the bytes
            }
        
            let data = Data(bytes: buffer, count: requestedLength)
            
            resourceLoadingRequest.dataRequest?.respond(with: data)
            resourceLoadingRequest.finishLoading()
            
            buffer.deallocate()
            inputStream!.close()
            
            return true
        }
        
        return false
    }
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/60285860

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档