[英]SwiftUI - MJPEG Video stream not updating Image in View
Given the following StreamView()
:鉴于以下StreamView()
:
struct StreamView: View {
@StateObject var stream = MJPEGStream()
var body: some View {
MpegView(mjpegStream: self.stream)
.background(.red)
.frame(width: 200, height: 200)
}
}
struct StreamView_Previews: PreviewProvider {
static var previews: some View {
StreamView()
}
}
I have the following MpegView()
that implements ObservableObject
:我有以下实现ObservableObject
MpegView()
:
class MJPEGStream: ObservableObject {
@Published var stream = MJPEGStreamLib()
init() {
self.stream.play(url: URL(string: "http://192.168.1.120/mjpeg/1")!)
}
}
struct MpegView: View {
@ObservedObject var mjpegStream: MJPEGStream
var body: some View {
Image(uiImage: self.mjpegStream.stream.image)
.resizable()
}
}
Basically the following class replaces an instance of var image = UIImage()
with an updated image of the MJPEG stream:基本上,以下类用 MJPEG 流的更新图像替换var image = UIImage()
的实例:
class MJPEGStreamLib: NSObject, URLSessionDataDelegate {
enum StreamStatus {
case stop
case loading
case play
}
var receivedData: NSMutableData?
var dataTask: URLSessionDataTask?
var session: Foundation.URLSession!
var status: StreamStatus = .stop
var authenticationHandler: ((URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?
var didStartLoading: (() -> Void)?
var didFinishLoading: (() -> Void)?
var contentURL: URL?
var image = UIImage()
override init() {
super.init()
self.session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: nil)
}
convenience init(contentURL: URL) {
self.init()
self.contentURL = contentURL
self.play()
}
deinit {
self.dataTask?.cancel()
}
// Play function with url parameter
func play(url: URL) {
// Checking the status for it is already playing or not
if self.status == .play || self.status == .loading {
self.stop()
}
self.contentURL = url
self.play()
}
// Play function without URL paremeter
func play() {
guard let url = self.contentURL, self.status == .stop else {
return
}
self.status = .loading
DispatchQueue.main.async {
self.didStartLoading?()
}
self.receivedData = NSMutableData()
let request = URLRequest(url: url)
self.dataTask = self.session.dataTask(with: request)
self.dataTask?.resume()
}
// Stop the stream function
func stop() {
self.status = .stop
self.dataTask?.cancel()
}
// NSURLSessionDataDelegate
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
// Controlling the imageData is not nil
if let imageData = self.receivedData, imageData.length > 0,
let receivedImage = UIImage(data: imageData as Data) {
if self.status == .loading {
self.status = .play
DispatchQueue.main.async {
self.didFinishLoading?()
}
}
// Set the imageview as received stream
DispatchQueue.main.async {
self.image = receivedImage
}
}
self.receivedData = NSMutableData()
completionHandler(.allow)
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
self.receivedData?.append(data)
}
// NSURLSessionTaskDelegate
func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
var credential: URLCredential?
var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
// Getting the authentication if stream asks it
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
if let trust = challenge.protectionSpace.serverTrust {
credential = URLCredential(trust: trust)
disposition = .useCredential
}
} else if let onAuthentication = self.authenticationHandler {
(disposition, credential) = onAuthentication(challenge)
}
completionHandler(disposition, credential)
}
}
Then in my main ContentView()
I simply have:然后在我的主要ContentView()
我只有:
struct ContentView: View {
var body: some View {
StreamView()
}
}
The problem is that the Image
in the MpegView()
is not getting updated with the received frames from the stream.问题是MpegView()
中的Image
没有使用从流中接收到的帧进行更新。 I'm not sure if it's my implementation for the class library or the @Published
or @StateObject
properties.我不确定这是否是我对类库或@Published
或@StateObject
属性的实现。
NOTE: I can confirm that the stream works via the web browser and also if I debug what the receivedImage
is it's the actual frame from the streamed video.注意:我可以通过网络浏览器确认流是否正常工作,并且如果我调试receivedImage
是来自流视频的实际帧。
The value of your observed property stream
in MJPEGStream
is a pointer to an MJPEGStreamLib
object. MJPEGStream
观察到的属性stream
的MJPEGStream
是指向MJPEGStreamLib
对象的指针。
The only time that property changes, and the only time your ObservableObject
will cause the MpegView
to be updated, is when you first assign a value to the pointer - when the MpegView
is first created.属性更改的唯一时间,也是您的ObservableObject
将导致MpegView
更新的唯一时间,是您第一次为指针赋值时 - 首次创建MpegView
时。 After that, the pointer to the object never changes, even if the object it points to is quickly generating images.之后,指向对象的指针永远不会改变,即使它指向的对象正在快速生成图像。 So your view never updates.所以你的视图永远不会更新。
If you want your Swift view to update whenever the image in your MJPEGStreamLib
object changes, then you need to make MJPEGStreamLib
the ObservableObject
and mark its image
property as @Published
.如果您希望您的 Swift 视图在MJPEGStreamLib
对象中的图像发生更改时更新,那么您需要将MJPEGStreamLib
ObservableObject
并将其image
属性标记为@Published
。
MJPEGStreamLib contains static image variable so image view is not updating. MJPEGStreamLib 包含静态图像变量,因此图像视图不会更新。 You need to bind variables using a property wrapper like @State or @Published.您需要使用@State 或@Published 之类的属性包装器来绑定变量。 Still not work please comment here.仍然无法正常工作,请在此处发表评论。 I will help you我会帮你
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.