Looping with GPUImageMovie
What's the 'best practice' way to use GPUImageMovie for creating a looping video? At the moment, I'm disposing the GPUImageMovie after completion and recreating it again, but I'm running into a memory warning and eventually an app crash. Is there a way to extend the base class so that it just seeks to first frame OR select another video file from url after completion?
Hi, I'm having the same problem here, I start running the movie and add filters to it like this:
- (void) startRunningMovie {
NSString *pathToMovie = [[NSBundle mainBundle] pathForResource:@"TestMovie" ofType:@"m4v"];
unlink([pathToMovie UTF8String]);
NSURL *movieURL = [NSURL fileURLWithPath:pathToMovie];
if(_movieFile) {
[_movieFile endProcessing];
[_movieFile removeAllTargets];
_movieFile = nil;
}
_movieFile = [[GPUImageMovie alloc] initWithURL:movieURL];
//_movieFile.runBenchmark = YES;
[_movieFile addTarget:_gammaFilter];
[_movieFile addTarget:_brightnessFilter];
[_movieFile addTarget:_heatFilter];
[_movieFile startProcessing];
}
Then at some point I do this:
- (void) resetViewport {
if(_movieFile) {
[_movieFile endProcessing];
[_movieFile removeAllTargets];
_movieFile = nil;
}
}
Still when testing on device, after these steps startRunningMovie - > resetViewport -> startRunningMovie I'll crash with Received Memory Warning.
Any ideas why? Am I doing something wrong?
Any help will be highly appreciated.
Horatiu
How large is this movie? You're running three simultaneous filters on it, and each one will need a framebuffer the size of each movie frame. That can be quite a lot of memory for 720p or 1080p videos. You might want to consider using -forceProcessingAtSize: to reduce the size of the filters to only what will be used in your final output.
Hi Brad, first of all thank you for your response.
The test movie is : 960x540 and it has 22 seconds in length
For each of the filters I'm doing:
[_heatFilter forceProcessingAtSize:_regularOutputView.sizeInPixels];
[_brightnessFilter forceProcessingAtSize:_regularOutputView.sizeInPixels];
[_gammaFilter forceProcessingAtSizeRespectingAspectRatio:_regularOutputView.sizeInPixels];
The first time I load and run the movie it works as expected. When I try to remove the GPUImageMovie object and recreated it then it crashes.
It seems that the GPUImageMovie doesn't endProcessing. Also I've put a NSLog in the dealloc method of GPUImageMovie and this never gets called.
Thanks again for your help and hopefully you can figure out more then me to help me sort things out.
Is this with the latest code from the repository? There was an -endProcessing bug that got fixed a few days ago, and I've just committed a significant rewrite of portions of the framework that include the movie sources.
If this still is happening with the current code, please create an issue for it on the GitHub page so that I can add it to my to-do list.
I don't think that this is with the latest code. I'll grab it and will create an issue if it is still the case. Thanks!
I just grabbed the latest version from repository and the problem still exists.
Some more things I've found out about this. If the playback gets to the end and I call endProcessing and then recreate the GPUImageMovie after that everything works ok, so I don't crash, the dealloc method of the GPUImageMovie gets called as it should when I destroy the object.
However if I do endProcessing and try to destroy the GPUImageMovie and create a new one during processing/playback then I crash
So my guess is that weakSelf is holding on some objects in those blocks and the GPUImageMovie object is not allowed to die until that weakSelf lets go to whatever is holding on.
I have the video looping working really well.
In the -(void)endProcessing
in GPUImageMovie.m i've added a simple NSNotificationCenter postNotification command to tell the system that the video has stopped processing;
- (void)endProcessing; { for (id<GPUImageInput> currentTarget in targets) { [currentTarget endProcessing]; [[NSNotificationCenter defaultCenter] postNotificationName:@"GPUImageMovieEndProcessing" object:nil]; NSLog(@"endProcessing"); } if (synchronizedMovieWriter != nil) { [synchronizedMovieWriter setVideoInputReadyCallback:^{}]; [synchronizedMovieWriter setAudioInputReadyCallback:^{}]; } }
then in the view I'm playing the movie back I've just registered it to observe that NSNotification and set the selector to load the movie file again, with this at the top;
if(movieFile) { [movieFile removeAllTargets]; movieFile = nil; }
Would it be possible for you to post the project or the entire view code? For some reason it seems to be causing the movie to not play back through the whole duration, but I'm guessing something is wrong with the way I'm listening for the notification.
In the -(void)endProcessing
in GPUImageMovie.m i've added a simple NSNotificationCenter postNotification command to tell the system that the video has stopped processing;
- (void)endProcessing; { for (id<GPUImageInput> currentTarget in targets) { [currentTarget endProcessing]; [[NSNotificationCenter defaultCenter] postNotificationName:@"GPUImageMovieEndProcessing" object:nil]; NSLog(@"endProcessing"); } if (synchronizedMovieWriter != nil) { [synchronizedMovieWriter setVideoInputReadyCallback:^{}]; [synchronizedMovieWriter setAudioInputReadyCallback:^{}]; } }
then in the view I'm playing the movie back I've just registered it to observe that NSNotification and set the selector to load the movie file again, with this at the top;
if(movieFile) { [movieFile removeAllTargets]; movieFile = nil; }
Unfortunately, I don't know of a good way to do this. Are you sure that you're releasing the movie instance properly? I know that others have done this for looping, and they've not run into these kinds of memory issues.