'Swift Mjpeg Streaming Only Showing Single Frame
I am trying to create a Mjepg stream. I have followed a tutorial and it's not a lot of code to get a stream to lead but I don't understand why it's not working for me.
//Class properties
private var mjpegSession : URLSession?
private var mjpegData:Data = Data()
override func viewDidLoad(){
...
mjpegSession = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
if let url = URL(string: "http://cam6284208.miemasu.net/nphMotionJpeg?Resolution=640x480") {
mjpegSession?.dataTask(with: url).resume()
}
}
extension ViewController: URLSessionDataDelegate, URLSessionTaskDelegate {
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
mjpegData.append(data)
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
if mjpegData.count > 0 {
DispatchQueue.main.async{
if let image = UIImage(data: self.mjpegData){
self.cameraImageView.image = image
}
}
}
mjpegData.removeAll()
completionHandler(.allow)
}
}
The tutorial removed all the data in on the line that says
mjpegData.removeAll()
However if I do that the image is incomplete and there's errors in the log about not being able to decode an image. If I remove it, I get a complete frame or image but I only get one. Using that url in VLC shows a constant stream so I know the url is valid stream. Thanks for everyone's time.
Solution 1:[1]
So I found a solution having to do with queues and background threads that seemed to fix my problem. Here's the solution in case anyone else runs into this.
//Class properties
private var mjpegSession : URLSession?
private var mjpegData:Data = Data()
override func viewDidLoad(){
...
self.queue = DispatchQueue(label: "MjpegQueue")
self.mjpegSession = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
if let url = URL(string: "http://cam6284208.miemasu.net/nphMotionJpeg?Resolution=640x480") {
DispatchQueue.global().async {
self.mjpegSession?.dataTask(with: url).resume()
}
}
}
extension ViewController: URLSessionDataDelegate, URLSessionTaskDelegate {
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
mjpegData.append(data)
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
queue.sync{
if mjpegData.count > 0 {
let data = self.mjpegData
if let image = UIImage(data: data){
DispatchQueue.main.async {
self.cameraImageView.image = image
}
}
}
mjpegData.removeAll()
}
completionHandler(.allow)
}
}
Solution 2:[2]
import UIKit
class ViewController
: UIViewController, URLSessionDataDelegate
{
// Variables
@IBOutlet weak var imageview : UIImageView!
private var queue = Data( )
// View : Did Load
override func viewDidLoad( )
{
super.viewDidLoad()
let session = URLSession( configuration: .default, delegate: self, delegateQueue: nil )
if let url = URL( string: "http://cam6284208.miemasu.net/nphMotionJpeg?Resolution=640x480" )
{
session.dataTask( with: url ).resume( )
}
}
// URLSession : Data Callback
func urlSession
(
_ session : URLSession,
dataTask : URLSessionDataTask,
didReceive data : Data
)
{
queue.append( data )
}
// URLSession : Response Callback
func urlSession
(
_ session : URLSession,
dataTask : URLSessionDataTask,
didReceive response : URLResponse,
completionHandler : @escaping( URLSession.ResponseDisposition ) -> Void
)
{
if queue.count > 0
{
if let image = UIImage( data: queue )
{
DispatchQueue.main.async
{
self.imageview.image = image
}
}
}
queue.removeAll( )
completionHandler( .allow )
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Jonathan Brown |
Solution 2 |