This is a follow-up to a previous but only marginally related question
I'm using the GPUImage library to apply filters to still photos and videos in my camera app. Almost everything is working nicely. One remaining issue that I have not been able to resolve is as follows:
What is saved to that new URL is a video with the correct duration but no movement. When I hit play, it's just a still image, I think the first frame of the video. This happens anytime I apply any filter to a video that I retrieve from the filesystem. Applying a filter to live recording video works just fine. My code is below.
Can anyone tell me how I can modify this to save the entire original video with a filter applied?
- (void)applyProcessingToVideoAtIndexPath:(NSIndexPath *)indexPath withFilter:(GPUImageFilter *)selectedFilter
{
NSArray *urls = [self.videoURLsByIndexPaths objectForKey:self.indexPathForDisplayedImage];
NSURL *url = [urls lastObject];
self.editedMovie = [[GPUImageMovie alloc] initWithURL:url];
assert(!!self.editedMovie);
[self.editedMovie addTarget:selectedFilter]; // apply the user-selected filter to the file
NSURL *movieURL = [self generatedMovieURL];
// A different movie writer than the one I was using for live video capture.
movieWriter = [[GPUImageMovieWriter alloc] initWithMovieURL:movieURL size:CGSizeMake(640.0, 640.0)];
[selectedFilter addTarget:movieWriter];
movieWriter.shouldPassthroughAudio = YES;
self.editedMovie.audioEncodingTarget = movieWriter;
[self.editedMovie enableSynchronizedEncodingUsingMovieWriter:movieWriter];
[movieWriter startRecording];
[self.editedMovie startProcessing];
__weak GPUImageMovieWriter *weakWriter = movieWriter;
__weak CreateContentViewController *weakSelf = self;
[movieWriter setCompletionBlock:^{
[selectedFilter removeTarget:weakWriter];
[weakWriter finishRecordingWithCompletionHandler:^{
NSArray *urls = [weakSelf.videoURLsByIndexPaths objectForKey:weakSelf.indexPathForDisplayedImage];
urls = [urls arrayByAddingObject:movieURL];
NSMutableDictionary *mutableVideoURLs = [weakSelf.videoURLsByIndexPaths mutableCopy];
[mutableVideoURLs setObject:urls forKey:weakSelf.indexPathForDisplayedImage];
weakSelf.videoURLsByIndexPaths = mutableVideoURLs;
dispatch_sync(dispatch_get_main_queue(), ^{
[self.filmRoll reloadData];
[weakSelf showPlayerLayerForURL:movieURL onTopOfImageView:weakSelf.displayedImageView];
});
}];
}];
}
I'm posting an 'answer' to this question for future visitors. My code is working now, but the fact is I'm not sure what made the difference. I don't want the above code to be a red herring for anyone. It's possible that the issue was elsewhere in my code base. However, I want to mention the differences between the method posted in the question and the method in my working code.
I simplified the structure of the controller so it's only dealing with one media item at a time. No more videoURLsByIndexPath, I just have self.mediaItem. This makes organization much more manageable, obviously. I also have a suspicion that adding an entry for movieURL to that dictionary inside the completion block of the movie writer may have been related to the unexpected results I was seeing.
movieWriter is now a strong property instead of an ivar. This shouldn't matter AFAIK, because ivars default to strong references. Nevertheless, that's a difference between what's posted here and my working code.
Almost certainly unrelated, but I did put an if-else around self.editedMovie.audioEncodingTarget = self.movieWriter
to check if the count of [self.mediaItem tracksWithType:AVMediaTypeAudio]
is greater than 0.
Since it was no longer necessary to maintain a dictionary of video URLs, I simply call self.mediaItem = [AVURLAsset assetWithURL:movieURL];
. I do this outside of the movie writer's completion block.
I feel that this is not the most valuable of answers, but I hope that the points I've mentioned here prove useful.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With