使用 SwiftUI 通过 HTTP 播放视频流
Play back video stream via HTTP with SwiftUI
我正在尝试使用 SwiftUI 中的 VideoPlayer 通过 ROS(机器人操作系统)从本地网络摄像机播放实时视频流。
但是流一直失败。这是我尝试过的:
使用这篇文章,我尝试了以下方法。
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 向后兼容性实现自定义播放器:
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还是失败了
如何使视频流正常工作?
更新:
所以显然这个 url 在播放器中工作正常但验证失败 AVAsset.isPLayable,所以检查没有价值。
"http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
此外,此工作流是 HTTP,因此排除了我对 HTTP 与 HTTPS 的看法。
这一定是我的流格式的兼容性问题。知道我该如何验证吗?
所以,我发现流是 MJPEG 流,而 AVPlayer 不支持它。因此它只显示第一帧。
所以我实现了一个获取 MJPEG 流的相机服务,如下所示:
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)
}
}
我正在尝试使用 SwiftUI 中的 VideoPlayer 通过 ROS(机器人操作系统)从本地网络摄像机播放实时视频流。 但是流一直失败。这是我尝试过的:
使用这篇文章,我尝试了以下方法。 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 向后兼容性实现自定义播放器:
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还是失败了
如何使视频流正常工作?
更新: 所以显然这个 url 在播放器中工作正常但验证失败 AVAsset.isPLayable,所以检查没有价值。
"http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
此外,此工作流是 HTTP,因此排除了我对 HTTP 与 HTTPS 的看法。
这一定是我的流格式的兼容性问题。知道我该如何验证吗?
所以,我发现流是 MJPEG 流,而 AVPlayer 不支持它。因此它只显示第一帧。
所以我实现了一个获取 MJPEG 流的相机服务,如下所示:
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)
}
}