MjpegStreamingKit is a small iOS framework providing a view controller for UIImageView to play mjpeg video streams with content type multipart/x-mixed-replace within it.
Using this controller is quite simple... all you have to do is to import the framework, initialize it with an UIImageView and call the play() method:
import MjpegStreamingKit
let imageView = UIImageView(frame: rect)
let streamingController = MjpegStreamingController(imageView: imageView)
// To play url do:
let url = NSURL(string: "http://mjpeg.streaming.url/movie.mjpeg")
streamingController.play(url: url!)
// As an alternative you could do as well:
let imageView = UIImageView(frame: rect)
let streamingController = MjpegStreamingController(imageView: imageView)
streamingController.contentURL = NSURL(string: "http://mjpeg.streaming.url/movie.mjpeg")
streamingController.play() // if contentURL is not nul it will be played
// Or directly init the controller with both the imageView and the url
let imageView = UIImageView(frame: rect)
let url = NSURL(string: "http://mjpeg.streaming.url/movie.mjpeg")
let streamingController = MjpegStreamingController(imageView: imageView, contentURL: url!)
streamingController.play()
There is a slight difference between play(url: NSURL) and play():
- play(url: NSURL): when called if another video is playing it will be stopped and the new source will start
- play(): when called if another video is playing nothing will happen, otherwise it will start reproducing the contentURL
To stop a video all you need to do is to call the stop() method:
streamingController.stop() // this will stop the video and the data transfer
You can of course create the UIImageView with interface builder (an IBOutlet) and initialize the MjpegStreamingController with it.
MjpegStreamingController has two properties that can be set to perform actions at the beginning and at the end of the loading time:
- var didStartLoading: (()->Void)?: this closure is called right after the play() method has been invoked and should be used to set-up whatever should happen while the stream is loading (like displaying an activity indicator and start its animation)
- var didFinishLoading: (()->Void)?: this closure is called right before displaying the first frame of the video stream and should be use to undo what was done by didStartLoading (like stopping the anipation of the activity indicator and hide it)
class ViewController: UIViewController {
@IBOutlet weak var imageView: UIImageView!
@IBOutlet weak var loadingIndicator: UIActivityIndicatorView!
var streamingController: MjpegStreamingController!
override func viewDidLoad() {
super.viewDidLoad()
streamingController.didStartLoading = { [unowned self] in
self.loadingIndicator.hidden = false
self.loadingIndicator.startAnimating()
}
streamingController.didFinishLoading = { [unowned self] in
self.loadingIndicator.stopAnimating()
self.loadingIndicator.hidden = true
}
}
...
}
These two properties are completely optionals hence you are free to ignore them
If when attempting to connect MjpegStreamingController receive an authentication challenge it will try to handle it in different ways depending on the authentication type and if the authenticationHandler is set:
- authentication type is NSURLAuthenticationMethodServerTrust: usually happening if the url is using https instead of http, this case is automatically handled and no action is required
- any other authentication type: in this case it checks if the closure authenticationHandler is set:
- if it's set then it calls it passing in input the authentication challange, it will be then up to the closure to provide the NSURLSessionAuthChallengeDisposition and NSURLCredential to continue with the authentication process
- if it's not set MjpegStreamingController will fallback on the default behaviour of a NSURLSession in case of authentication challenges
Here it is an example of how to implement a custom authentication handler:
streamingController.authenticationHandler = { challenge in
// Checking if credentials are available in key chain
if let credentials = NSURLCredentialStorage.sharedCredentialStorage().credentialsForProtectionSpace(challenge.protectionSpace) where credentials.count > 0 {
return (.UseCredential, credentials.values.first)
} else { // if not already available generate a credentials object to use
let credentials: NSURLCredential? = self.retrieveCredentials() // a method that is creating NSURLCredential object
return (.UseCredential, credentials)
}
}
A simple way to deal with HTTP Basic Auth without having to provide an authentication handler is to put the credentials directly inside the url as follow:
streamingController.contentURL = NSURL(string: "http://username:password@mjpeg.streaming.url/movie.mjpeg")