简体   繁体   English

通过 HTTP 和 SwiftUI 播放视频 stream

[英]Play back video stream via HTTP with SwiftUI

I'm trying to play back a live video stream from a local network camera, through ROS (Robot Operating System) using VideoPlayer in SwiftUI.我正在尝试使用 SwiftUI 中的 VideoPlayer 通过 ROS(机器人操作系统)从本地网络摄像机播放实时视频 stream。 But the stream keeps failing.但是 stream 不断失败。 Here is what I tried:这是我尝试过的:

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

Code:代码:

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

This results in black player and the console writes out:这导致黑色播放器和控制台写出:

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

Then I wanted to validate the video URL:然后我想验证视频 URL:
Opening the video url in VLC works great - so the url is correct.在 VLC 中打开视频 url 效果很好 - 所以 url 是正确的。

Testing the url with AVAsset.isPlayable is returning false.使用 AVAsset.isPlayable 测试 url 返回 false。 So that led me to that something is wrong with the url.这让我发现 url 有问题。

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

So I suspect it is because of the HTTP (and not HTTPS) protocol.所以我怀疑这是因为 HTTP(而不是 HTTPS)协议。 My camera only supports HTTP, so switching to HTTPS is not an option.我的相机只支持 HTTP,所以不能切换到 HTTPS。

I tried setting AllowArbitraryLoads in the app properties, but with no luck.我尝试在应用程序属性中设置 AllowArbitraryLoads,但没有运气。 在此处输入图像描述

I also tried implementing a custom player using SwiftUI baclward compatibility:我还尝试使用 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) {
        
    }
}

But.isPlayable still fails.但是.isPlayable 仍然失败。

How do I make the video stream work?如何使视频 stream 工作?

Update: So apparently this url works fine in the players but fails validation AVAsset.isPLayable, so that check is nothing worth.更新:所以显然这个 url 在播放器中工作正常,但验证 AVAsset.isPLayable 失败,所以检查没有任何价值。

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

Also this working stream is HTTP, so that rules out my thery on HTTP vs. HTTPS.这个工作的 stream 也是 HTTP,这样就排除了我对 HTTP 与 Z0E8431019A404F21D2B34 的理论

It must be a compatibility issue with my stream format.这一定是与我的 stream 格式的兼容性问题。 Any idea how I can verify that?知道如何验证吗?

So, I figured out that the stream is a MJPEG stream and the AVPlayer doesen't support that.所以,我发现 stream 是 MJPEG stream 而 AVPlayer 不支持它。 Therefore it only shows the first frame.因此它只显示第一帧。

So I've implemented a camera service that fetches the MJPEG stream, like this:所以我实现了一个摄像头服务来获取 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