I am trying to play video that is coming from an IP camera in iOS, but currently I tried 2 methods and they both seem to be filling up the memory of my iOS device really fast. I am using ARC for this project.
My IP camera uses Videostream.cgi
(Foscam), which is a well-known way for IP cameras to stream 'video' through the browser.
So, I tried 3 ways, which all end up in crashing my iOS app, with an out-of-memory exception.
1. Putting an UIWebView
on my UIViewController
and call the CGI directly using a NSURLRequest
.
NSString* url = [NSString stringWithFormat:@"http://%@:%@/videostream.cgi?user=%@&pwd=%@&rate=0&resolution=%ld", camera.ip, camera.port, camera.username, camera.password, (long)_resolution];
NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
webView = [[UIWebView alloc] init];
[webView loadRequest:request];
2. Putting an UIWebView
on my UIViewController
and creating a piece of HTML (in code) which includes a <img>
tag which has a source to the CGI mentioned before. (see: IP camera stream with UIWebview works on IOS 5 but not on IOS 6)
NSString* imgHtml = [NSString stringWithFormat:@"<img src='%@'>", url];
webView = [[UIWebView alloc] init];
[webView loadHTMLString:imgHtml];
3. Using a custom control, based on a UIImageView
, which fetches data continuously. https://github.com/mateagar/Motion-JPEG-Image-View-for-iOS
All of these things burn through memory and even when I try to remove them and re-add them after a certain period of time, but this does not seem to fix the problem. Memory won't be released and the iPad crashes.
UPDATE:
I am currently modifying option 3 of the solutions I tried. It is based on a NSURLConnection
and the data it retrieves.
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
if (!_receivedData) {
_receivedData = [NSMutableData new];
}
[_receivedData appendData:data];
NSRange endRange = [_receivedData rangeOfData:_endMarkerData
options:0
range:NSMakeRange(0, _receivedData.length)];
NSUInteger endLocation = endRange.location + endRange.length;
if (_receivedData.length >= endLocation) {
NSData *imageData = [_receivedData subdataWithRange:NSMakeRange(0, endLocation)];
UIImage *receivedImage = [UIImage imageWithData:imageData];
if (receivedImage) {
NSLog(@"_receivedData length: %d", [_receivedData length]);
self.image = receivedImage;
_receivedData = nil;
_receivedData = [NSMutableData new];
}
}
if (_shouldStop) {
[connection cancel];
}
}
_receivedData
is a NSMutableData
object. which I try to "empty" once an image is retrieved from the stream. The part in if (receivedImage
) is called when it is supposed to be called. The length of the _receivedData
object is also not increasing, it stays around the same size (~ 14k), so that seems to work.
But somehow, with every didReceiveData
the memory my app is using increases, even when I disable the line self.image = receivedImage
.
UPDATE
As iosengineer suggested, I have been playing with autorelease pools, but this does not solve the problem.
Using Instruments I found out that most of the allocations are done by CFNetwork
, in the method HTTPBodyData::appendBytes(unsigned char const*, long)
. (This allocates 64KB at a time and keeps them alive).