Since the time that Axis Communications introduced the first Internet Protocol (IP) camera a decade and a half ago, basic security monitoring has become affordable for middle class families and small businesses. Now with the rise of smartphones, more and more people want to check into their homes and offices while on the road using standard IP technology.
Most IP cameras provide a basic video stream in Motion-JPEG format — basically a constantly updating stream of JPEG-compressed bitmap files. The proliferation of IP camera applications on the iTunes App Store shows that basic handling of these Motion JPEG feeds is pretty simple — drop in a UIWebView, point it your IP camera’s URL, and you are off and running. Unfortunately, this simple solution does not perform particularly well, and it does not lend itself to easily sizing Motion-JPEG streams with other views. MotionJpegImageView provides a simple interface for creating and displaying these streams within an iOS application.
Motion-JPEG streams are really nothing more than constantly updating JPEG image files. As such, it seemed appropriate to base the Motion-JPEG view on Apple’s existing UIImageView class. Rather than providing a UIImage file, however, we fill MotionJpegImageView by providing it a URL for the video stream. Internally, MotionJpegImageView creates a connection to the stream and receives frames one at a time over the network. As it receives each frame, it hydrates the stream data into a UIImage object, and then sets its inherited image property. Thus, the only limitations are
- The speed of your network connection
- The frame rate of the IP camera
- The phone’s ability to process inbound packets and rehydrate UIImage files from raw JPEG data.
By contrast, a UIWebView which displays a Motion-JPEG stream has to deal with all the other overhead of parsing and rendering each frame as if it were a web page. While this may not seem like a lot of extra work (after all, the Motion-JPEG stream is only delivering a single image per page load), it turns out that the UIImageView subclass approach is typically seven or eight times faster than UIWebView.
The test application included with the MotionJpegImageView project demonstrates this dramatic difference. At the top of the screen, it creates a classic UIWebView stream; at the bottom is the same stream using UIImageView. With more than one stream displayed simultaneously in this way, the differences are even more dramatic — UIWebView struggles to update frames once every second or two, while MotionJpegImageView displays a more or less smooth video stream.
In the example, both of the streams are running at full Retina Display resolution — accommodating two 640×480 streams onscreen at once. Although UIWebView basically falls apart with more than two streams, we have demonstrated that MotionJpegImageView provides a decent frame rate when up to 8 video streams are displayed onscreen simultaneously — provided that we do not oversample too much in terms of resolution (i.e., rather than loading 8 streams at 640×480 resolution, we limit ourselves to 160×120 resolution for each).
You can download the full source code from github:
Secure Cameras and Streams
Basic video streaming is pretty simple. However, most people will want to secure their video feeds with a username and password, and often encrypt the stream using HTTPS. MotionJpegImageView can handle these feeds, as well.
When it comes to handling secure streams, there are a couple of options. First, you can load an encrypted stream simply by passing a URL with the HTTPS scheme at front. In order to make HTTPS work, the server (your IP camera) needs to have a certificate installed. While businesses may have the resources to pay for a publicly trusted certificate from a regular certificate authority, individuals will often resort to installing a “self-signed” or “standalone” certificate. The advantage of a self-signed certificate is that you can save yourself a couple of hundred dollars; the disadvantage is that you may be vulnerable to a “man in the middle” attack, in which a cracker intercepts requests you send to the IP camera and can therefore gain access to things like the username and password you are using. For use on a secure local area network in a private residence, the chance of a man-in-the-middle attack is quite low, and therefore the cost of the certificate outweighs the benefit. As a result, if you want to access an HTTPS video feed on a camera with a self-signed certificate, all you need to do is set the “allowSelfSignedCertificates” property to YES (it is NO by default):
_imageView.allowSelfSignedCertificates = YES;
Another common security option is the ability to password-protect the video feed. Password protection is all well and good, but if you use it without also using an encrypted data stream, your username and password are sent over the wire using “clear text” — meaning that anyone who knows how to install one of the free network sniffing utilities such as Wireshark can easily discover your login credentials. Although we recommend that you use both a password and an encrypted stream, if you want to allow clear text logins, you can set the “allowClearTextCredentials” property to YES (again, this is NO by default):
_imageView.allowClearTextCredentials = YES;
Finally, you can set the username and password on your stream programmatically using the username and password properties, or you can leave these blank. If you do attempt to access a password-protected stream and the login credentials are either blank or incorrect, MotionJpegImageView will display a dialog box in which the user can enter the appropriate information. Credentials persist for the duration of the session — i.e., once you close the app, you will need to re-enter credentials when you attempt to access the stream again later.
- Much improved frame rate (when compared with UIWebView). 7x to 8x speed improvement when loading the same size stream under the same network conditions. More efficient processing makes it possible to load more streams simultaneously; whereas UIWebView tops out with one or two feeds, MotionJpegImageView can work reasonably well with up to 8 streams.
- Easy handling of passwords and secure data streams.
- Simple resizing. With UIWebView, resizing can be tricky. Using the “scalesPageToFit” property will fit the feed within the frame of the UIWebView without scrolling, but often the page will render with extra whitespace around the edges. If you know the exact dimensions of the feed, you can manually scale the content using an affine transformation, but it can be difficult to determine image dimensions reliably at runtime. By contrast, MotionJpegImageView automatically sizes content to fit according to the option specified in its “contentMode” property (which is set to “UIViewContentModeScaleAspectFit” by default).
- Convenient play/pause/stop/clear options. With UIWebView, you can either load the page or stop loading the page, and reloading can take a couple of seconds. With MotionJpegImageView, you can play or pause the stream quickly and easily. You can also stop (which clears the screen), or blank out a paused feed using the “clear” method.