You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xcode9 Swift4下播放服务返回的无扩展名视频URL解决方案咨询

解决无扩展名视频URL无法在AVPlayer中播放的问题

我之前也碰到过一模一样的情况——AVPlayer默认会依赖URL的扩展名来识别媒体类型,没有扩展名的话,系统就没法判断该怎么解码视频,自然播放不了。针对你在Xcode9+Swift4环境下的场景,我整理了几个可行的解决方案:

方法一:已知视频格式时,手动指定媒体类型

如果已经明确视频的实际格式(比如是MP4、MOV或者HLS流),可以直接在创建AVURLAsset时,通过选项告诉系统媒体类型,这样AVPlayer就能正确加载了。

修改后的代码示例:

import AVKit
import AVFoundation

class ViewController: UIViewController {
    @IBOutlet weak var player_View: UIView!
    // 这里做了安全处理,避免URL为nil导致崩溃
    let videoURL = URL(string: "http://45.58.42.105:82/Handlers/ImageHandler.ashx?fileReference=67f0dce7-a5f5-4894-a8fd-5996f135626c")!
    
    var playerVC: AVPlayerViewController!
    var player: AVPlayer!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 假设你的视频是MP4格式,对应MIME类型为"video/mp4"
        // 如果是HLS流,改成"application/x-mpegURL"即可
        let assetOptions: [String: Any] = [
            AVURLAssetMediaTypeKey: AVMediaType.video.rawValue,
            // 确保能获取精确的视频时长和时序信息
            AVURLAssetPreferPreciseDurationAndTimingKey: true
        ]
        
        let asset = AVURLAsset(url: videoURL, options: assetOptions)
        let playerItem = AVPlayerItem(asset: asset)
        
        player = AVPlayer(playerItem: playerItem)
        playerVC = AVPlayerViewController()
        playerVC.player = player
        
        // 将播放器视图嵌入到你的player_View中
        addChild(playerVC)
        playerVC.view.frame = player_View.bounds
        player_View.addSubview(playerVC.view)
        playerVC.didMove(toParent: self)
        
        // 开始播放
        player.play()
    }
}

方法二:通用方案——用ResourceLoader拦截请求补充MIME类型

如果不确定视频格式,或者想兼容多种格式,最好的方式是用AVAssetResourceLoaderDelegate拦截AVPlayer的加载请求,自己去获取视频数据,并在返回时补充正确的MIME类型(可以从服务器响应头里取,或者默认设置常用类型)。

首先创建一个自定义的资源加载器类:

import AVFoundation

class VideoResourceLoader: NSObject, AVAssetResourceLoaderDelegate {
    private let session: URLSession
    
    override init() {
        session = URLSession(configuration: .default)
        super.init()
    }
    
    func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool {
        guard let targetURL = loadingRequest.request.url else {
            loadingRequest.finishLoading(with: NSError(domain: "VideoLoaderError", code: -1, userInfo: [NSLocalizedDescriptionKey: "无效的视频URL"]))
            return false
        }
        
        // 用URLSession自己请求视频数据
        let task = session.dataTask(with: targetURL) { [weak self] data, response, error in
            guard let self = self else { return }
            
            if let error = error {
                loadingRequest.finishLoading(with: error)
                return
            }
            
            guard let httpResponse = response as? HTTPURLResponse, let videoData = data else {
                loadingRequest.finishLoading(with: NSError(domain: "VideoLoaderError", code: -2, userInfo: [NSLocalizedDescriptionKey: "未获取到视频数据或响应"]))
                return
            }
            
            // 优先从服务器响应头取Content-Type,如果没有则默认设为MP4
            var contentType = httpResponse.allHeaderFields["Content-Type"] as? String ?? "video/mp4"
            if contentType.isEmpty {
                contentType = "video/mp4"
            }
            
            // 给AVPlayer提供必要的内容信息
            loadingRequest.contentInformationRequest?.contentType = contentType
            loadingRequest.contentInformationRequest?.contentLength = Int64(videoData.count)
            loadingRequest.contentInformationRequest?.isByteRangeAccessSupported = true
            
            // 返回视频数据并完成加载
            loadingRequest.dataRequest?.respond(with: videoData)
            loadingRequest.finishLoading()
        }
        
        task.resume()
        return true
    }
}

然后在ViewController中使用这个加载器:

import AVKit
import AVFoundation

class ViewController: UIViewController {
    @IBOutlet weak var player_View: UIView!
    let videoURL = URL(string: "http://45.58.42.105:82/Handlers/ImageHandler.ashx?fileReference=67f0dce7-a5f5-4894-a8fd-5996f135626c")!
    
    var playerVC: AVPlayerViewController!
    var player: AVPlayer!
    var resourceLoader: VideoResourceLoader!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        resourceLoader = VideoResourceLoader()
        
        // 创建一个自定义URL scheme,让AVAsset把加载请求交给我们的loader处理
        let customAssetURL = URL(string: "custom-video://play")!
        let asset = AVURLAsset(url: customAssetURL)
        asset.resourceLoader.setDelegate(resourceLoader, queue: DispatchQueue.main)
        
        let playerItem = AVPlayerItem(asset: asset)
        player = AVPlayer(playerItem: playerItem)
        playerVC = AVPlayerViewController()
        playerVC.player = player
        
        // 嵌入播放器视图
        addChild(playerVC)
        playerVC.view.frame = player_View.bounds
        player_View.addSubview(playerVC.view)
        playerVC.didMove(toParent: self)
        
        player.play()
    }
}

额外建议:让后端补充响应头

如果有条件的话,最好和后端开发同学沟通,让他们在返回视频时,在HTTP响应头中正确设置Content-Type字段(比如MP4设为video/mp4)。这样即使URL没有扩展名,AVPlayer也能自动识别媒体类型,不需要前端做额外处理,这是最省心的方案。

内容的提问来源于stack exchange,提问作者Demi Ali

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:07:27