繁体   English   中英

通过 HTTP 和 SwiftUI 播放视频 stream

[英]Play back video stream via HTTP with SwiftUI

我正在尝试使用 SwiftUI 中的 VideoPlayer 通过 ROS(机器人操作系统)从本地网络摄像机播放实时视频 stream。 但是 stream 不断失败。 这是我尝试过的:

使用这篇文章,我尝试了以下方法。 https://www.hackingwithswift.com/quick-start/swiftui/how-to-play-movies-with-videoplayer

代码:

VideoPlayer(player: AVPlayer(url: URL(string: "http://192.168.45.100:8080/stream?topic=/image_raw")!))

这导致黑色播放器和控制台写出:

2021-11-24 14:11:47.252729+0100 wifi-test[2965:5457477] <CATransformLayer: 0x2820ae8c0> - changing property masksToBounds in transform-only layer, will have no effect
2021-11-24 14:11:47.275318+0100 wifi-test[2965:5457477] <CATransformLayer: 0x282094c60> - changing property allowsGroupBlending in transform-only layer, will have no effect

然后我想验证视频 URL:
在 VLC 中打开视频 url 效果很好 - 所以 url 是正确的。

使用 AVAsset.isPlayable 测试 url 返回 false。 这让我发现 url 有问题。

let url = URL(string: "http://192.168.45.100:8080/stream?topic=/image_raw")
if AVAsset(url: url!).isPlayable {}

所以我怀疑这是因为 HTTP(而不是 HTTPS)协议。 我的相机只支持 HTTP,所以不能切换到 HTTPS。

我尝试在应用程序属性中设置 AllowArbitraryLoads,但没有运气。 在此处输入图像描述

我还尝试使用 SwiftUI baclward 兼容性实现自定义播放器:

class Video {
    let realUrl = "http://192.168.45.100:8080/stream?topic=/image_raw"
    let testUrl = "https://sylvan.apple.com/Videos/comp_GL_G002_C002_PSNK_v03_SDR_PS_20180925_SDR_2K_AVC.mov"
    
    func getUrl() -> URL? {
        guard let url = URL(string: realUrl) else {
            assertionFailure("Video url not valid")
            return nil
        }
        
        guard AVAsset(url: url).isPlayable else {
            assertionFailure("Video not playable")
            return nil
        }
        
        return url
    }
    
}

struct CustomPlayer: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> some AVPlayerViewController {
        
        let video = Video()
        let controller = AVPlayerViewController()
        let player = AVPlayer(url: video.getUrl()!)
        controller.player = player
        return controller
    }
    
    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
        
    }
}

但是.isPlayable 仍然失败。

如何使视频 stream 工作?

更新:所以显然这个 url 在播放器中工作正常,但验证 AVAsset.isPLayable 失败,所以检查没有任何价值。

"http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"

这个工作的 stream 也是 HTTP,这样就排除了我对 HTTP 与 Z0E8431019A404F21D2B34 的理论

这一定是与我的 stream 格式的兼容性问题。 知道如何验证吗?

所以,我发现 stream 是 MJPEG stream 而 AVPlayer 不支持它。 因此它只显示第一帧。

所以我实现了一个摄像头服务来获取 MJPEG stream,如下所示:

protocol CameraServiceDelegateProtocol {
    func frame(image: UIImage) -> Void
}

protocol CameraServiceProtocol {
    var rosServiceDelegate: CameraServiceDelegateProtocol { get set }
}

class CameraService: NSObject, ObservableObject {
    var cameraServiceDelegate: CameraServiceDelegateProtocol
    let realUrl = URL(string: "http://192.168.45.100:8080/stream?topic=/image_raw")
    var dataTask: URLSessionDataTask?
    var receivedData: NSMutableData = NSMutableData()
    var session: URLSession?
    
    init(delegate: CameraServiceDelegateProtocol) {
        cameraServiceDelegate = delegate
    }
    
    func play() {
        session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: nil)
        dataTask = session?.dataTask(with: realUrl!)
        dataTask?.resume()
    }
    
    func stop() {
        dataTask?.cancel()
    }
}

extension CameraService: URLSessionDataDelegate {
    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
        if self.receivedData.length > 0,
            let receivedImage = UIImage(data: self.receivedData as Data) {
                
            DispatchQueue.main.async {
                self.cameraServiceDelegate.frame(image: receivedImage)
            }
                
            self.receivedData = NSMutableData()
        }
            
        completionHandler(URLSession.ResponseDisposition.allow) //.Cancel,If you want to stop the download
            
    }
        
    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
        self.receivedData.append(data)
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM