Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GPUImage filtering video

Tags:

ios

gpuimage

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:

  1. I capture a GPUImageMovie
  2. I write it to the file system
  3. I read it from the file system and apply a new filter
  4. I write it to a different URL in the filesystem

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];
            });
        }];
    }];
}
like image 826
geraldWilliam Avatar asked Apr 09 '14 20:04

geraldWilliam


1 Answers

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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

like image 185
geraldWilliam Avatar answered Oct 13 '22 16:10

geraldWilliam